using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
using System.Globalization;

using CommonLang;
using CommonLang.IO;
using CommonLang.IO.Attribute;
using CommonLang.Property;
using CommonLang.Protocol;
using System.Text;

namespace CommonLang.Protocol
{
    public class MessageFactoryGenerator : IExternalizableFactory, IComparer<Type>
    {
        private static Type ext_type = typeof(IExternalizable);
        private HashMap<Type, Type> all_types = new HashMap<Type, Type>();
        private HashMap<int, Type> typecls = new HashMap<int, Type>();
        private HashMap<Type, int> typeids = new HashMap<Type, int>();
        private Log.Logger log = Log.LoggerFactory.GetLogger(typeof(MessageFactoryGenerator).Name);
        private Type[] null_args = new Type[0];
        /// <summary>
        /// 将一个程序集内的所有类符合[MessageType]的类全部注册到编解码器
        /// </summary>
        /// <param name="assembly"></param>
        public void RegistAssembly(params Assembly[] assembly)
        {
            foreach (Assembly asm in assembly)
            {
                Type[] types = null;
                try
                {
                    //log.Info("Get Assembly Types : " + asm.FullName);
                    types = asm.GetTypes();
                }
                catch (Exception err)
                {
                    log.InfoFormat("Get Types Error : {1}\n    {0}", err.Message, asm.FullName);
                    continue;
                }
                try
                {
                    foreach (var type in types)
                    {
                        if (ext_type.IsAssignableFrom(type))
                        {
                            MessageTypeAttribute msgID = PropertyUtil.GetAttribute<MessageTypeAttribute>(type);
                            if (msgID != null)
                            {
                                if (type.GetConstructor(null_args) == null)
                                {
                                    log.ErrorFormat("No default null arguments constructor : {0}\n    {1}();", type.FullName, type.Name);
                                }
                                else
                                { 
                                    all_types.TryAdd(type, type);
                                }
                            }
                        }
                    }
                }
                catch (Exception err)
                {
                    log.Error(err.Message, err);
                }
            }
            SyncType();
        }


        private List<Type> SyncType()
        {
            typecls.Clear();
            typeids.Clear();
            List<Type> typenames = new List<Type>(all_types.Count);
            foreach (Type type in all_types.Values)
            {
                typenames.Add(type);
            }
            typenames.Sort(this);
            for (uint i = 0; i < typenames.Count; i++)
            {
                Type type = typenames[(int)i];
                MessageTypeAttribute msgID = PropertyUtil.GetAttribute<MessageTypeAttribute>(type);
                int id = msgID.MessageTypeID;
                if (id == 0)
                {
                    id = (int)((i | 0x80000000L) & 0xFFFFFFFF);
                }
                if (typecls.ContainsKey(id))
                {
                    Type ot = typecls[id];
                    throw new Exception(string.Format(
                        "Duplicate Type : id = 0x{0} with \"{1}\" - \"{2}\"",
                        id.ToString("X"), type.FullName, ot.FullName));
                }
                typecls.Put(id, type);
                typeids.Put(type, id);
            }
            return typenames;
        }

        public int Compare(Type x, Type y)
        {
            MessageTypeAttribute mtX = PropertyUtil.GetAttribute<MessageTypeAttribute>(x);
            MessageTypeAttribute mtY = PropertyUtil.GetAttribute<MessageTypeAttribute>(y);
            if (mtX != null && mtY != null)
            {
                return mtX.MessageTypeID - mtY.MessageTypeID;
            }
            if (mtX != null)
            {
                return 1;
            }
            if (mtY != null)
            {
                return -1;
            }
            return x.FullName.CompareTo(y.FullName);
        }

        public int GetTypeID(Type type)
        {
            return typeids.Get(type);
        }

        public Type GetType(int id)
        {
            return typecls.Get(id);
        }

        public string ListAll(string prefix = "")
        {
            StringBuilder sb = new StringBuilder();
            List<Type> types = SyncType();
            foreach (Type type in types)
            {
                int id = typeids[type];
                object msg = ReflectionUtil.CreateInstance(type);
                sb.AppendLine(prefix + string.Format("0x{0:X8}", id) + " - " + msg.GetType().FullName);
            }
            return sb.ToString();
        }

    }
}