using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
using nft.framework.drawing;
using nft.framework.plugin;
using nft.util;
using System.IO;
using nft.framework.loader;
using nft.ui.mainframe_old;
using nft.ui.mainframe;

namespace nft.framework
{
	/// <summary>
	/// load core services
	/// </summary>
	public sealed class GlobalModules
	{
        static private Dictionary<Type, GlobalModuleEntry> map = new Dictionary<Type, GlobalModuleEntry>();

        static public IGraphicManagerOld GraphicManagerOld { get { throw new NotImplementedException(); } }

		static public void Initialize(){
			XmlDocument doc = XmlUtil.LoadFile(Path.Combine(Directories.AppBaseDir,"core_modules.xml"));
            ParamsReader reader = new ParamsReader(doc.BaseURI, new XmlParamParser(doc));
            ParamsReader root = reader["modules"];

            ParamsReader frameNode = root["main-frame"];
            CreateMainFrame(frameNode);

            LoadModules(root);

		}

        /// <summary>
        /// Load and create UI MainFrame instance with the 'main-frame' element.
        /// </summary>
        /// <param name="node"></param>
        static private void CreateMainFrame(ParamsReader node) {
            ParamsReader el = node["class"];
            Type t = PluginUtil.loadTypeFromManifest(el);
            object result = null;
            try {
                // give XmlNode as first argument of constructor.
                result = Activator.CreateInstance(t, new object[] { node });
            } catch (Exception e) {
                Debug.WriteLine(e.Message);
                Debug.WriteLine(e.StackTrace);
                string templ = I18n.T("nft.framework.plugin", "Failed to create an instance of the class '{0}'.");
                throw new Exception(string.Format(templ, t.FullName, node.SourceURI), e);
            }

            IMainFrame frame = result as IMainFrame;
            if (frame == null) {
                string templ = I18n.T("nft.framework.plugin", "Cannot cast the class'{0}' to the class'{1}'.");
                object[] args = new object[] { t.FullName, "IMainFrame", node.SourceURI };
                throw new InvalidCastException(string.Format(templ, args));
            }
            // TODO: support factory style.
            Main.setFrame(frame);
        }

        static private void LoadModules(ParamsReader root) {
            foreach (ParamsReader cn in root.EnumChildren("module")) {
                try {
                    GlobalModuleEntry gm = LoadModule(cn);
                    if (gm != null) {
                        Debug.WriteLine(gm.Name + " loaded.");
                        Register(gm);
                    }
                } catch (Exception e) {
                    string txt = I18n.T("ERROR while loading a core module.");
                    if (UIUtil.ShowException(txt, e, UIInformLevel.severe))
                        throw;
                }
            }
        }

		/// <summary>
		/// Load a new object by reading a type from the manifest XML element.
		/// The "codeBase" attribute and the "name" attribute of
		/// a class element are used to determine the class to be loaded.
		/// </summary>
        private static GlobalModuleEntry LoadModule(ParamsReader node) {
            ParamsReader el = node["class"];
			Type t =  PluginUtil.loadTypeFromManifest(el);
            string name = node["name"].InnerText;
            string desc = node["description"].InnerTextOr("");
		    
			GlobalModuleEntry entry = new GlobalModuleEntry(null, null, name, desc);
            object result = null;
            try {
				// give XmlNode as first argument of constructor.
				result = Activator.CreateInstance(t,new object[]{node});
			} catch( Exception e ) {
				Debug.WriteLine(e.Message);
				Debug.WriteLine(e.StackTrace);
                string templ = I18n.T("nft.framework.plugin", "Failed to create an instance of the class '{0}'.");
				throw new Exception(string.Format(templ,t.FullName,node.SourceURI),e);
			}            
			if(!(result is IGlobalModule)){
                string templ = I18n.T("nft.framework.plugin", "Cannot cast the class'{0}' to the class'{1}'.");
                object[] args = new object[] { t.FullName, "IGlobalModule", node.SourceURI };
				throw new InvalidCastException(string.Format(templ,args));
			}
            entry.ModuleInstance = (IGlobalModule)result;
            entry.RegistType = entry.ModuleInstance.RegistType;
            return entry;
		}

        static public void Register(IGlobalModule module, Type typename, String name, String description)
		{
            GlobalModuleEntry entry = new GlobalModuleEntry(module, typename, name, description);
            Register(entry);
		}

        static internal void Register(GlobalModuleEntry entry) {
            map.Add(entry.RegistType, entry);
        }

		static public void Unregister(object module) {
            Type removeType = null;
            foreach (GlobalModuleEntry entry in map.Values) {
                if (entry.ModuleInstance == module) {
                    removeType = entry.RegistType;
                }
            }
            if(removeType!=null)
                map.Remove(removeType);
		}

		static public IGlobalModule GetModule(string typeName){
			Type type = Type.GetType(typeName);
            return GetModule(type);
		}

		static public IGlobalModule GetModule(Type type){
            if (type != null && map.ContainsKey(type))
                return map[type].ModuleInstance;
            else
                return null;
		}
	}

    public interface IGlobalModule {
		Type RegistType { get; }
		//string Name { get; }
		//string Description { get; }
	}

    internal class GlobalModuleEntry
    {
        public GlobalModuleEntry() {}

        public GlobalModuleEntry(IGlobalModule instance, Type rtype, string name, string desc) {
            ModuleInstance = instance;
            RegistType = rtype;
            Name = name;
            Description = desc;
        }

        public IGlobalModule ModuleInstance;
        public Type RegistType;
        public string Name;
        public string Description;
    }
}
