// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU General Public License(GPL).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

namespace UVTexIntegra.Scripting
{

    //! @addtogroup UVTexIntegra-Scripting名前空間
    //! @{

    /// @cond <summary>プレビュー処理をカスタマイズする時にベースクラスとして継承してください。</summary> @endcond
    /*! @brief プレビュー処理をカスタマイズするスクリプトのベースクラス
    @details DLLあるいはスクリプトの中に、引数なしコンストラクタをパブリックに公開する@ref UVTexIntegra.Scripting.ScriptMain "ScriptMain"継承クラスを作ると、そのスクリプトオブジェクトのインスタンスがUVTexIntegra上に生成されます。
    @details @par １つのDLLまたはスクリプトへの複数スクリプト登録
        名前空間を問わずアセンブリ中ですべての ScriptMain 継承クラスが列挙されます。
        1. ScriptMain をPublicに継承している
        2. 少なくとも引数のないデフォルトコンストラクタをPublicに公開している
        .
        条件を満たしたクラス１つに対して１つのインスタンスが生成されます。
    @details @par 継承クラスのプロパティに、UVTexIntegraのプロパティグリッドから入力する。
        ScriptMain継承クラスは、プロパティをパブリックに公開すると、UVTexIntegraからリアルタイムにパラメータを閲覧・編集できます。
    @details @par OnUVOperation() メソッドをオーバーライドする
        プレビューや保存処理の前にこのメソッドが呼び出されるので、パラメータに渡された UVTexIntegra.Scripting.ScriptMain.Face[] を参照し、Face.Normal などの面情報をもとに Face.Points[].Coordinate または Face.Points[].Color を書き換えていきます。
        基本的にはここまでの手順だけでカスタムベイクをプログラムできます。
    @details @par
        なお面ごとの処理だけではなく頂点ごとの処理を行うには OnUVOperation() の処理中に独自に解析バッファを作るか
        ScriptMain.Face.ShareRelation.GetRelationsSnap() メソッドを使います。
        このメソッドは現在UV座標がどのように複数の面に共有されているかリストにします。
        
    @details
        @par プロパティを保存させる
        さらに以下のシリアライズ条件を満たせば、プロパティに入力した値を保存できます。
            1. クラスに明示的にSerializable属性を与えられている
            2. デシリアライズ用コンストラクタ／(SerializationInfo info, StreamingContext stream)を引数としたパブリックコンストラクタを公開している。
            3. ISerializableを実装して、GetObjectData()メソッドを提供している。
            4. クラスにUVTexIntegra.Scripting.SerializeSaveAttribute属性を設定している。
            .
        再コンパイル時に型の名前（名前空間を含むフルネーム）を引き継いでいれば、古いScriptMainインスタンスから、新しいScriptMainインスタンスへの保存内容の引継ぎが行われます。
    @details @par 解放処理の記述方法： Close() / Dispose() メソッドのオーバーライドとその違いについて
        解放には Close() または Dispose(bool) をオーバーライドします。
        Close()も Dispose(bool) も基本的には必ず呼び出されます。
        - Dispose() やファイナライズで、常に Close() => Dispose(bool) の順で呼び出される。
        - Close() を継承する時のルール
            1. 複数回呼び出されても問題のない実装にしなければならない。
            2. オーバーライドされた Close() が base.Close() するかはエンドプログラマの裁量にゆだねる
            .
        - Dispose(bool) を継承する時のルール
            1. 複数回呼び出されても問題のない実装にしなければならない。
            2. オーバーライドされた Dispose(bool) は最後に base.Dispose(bool) を必ず呼び出さなくてはならない。
            3. オーバーライドするのは Dispose(bool) （Dispose()はvirtualではありません）。
            4. 呼び出す時は Dispose() に統一する（少なくともDispose(bool)より先にClose()をそれぞれvirtualに呼び出すべきなので、それをDispose()が行っています）。
            .
        - シリアライズによるプロパティ保存処理は、 Close() と Dispose(bool) の間に実施されます。
            1. 古いインスタンスの Close() 処理
            2. 古いインスタンスをシリアライズして、新しいインスタンスにデシリアライズ（バックアップ処理）
            3. 古いインスタンスの Dispose(bool) 処理
            .
        .
        総じて Dispose(bool) の方が強い解放処理であり、一方 Close() はシリアライズ保存処理よりも前に実行されるという特徴があります。
    @par UVTexIntegraでは、アセンブリは実行時にデタッチできません。
        UVTexIntegraでロードしたDLLプログラムを、修正など上書き更新したければメタセコイアを終了する必要があります。
    @par カスタムベイクダイアログはメタセコイア本体に対してモーダルにプレビュー＆保存
        現在のところ、UVTexIntegraは掲題の方針を採っています。したがってカスタムスクリプトがロード～解放される間、メタセコイアの形状やオブジェクトの状態は一定です。
    */
    public partial class ScriptMain : System.IDisposable
    {
        /// @cond <summary>オーバーライドしてスクリプトの表示名をカスタマイズできます。</summary> @endcond
        //! @brief スクリプトの表示名。 @details 標準では名前空間を含む最終的な型名を返しますが、オーバーライドによって自由な表示名を返すことが出来ます。
        //! コンボボックスのプルダウンメニューには、"Nameプロパティの値 ( DLLまたはスクリプトファイル名 )" のように表示されるので、「プラグインの機能を指すフィルター名」というニュアンスで命名するのが良いかと思われます。
        [System.ComponentModel.Browsable(false)]
        public virtual System.String Name { get { return GetType().FullName; } }

        /// @cond <summary>継承可能な解放処理です。Dispose時には必ず先にCloseが呼び出されます。Close()は何度呼び出されても問題ないようにしてください。オーバーライドしたClose()がbase.Close()するべきかは自由で、設計の裁量にゆだねられる前提で利用してください。</summary> @endcond
        /*! @brief 継承可能な解放処理
        @details Close() の基底の実装は、何もしないメソッドになっています。
        @details Close() がUVTexIntegraからどう呼び出されるかについては、@ref ScriptMain の説明を参照してください。
        @attention Close()の継承は、ひとつのインスタンスに対し複数回 Close の呼び出しを行っても問題のないように記述してください。それ以外は実装依存とします（Disposeに比べると緩やかなルールが導入されます）。*/
        public virtual void Close() { }

        /// @cond <summary>明示的に解放処理を呼び出します。</summary> @endcond
        //! @brief UVの変換処理を行います。
        //! @param [in,out] faces 処理対象のUV座標バッファが渡されます。書き換える事が出来るメンバ一覧は以下です。その他取得可能な情報などの詳細は Face クラスを参照してください。
        //! - faces[n].Points[n].Coordinate
        //! - faces[n].Points[n].Color
        public virtual void OnUVOperation(Face[] faces)
        {
        }

        /// @cond <summary>明示的に解放処理を呼び出します。</summary> @endcond
        //! @brief 破棄処理を行います。内部で virtual な Close() を行った後、 Dispose(bool) メソッドを呼び出します。
        //! @details Dispose(void) は外部からvirtualな Dispose(true) を呼び出すための処理です。
        //! @details Dispose() は、 Dispose(true) の前に virtual な Close() も呼び出します。
        //! @details 万が一、Dispose()の呼び出しが行われなかった場合はファイナライザ側でDispose()が実行されます。
        public void Dispose()
        {
            try { this.Close(); }
            finally
            {
                this.Dispose(true);
                System.GC.SuppressFinalize(this);
            }
        }

        /// @cond <summary>このインスタンスがシリアライズ可能な条件を満たすか判定します（Serializable属性+ISerializable実装+デシリアライズコンストラクタ公開）</summary> @endcond
        //! @brief このインスタンスが、すべてのシリアライズ条件を満たしているか判定します。
        //! @details 以下の全ての条件を満たすか判定します。
        //! - 基本列挙条件
        //!     1. ScriptMain をPublicに継承している
        //!     2. 引数のないデフォルトコンストラクタをPublicに公開している
        //!     .
        //! - シリアライズ可能条件
        //!     1. クラスに明示的にSerializable属性を与えられている
        //!     2. デシリアライズ用コンストラクタ／(SerializationInfo info, StreamingContext stream)を引数としたパブリックコンストラクタを公開している。
        //!     3. ISerializableを実装して、GetObjectData()メソッドを提供している。
        //!     .
        public bool IsSerializable() { return Utility.IsSerializableScriptMain(this.GetType()); }

        /// @cond <summary>このスクリプトのプロパティ状態が保存される設定になっているか取得します。</summary> @endcond 
        //! @brief スクリプトがシリアライズ可能( IsSerializable() == true )で、さらにSerializeSaveAttributeがtrueに設定されるているか取得します。
        //! @details スクリプトクラスをシリアライズ可能なようにコーディングして、さらにSerializeSaveAttributeをtrueに設定しているとき、
        //! UVTexIntegraはプラグインのシリアル化データをメタセコの終了と開始時に保存するようになります。
        //! この値がFalseである時、スクリプトクラスのプロパティは新規にロードするたび初期値にリセットされます。
        //! - 基本列挙条件
        //!     1. ScriptMain をPublicに継承している
        //!     2. 引数のないデフォルトコンストラクタをPublicに公開している
        //!     .
        //! - シリアライズ可能条件
        //!     1. クラスに明示的にSerializable属性を与えられている
        //!     2. デシリアライズ用コンストラクタ／(SerializationInfo info, StreamingContext stream)を引数としたパブリックコンストラクタを公開している。
        //!     3. ISerializableを実装して、GetObjectData()メソッドを提供している。
        //! - シリアライズ実行属性
        //!     1. クラスにUVTexIntegra.Scripting.SerializeSaveAttribute 属性を設定していて、値がtrueになっている。
        public bool IsSerializeSave()
        {
            SerializeSaveAttribute attr = GetType().GetPrimaryAttribute<SerializeSaveAttribute>();
            if (attr != null) return attr.IsSerializeSave && this.IsSerializable();
            return false;
        }

        //! @brief デフォルトコンストラクタ
        protected ScriptMain() { }

        //! @brief ファイナライザ
        //! @details 万が一、明示的にDispose()されなかった場合に備えて、ファイナライザ（参照の消滅などによるガベージコレクト直前の処理）では
        //! virtualな Close() および Dispose(false) を実施します。もし一度でも明示的に Dispose() されていれば、このファイナライザは実行されなくなります。
        ~ScriptMain() { try { this.Close(); } finally { this.Dispose(false); } }

        /// @cond <summary>継承可能な解放処理です。Dispose時には必ず先にCloseも呼び出すルールがあるため、継承クラスはDipose(bool)を直接呼び出さず、代わりにDispose()して下さい。Dispose(bool)をオーバーライドする時は何度呼び出されても問題ないようにして、最後に必ずbase.Dispose(bool)しなければなりません。</summary> @endcond
        /*! @brief 継承可能な Dispose メソッド
        @details 継承クラスのリソース解放処理を記述する時はこのメソッドをオーバーライドしてください。
        @param flag Disposeの呼び出し状態
        - true：明示的なIDisposable.Dispose()メソッドの呼び出しによるDisposeが実行されています。
        - false：明示的なDispose呼び出しが一度も無いまま、解放（参照の消滅による自動的なGC）直前に自らDisposeを呼び出しました。
        @details Dispose() がUVTexIntegraからどう呼び出されるかについては、@ref ScriptMain の説明を参照してください。
        @attention このメソッドを継承する時は、継承クラスのリソース解放処理の後、必ず base.Dispose(flag) を呼び出し、ベースクラスのリソース解放も実行してください。
        また Dispose は明示的な契機で呼び出せるため、 Dispose の実装は、ひとつのインスタンスに対し複数回 Dispose の呼び出しを行っても問題のないように記述してください
        （ただし一度でも明示的にDisposeされた後は、ガベージコレクションの解放契機で再びDispose(false)されることはなくなります）。
        @code
        public class DerivedClass : ScriptMain {
            [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] 
            protected override void Dispose(bool flag) {
                try {
                    if ( flag ) {
                        // 明示的なIDisposable.Dispose()メソッドに応答する時の処理
                    }
                    else {
                        // Disposeなしのガベージコレクトに応答する時の処理
                    }
                    // どっちみち応答する処理
                }
                finally { base.Dispose(flag); } // 必ずbase.Dispose(flag);
            }
        };
        @endcode */
        [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
        protected virtual void Dispose(bool flag)
        {
        }
    }

    //! @}
}
