// == 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名前空間
    //! @{

    //! @brief DLLからロードされたアセンブリを管理する情報クラスです
    [System.ComponentModel.TypeConverter(typeof(Scripting.PropertyCustomTypeConverter))]
    public partial class LoadedAssembly : IScriptObject, System.IDisposable
    {
		// type definition

        //! @brief LoadedAssemblyおよびLoadedAssembly派生クラス同士をFilePathプロパティで比較するクラス
		public class FilePathComparer : System.Collections.Generic.IComparer<LoadedAssembly>
        {
			//! @brief 比較メソッド
			public virtual int Compare(LoadedAssembly x, LoadedAssembly y)
            {
				System.String x_key = null; if ( x != null ) x_key = x.AbsPath;
				System.String y_key = null; if ( y != null ) y_key = y.AbsPath;
				return System.String.Compare( x_key, y_key, System.StringComparison.OrdinalIgnoreCase );
			}
		};

		// public properties

        //! @brief ロード時に解決された絶対ロードパス
        [System.ComponentModel.Description("DLLのロケーション"), System.ComponentModel.ReadOnly(true)]
        public virtual System.String AbsPath
        {
            get { if (m_assembly == null) return null; return m_assembly.Location; }
        }

        //! @brief 生成されたアセンブリのインスタンス
		[System.ComponentModel.BrowsableAttribute(false)]
		public System.Reflection.Assembly Assembly { get { return m_assembly; } }

		//! @brief ロード設定の表示用の名前を取得します。 @details 基底の実装ではパスを含まないファイル名を返します。
		[System.ComponentModel.BrowsableAttribute(false)]
		public virtual System.String DisplayName
        {
            get
            {
			    System.String name = LoadPath;
			    if ( name != null )
                {
				    System.Char[] invalidPathChars = System.IO.Path.GetInvalidPathChars();
				    foreach ( System.Char ch in invalidPathChars ) name = name.Replace( new System.String( ch, 1 ), System.String.Empty );
				    name = System.IO.Path.GetFileName(name);
			    }
			    return name;
            }
        }

        //! @brief ロード時に指定されたファイルパス
        [System.ComponentModel.Description("ロードパス"), System.ComponentModel.ReadOnly(true)]
        public virtual System.String LoadPath
        {
            get
            {
                if (m_assembly == null) return null;
                if (System.String.IsNullOrWhiteSpace(m_loadPath) == false) return m_loadPath;
                return m_assembly.Location;
            }
        }

        //! @brief 確保済みのScriptMainオブジェクト配列 @details Assembly内で発見されたScriptMain継承クラスを、
		//! 基底のScriptMainインスタンスとして列挙します。ひとつも見つからない時は、nullではなくEmptyを返します。
		[System.ComponentModel.BrowsableAttribute(false)]
		public System.Collections.ObjectModel.ReadOnlyCollection<ScriptMain> Objects
        {
			get
            {
                ScriptMain[] ret = m_objects; if (m_objects == null) ret = new ScriptMain[0];
                return System.Array.AsReadOnly<ScriptMain>(ret);
			}
		}

		//! @brief シリアライズするための情報を返します。
		//! @details SerializeInformation継承クラスのLoad()の返すLoadedAssembly継承クラスと、
		//! LoadedAssembly継承クラスのSerializeInfoプロパティは互いにLoad/Serialize可能な関係を維持するようにしてください。
		[System.ComponentModel.BrowsableAttribute(false)]
		public virtual SerializeInformation SerializeInfo { get { return new SerializeInformation(this); } }
        
        // public methods ////////////////
        //! @brief リソース解放を要求します。全てのScriptMainインスタンスがDispose()されます。
        public void Dispose() { Dispose(true); System.GC.SuppressFinalize(this); }

        //! @brief SerializeInformationからの新規構築を試みます。バックアップしたい場合は、SerializeInformation.serializedCollectionを利用します。
        //! @exception LoadException アセンブリロード時に例外が発生しました。詳しい内容はInnerExceptionを参照してください。
        public static LoadedAssembly From(SerializeInformation info)
        {
			System.Reflection.Assembly assembly = null;
			try { assembly = System.Reflection.Assembly.LoadFrom(info.FilePath); }
            catch (System.Exception ex) { throw new LoadException(info.FilePath, ex); }

			LoadedAssembly ret = new LoadedAssembly();
			ret.Initialize(info.FilePath, assembly, info.SerializedCollection);
			return ret;
		}

        //! @brief 新規ファイルパスを指定して任意のDLLを読み込みます。
        //! @details ロードに成功すると、Assembly内を総検索して、デフォルトコンストラクタにアクセス可能かつpublicに公開されたScriptMain継承クラスのインスタンスを生成し、ScriptMainインスタンスとして列挙します。
        //! 指定されたAssemblyに有効なScriptMainオブジェクトが見つからない場合、Objectsは空の配列を返します。
        //! @return AssemblyとScriptMainオブジェクト配列がペアになったLoadedAssemblyインスタンス。
        //! @exception LoadException ファイルパスからのDLLロードに失敗すると、LoadException例外をスローします。
        public static LoadedAssembly From(System.String filePath) { return From(new SerializeInformation(filePath)); }

        //! @brief 内部のScriptMainリストをByte[][]にシリアライズします。有効な要素がひとつも無い時は空の配列が返ります。
        public System.Byte[][] ObjectsToSerializedByteArray()
        {
            System.Collections.Generic.List<System.Byte[]> serializedList = new System.Collections.Generic.List<System.Byte[]>();
            if (m_objects != null) foreach (ScriptMain instance in m_objects)
            {
                if ((instance == null) || (instance.IsSerializeSave() == false)) continue;
                System.Byte[] addSerialized = ScriptMain.Utility.ToSerializedByteArray(instance);
                if (addSerialized != null) serializedList.Add(addSerialized);
            }
            return serializedList.ToArray();
        }

        // protected implements ////////////////
        protected LoadedAssembly() {}
        ~LoadedAssembly() { Dispose(false); }

        //! @brief 全てのScriptMainインスタンスをDisposeします。
        //! @details Dispose処理を継承する時は、Dispose(bool)を継承して、必ずベースクラスのDispose(bool)を呼び出してください。
        protected virtual void Dispose(bool ExplicitDispose)
        {
            if (m_objects != null)
            {
                foreach (ScriptMain instance in m_objects) if (instance != null) instance.Dispose();
                m_objects = null;
            }
        }
        
        //! @brief セットアップ。
        //! @details 新規セットアップ用です。呼び出し時点でLoadedAssemblyに既存のテーブルはDisposeされるだけで古いデータのバックアップはされません。
        //! ただし引数を使ってByte[][]からのデシリアライズを指定できます（省略可）。
        protected void Initialize(System.String loadPath, System.Reflection.Assembly assembly, System.Byte[][] serializedCollection)
        {
			if (m_objects != null) foreach (ScriptMain instance in m_objects) if (instance != null) instance.Dispose();
            m_loadPath = loadPath;
            m_assembly = assembly;
			m_objects = ScriptMain.Utility.BuildInstancesFromTypes(assembly, serializedCollection);
        }

        // private fields ////////////////////////
        System.Reflection.Assembly m_assembly;
        System.String m_loadPath;
		ScriptMain[] m_objects;
    }

    //! @}
}
