using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Serialization;
using nft.framework;
using System.IO;

namespace nft.framework.plugin
{
	/// <summary>
	/// Allows objects from plug-ins to be de-serialized.
	/// </summary>
	public class PluginSerializationBinder : SerializationBinder
	{
		static public readonly PluginSerializationBinder theInstance;

		static PluginSerializationBinder()
		{
			theInstance = new PluginSerializationBinder();
		}

		private PluginSerializationBinder(){
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(OnAssemblyResolve);
            Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly a in asms)
            {
                if (a.GlobalAssemblyCache) continue;
                asmcache.Add(a.CodeBase, a);
            }
        }

        #region resolve assembly on deserialize
        private static string getModuleName(string name) {
            return name.Substring(0, name.IndexOf(','));
        }

        private Assembly OnAssemblyResolve(object sender, ResolveEventArgs args) {
            // TODO: what is the correct way to use an application specific logic to resolve assemblies
            string name = getModuleName(args.Name);
            Trace.WriteLine("OnAssemblyResolve :" + args.Name);
            Assembly asm = null;
            // try assemblies of plug-ins
            if(assemblies.TryGetValue(name, out asm)){
                Trace.WriteLine("Find");
            }
            return asm;
        }
        #endregion

        protected Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>();

		static public void registerAssembly(Assembly asm)
		{
			theInstance.addAssembly(asm);
		}

        protected static Dictionary<string, Assembly> asmcache = new Dictionary<string, Assembly>();
        public static Assembly SafeLoadAssembly(IFileSource f) {
            Assembly a = null;
            LocalFile lfile = f as LocalFile;
            if (!asmcache.TryGetValue(f.Uri, out a))
            {
                if (lfile != null)
                {
                    // load assumig from local strage.
                    a = Assembly.LoadFrom(lfile.AbsolutePath);
                }
                else
                {
                    // load from byte stream.
                    using (Stream stream = f.OpenRead())
                    {
                        byte[] dllimage = new byte[stream.Length];
                        stream.Read(dllimage, 0, dllimage.Length);
                        stream.Close();
                        a = Assembly.Load(dllimage);
                    }
                }
                asmcache.Add(f.Uri, a);
            }
            return a;
        }

		protected void addAssembly(Assembly asm)
		{
			if(!assemblies.ContainsKey(asm.GetName().Name))
				assemblies.Add(asm.GetName().Name,asm);
		}

		public override Type BindToType(string assemblyName, string typeName) 
		{
			string shortName = assemblyName.Substring(0,assemblyName.IndexOf(','));
			Assembly assem = assemblies[shortName];
			Type t = null;
			if(assem!=null)
				t = assem.GetType(typeName);
			if( t!=null )
				return t;
			Trace.WriteLine("search for all assemblies.("+shortName+","+typeName+")");
			foreach( Assembly asm in assemblies.Values )
			{
				t = asm.GetType(typeName);
				if(t!=null)	return t;
			}
			Trace.WriteLine("type ["+typeName+"] is not found.");
			return null;
		}


		//		public override System.Type BindToType(string assemblyName, string typeName) {
		//			Type t;
		//			
		//			t = Type.GetType(typeName);
		//			if(t!=null)		return t;
		//
		//			Trace.WriteLine("binding "+typeName);
		//
		//			// try assemblies of plug-ins
		//			foreach( Contribution cont in Core.plugins.contributions ) {
		//				 Assembly asm = cont.assembly;
		//				if(asm!=null) {
		//					t = asm.GetType(typeName);
		//					if(t!=null)	return t;
		//				}
		//			}
		//			Trace.WriteLine("not found");
		//			return null;
		//		}		public override System.Type BindToType(string assemblyName, string typeName) {
	}
}
