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;

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"];
            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 = Main.resources["global_modules.load_error"].stringValue;
					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 = Main.resources["xml.class_load_error"].stringValue;
				throw new Exception(string.Format(templ,t.FullName,node.SourceURI),e);
			}            
			if(!(result is IGlobalModule)){
				string templ = Main.resources["xml.class_cast_error"].stringValue;
                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;
    }
}
