using System;
using System.Diagnostics;
using System.Reflection;
using System.Xml;
using nft.ui.command;
using nft.framework;
using nft.framework.plugin;
using nft.util;

namespace nft.contributions.ui
{
	/// <summary>
	/// CtbCommand の概要の説明です。
	/// </summary>
    public class CtbCommand : Contribution
	{
		protected enum MethodType { Static, Instance, Constructor };
        protected readonly MethodType methodType;
        protected readonly string methodName;
        protected readonly Type targetType;
        protected readonly string commandType;
		protected readonly ICommandProcedure procedure;
		private string path;

        public CtbCommand(Plugin p, ParamsReader contrib)
            : base(p, contrib)
		{
			path = contrib.SourceURI;
            ParamsReader cls = contrib["class"];
			targetType = PluginUtil.loadTypeFromManifest(cls);

            ParamsReader mn = contrib["method"];
            ParamsReader cmd = contrib["command"];
            commandType = cmd["type"].InnerTextOr("procedure");

			if(!mn.IsNull)
			{
				methodName = mn["name"].InnerText;
                if (bool.Parse(mn["static"].InnerTextOr("false")))
					methodType = MethodType.Static;
				else
					methodType = MethodType.Instance;

				if( methodName == null )
				{
					string templ = I18n.T("An attribute '{1}' is required for the node '{0}'.");
					throw new Exception(string.Format(templ,"method","name",contrib.SourceURI));
				}
			}
			else
				methodType = MethodType.Constructor;

            procedure = Createprocedure(contrib);
            // Set properties for default CommandUI creation.
            cmd.OverWrite("name", cmd.InnerTextOr(this.Name));
            cmd.OverWrite("description", cmd.InnerTextOr(this.Description));
            CommandManager.TheInstance.BindCommand(this, null, procedure, cmd);
		}

		protected virtual ICommandProcedure Createprocedure( ParamsReader contrib )
		{
            // ModalForm/ModelessForm/Subfrom-Commands will be created.
            ICommandProcedure procedure = Main.mainFrame.Createprocedure(commandType, targetType, contrib);
            if (procedure != null) return procedure;

			procedure = CreateTarget() as ICommandProcedure;
			if(procedure==null)
			{
                string[] args = new string[] { commandType, targetType.Name, methodName };
                string msg = I18n.F("Failed to create Commandprocedure[type={0}, class={1}, method={2}].", args);
				throw new PluginXmlException(contrib,msg);
			}
			return procedure;
		}

        public ICommandProcedure Commandprocedure {
            get { return procedure; }
        }

		public object CreateTarget()
		{
			try 
			{				
				if( methodType == MethodType.Static )
				{
					object obj = targetType.InvokeMember(methodName,
                        BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static,
						null,null,new object[]{id});
					return obj;
				}
				else
				{
					object obj = Activator.CreateInstance(targetType);
					if( methodType == MethodType.Instance )
						return targetType.InvokeMember(methodName,
							BindingFlags.Public|BindingFlags.InvokeMethod|BindingFlags.Instance,
							null,obj,new object[]{id});
					else 
						return obj; // MethodType.Constructor
				}
			} 
			catch( Exception e ) 
			{
                string templ = I18n.T("Failed to create an instance of the class '{0}'.");
				throw new Exception(string.Format(templ,targetType.FullName,path),e);
			}			
		}
	}
}
