// == 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
{
    namespace Scripting {

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

        //! @brief コンストラクターを修飾して、フィールドおよびプロパティをDefaultValueAttributeで初期化するリクエストを行います。
        //! @details UVTexIntegraで、再コンパイルによるデータ引継ぎなどを全く行わず、単純に新しいオブジェクトとして新規作成するときだけ読み取られます。
        [System.AttributeUsage(System.AttributeTargets.Constructor, Inherited = false)]
        public class ApplyDefaultValueAttribute : System.Attribute 
        {
            //! @brief コンストラクタ
            //! @param flag フィールドもプロパティも初期化しないときはfalse。それ以外はtrue。
            public ApplyDefaultValueAttribute(bool flag)
            {
                m_ApplyDefaultValue = flag;
                m_applyField = true;
                m_applyProperty = true;
                m_propertyFlags = m_fieldFlags = System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance |
                     System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
                m_nullDefaultEnabled = true;
            }
            //! @brief コンストラクタでflagとして設定した値を読み取ります。
            public bool ApplyDefaultValue { get { return m_ApplyDefaultValue; } }
            //! @brief フィールドを初期化するかどうかを設定・取得します。
            //! @details このフラグは標準でtrueです。例えばフィールドだけ設定したい時は次のようにします。
            //! - コンストラクタでflag(ApplyDefaultValue)をtrueに設定
            //! - ApplyPropertyをfalseに設定
            public bool ApplyField { get { return m_ApplyDefaultValue && m_applyField; } set { m_applyField = value; } }
            //! @brief プロパティを初期化するかどうかを設定・取得します。
            //! @details このフラグは標準でtrueです。例えばプロパティだけ設定したい時は次のようにします。
            //! - コンストラクタでflag(ApplyDefaultValue)をtrueに設定
            //! - ApplyFieldをfalseに設定
            public bool ApplyProperty { get { return m_ApplyDefaultValue && m_applyProperty; } set { m_applyProperty = value; } }
            //! @brief フィールドの列挙フラグを指定します。
            //! @details 標準では System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | 
            //! System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic が指定されます。
            public System.Reflection.BindingFlags FieldFlags { get { return m_fieldFlags; } set { m_fieldFlags = value; } }
            //! @brief DefaultValueとしてnullが与えられる時、そのnullでメンバをクリアするべきか設定・取得します。標準ではtrueです。
            public bool NullDefaultEnabled { get { return m_nullDefaultEnabled; } set { m_nullDefaultEnabled = value; } }
            //! @brief プロパティの列挙フラグを指定します。
            //! @details 標準では System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | 
            //! System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic が指定されます。
            public System.Reflection.BindingFlags PropertyFlags { get { return m_propertyFlags; } set { m_propertyFlags = value; } }
            //! @brief ApplyDefaultValueAttributeの設定を元にオブジェクトに初期値を適用します。
            public void ApplyDefaultValueTo(System.Object target)
            {
                if (target == null) return;
                if (ApplyProperty) AttributeExtensions.ApplyDefeultValueAttributeToProperties(target, PropertyFlags, m_nullDefaultEnabled);
                if (ApplyField) AttributeExtensions.ApplyDefeultValueAttributeToFields(target, FieldFlags, m_nullDefaultEnabled);
            }
            private bool m_ApplyDefaultValue, m_applyProperty, m_applyField, m_nullDefaultEnabled;
            private System.Reflection.BindingFlags m_propertyFlags, m_fieldFlags;
        }

        //! @brief メソッドに渡された引数のいずれかが無効な場合にスローされる例外クラスです。
        public class ArgumentException : Exception
        {
            public ArgumentException() : base() { }
            public ArgumentException(System.String message) : base(message) { }
            public ArgumentException(System.String message, System.Exception innerException) : base(message, innerException) { }
        }

        //! @brief プロパティやコレクションからのアセンブリの取得で、該当する要素がないLoadedAssembly要素が見つからない場合スローされる例外クラスです。
        public class AssemblyNotFoundException : Exception {}

        //! @brief IFormatterに対し、ロード済みの特定のアセンブリからのTypeロードを指定するSerializationBinderクラスです。
        //! @details IFormatter.Binderプロパティにこのクラスを設定すると、IFormatterはAssemblySelectedSerializeBinderコンストラクタに設定したAssemblyからのTypeロードを行うようになります。
        public class AssemblySelectedSerializeBinder : System.Runtime.Serialization.SerializationBinder
        {
            public AssemblySelectedSerializeBinder(System.Reflection.Assembly assembly) { m_assembly = assembly; }
            public override System.Type BindToType(System.String assemblyName, System.String typeName) {
                if (m_assembly == null) return null; return m_assembly.GetType(typeName);
            }
            private readonly System.Reflection.Assembly m_assembly;
        }

        //! @brief スクリプトコンパイル時に起こる例外をラッピングします。
        public class CompileException : Exception
        {
            public CompileException(System.String message, System.CodeDom.Compiler.CompilerResults r) : base(message) {
                Results = r;
            }
            public CompileException(System.String message, System.Exception innerEx, System.CodeDom.Compiler.CompilerResults r) : base(message, innerEx) {
                Results = r;
            }
            public readonly System.CodeDom.Compiler.CompilerResults Results;
        };

        //! @brief スクリプト処理によって生じる独自のアプリケーション例外の基底クラスです
        public class Exception : System.Exception
        {
            public Exception() : base() { }
            public Exception(System.String message) : base(message) { }
            public Exception(System.String message, System.Exception innerException) : base(message, innerException) { }
        }

        //! @brief ファイルが見つからない時にスローされる例外クラスです。
        public class FileNotFoundException : Exception
        {
            public FileNotFoundException(System.String path) : base(path) { }
            public FileNotFoundException(System.String path, System.Exception innerException) : base(path, innerException) { }
        }
        
        //! @brief 対応しないファイル形式が要求された時にスローされる例外クラスです。ファイルパスの拡張子から適切なコンパイラを検出できません。
        public class FileNotSupportedException : Exception
        {
            public FileNotSupportedException(System.String message) : base(message) { }
        }

        //! @brief 抽象化されたスクリプトオブジェクトを格納するインターフェースです。
        //! @details ScriptObject,LoadedAssembly,CompiledAssemblyのいずれかを格納します。
        public interface IScriptObject
        {
            //! @brief 表示名を取得します。
            System.String DisplayName { get; }
        }

        //! @brief スクリプトロード時に起こる例外をラッピングします。
        public class LoadException : Exception
        {
            public LoadException(System.String message, System.Exception innerEx) : base(message, innerEx) { }
        };

        //! @brief プロパティ表示をカスタマイズするTypeConverter派生クラスです。
		//! 
		public class PropertyCustomTypeConverter : System.ComponentModel.TypeConverter
		{
		    //! @brief プロパティ表示名を外部から設定するためのカスタム属性です。
		    [System.AttributeUsage(System.AttributeTargets.Property)]
		    public class DisplayNameAttribute : System.Attribute
		    {
                public DisplayNameAttribute(System.String name) { m_customDisplayName = name; }
                public System.String CustomDisplayName { get { return m_customDisplayName; } }
			    private System.String m_customDisplayName;
		    };

            //! @brief カスタム属性を外部に説明するためのフック処理をはさむPropertyDescriptor派生クラスです。
            public class CustomDescriptor : System.ComponentModel.PropertyDescriptor
            {
                public CustomDescriptor(System.ComponentModel.PropertyDescriptor base_descriptor)
                    : base(base_descriptor)
                {
                    m_base = base_descriptor;
                }
                public override string Category { get { return m_base.Category; } }
                public override System.Type ComponentType { get { return m_base.ComponentType; } }
                public override string Description { get { return m_base.Description; } }
                public override string DisplayName
                {
                    get
                    {
                        DisplayNameAttribute attr = m_base.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
                        if (attr != null) return attr.CustomDisplayName;
                        return m_base.DisplayName;
                    }
                }
                public override bool IsReadOnly { get { return m_base.IsReadOnly; } }
                public override System.Type PropertyType { get { return m_base.PropertyType; } }
                public override bool CanResetValue(object component) { return m_base.CanResetValue(component); }
                public override object GetValue(object component) { return m_base.GetValue(component); }
                public override void ResetValue(object component) { m_base.ResetValue(component); }
                public override bool ShouldSerializeValue(object component) { return m_base.ShouldSerializeValue(component); }
                public override void SetValue(object component, object value) { m_base.SetValue(component, value); }
                private readonly System.ComponentModel.PropertyDescriptor m_base;
            }

            public PropertyCustomTypeConverter() {}

            //! @brief カスタマイズされたプロパティ情報の列挙。通常のPropertyDescriptorをベースにした、CustomDescriptorコレクションを返します。
            public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object instance, System.Attribute[] filters)
            {
			    System.ComponentModel.PropertyDescriptorCollection collection = new System.ComponentModel.PropertyDescriptorCollection(null);
			    System.ComponentModel.PropertyDescriptorCollection properies = null;
			    if ( base.GetPropertiesSupported(context) ) properies = base.GetProperties(context, instance, filters);
			    else properies = System.ComponentModel.TypeDescriptor.GetProperties(instance, filters, true);
                foreach (System.ComponentModel.PropertyDescriptor desc in properies) collection.Add(new CustomDescriptor(desc));
			    return collection;
            }

            //! @brief プロパティ情報の列挙処理がオーバーライドされているかを返します（カスタマイズされているのでtrueを返します）。
            public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { return true; }
		};

        //! @brief ScriptMainクラスを修飾して、スクリプトのシリアライズデータをメタセコの終了と開始時にUVTexIntegraの設定データとして保存させます。
        //! @details SerializeSaveAttributeをtrueに設定しなければ、スクリプトクラスのプロパティは新規にロードするたび初期値にリセットされます。
        [System.AttributeUsage(System.AttributeTargets.Class)]
        public class SerializeSaveAttribute : System.Attribute 
        {
            public SerializeSaveAttribute() { this.IsSerializeSave = true; }
            public SerializeSaveAttribute(bool value) { this.IsSerializeSave = value; }
            public bool IsSerializeSave { get; set; }
        }

        //! @brief IFormatterに対し、特定の型へのキャストを要求するSerializationBinderクラスです。
        //! @details IFormatter.Binderプロパティにこのクラスを設定すると、IFormatterはTypeCastSerializeBinderコンストラクタに設定したtypeへのシリアライズを行うようになります。
        public class TypeCastSerializeBinder : System.Runtime.Serialization.SerializationBinder 
        {
            public TypeCastSerializeBinder(System.Type castToType) { m_t = castToType; }
            public override System.Type BindToType(System.String assemblyName, System.String typeName) { return m_t; }
            private System.Type m_t;
        }

        //! @}
    }
}
