/*
 * PluginInfo.cs
 * Copyright (c) 2007-2009 kbinani
 *
 * This file is part of LipSync.
 *
 * LipSync is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * LipSync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
using System;
using System.IO;

using Plugin;

namespace LipSync {

    /// <summary>
    /// プラグインに関する情報
    /// </summary>
    public class PluginInfo : ICloneable {
        private string _location;
        private string _className;
        private IPlugin _instance = null;
        private string _id;

        public object Clone() {
            return new PluginInfo( _location, _className );
        }

        /// <summary>
        /// PluginInfoクラスのコンストラクタ
        /// </summary>
        /// <param name="path">アセンブリファイルのパス</param>
        /// <param name="cls">クラスの名前</param>
        private PluginInfo( string path, string full_name ) {
            this._location = path;
            this._className = full_name;
            this._id = this.Instance.Name + "@" + Path.GetFileName( path );
        }

        public string ID {
            get {
                return _id;
            }
        }

        public IPlugin Instance {
            get {
                if ( _instance == null ) {
                    _instance = CreateInstance();
                }
                return _instance;
            }
        }

        /// <summary>
        /// アセンブリファイルのパス
        /// </summary>
        public string Location {
            get {
                return _location;
            }
        }

        /// <summary>
        /// クラスの名前
        /// </summary>
        public string ClassName {
            get {
                return _className;
            }
        }

        /// <summary>
        /// 有効なプラグインを探す
        /// </summary>
        /// <returns>有効なプラグインのPluginInfo配列</returns>
        public static PluginInfo[] FindPlugins() {
            System.Collections.ArrayList plugins =
                new System.Collections.ArrayList();
            //IPlugin型の名前
            string ipluginName = typeof( Plugin.IPlugin ).FullName;

            //プラグインフォルダ
            string folder = System.IO.Path.GetDirectoryName(
                System.Reflection.Assembly
                .GetExecutingAssembly().Location );
            /*folder += "\\plugins";
            if ( !System.IO.Directory.Exists( folder ) )
                throw new ApplicationException(
                    "プラグインフォルダ\"" + folder +
                    "\"が見つかりませんでした。" );*/

            //.dllファイルを探す
            string[] dlls =
                System.IO.Directory.GetFiles( folder, "*.dll" );

            foreach ( string dll in dlls ) {
                try {
                    //アセンブリとして読み込む
                    System.Reflection.Assembly asm =
                        System.Reflection.Assembly.LoadFrom( dll );
                    foreach ( Type t in asm.GetTypes() ) {
                        //アセンブリ内のすべての型について、
                        //プラグインとして有効か調べる
                        if ( t.IsClass && t.IsPublic && !t.IsAbstract &&
                            t.GetInterface( ipluginName ) != null ) {
                            //PluginInfoをコレクションに追加する
                            plugins.Add( new PluginInfo( dll, t.FullName ) );
                        }
                    }
                } catch {
                }
            }

            //コレクションを配列にして返す
            return (PluginInfo[])plugins.ToArray( typeof( PluginInfo ) );
        }

        /// <summary>
        /// プラグインクラスのインスタンスを作成する
        /// </summary>
        /// <returns>プラグインクラスのインスタンス</returns>
        private IPlugin CreateInstance() {
            try {
                //アセンブリを読み込む
                System.Reflection.Assembly asm =
                    System.Reflection.Assembly.LoadFrom( this.Location );
                //クラス名からインスタンスを作成する
                return (Plugin.IPlugin)
                    asm.CreateInstance( this.ClassName );
            } catch {
                return null;
            }
        }
    }

}
