using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonLang.IO.Attribute
{
    [AttributeUsage(AttributeTargets.Class)]
    public class MessageTypeAttribute : System.Attribute
    {
        readonly int _messageTypeID;

        public MessageTypeAttribute(int messageType)
        {
            _messageTypeID = messageType;
        }
        public int MessageTypeID
        {
            get { return _messageTypeID; }
        }
    }

    public class MessageTypeFactory : IExternalizableFactory
    {
        private static Log.Logger log = Log.LoggerFactory.GetLogger(typeof(MessageTypeFactory).Name);
        private static MessageTypeFactory instance = new MessageTypeFactory();
        private static HashMap<int, Type> typecls = new HashMap<int, Type>();
        private static HashMap<Type, int> typeids = new HashMap<Type, int>();

        internal static int HashString(string str)
        {
            // BKDR Hash Function
            long seed = 131; // 31 131 1313 13131 131313 etc..
            long hash = 0;
            char[] chars = str.ToCharArray();//.toCharArray();
            foreach (char ch in chars)
            {
                hash = hash * seed + ch;
            }
            return (int)(hash & 0x7FFFFFFF);
        }

        internal static MessageTypeAttribute GetMessageType(Type type)
        {
            object[] infos = type.GetCustomAttributes(typeof(MessageTypeAttribute), false);
            foreach (object info in infos)
            {
                if (info is MessageTypeAttribute)
                {
                    MessageTypeAttribute msgtype = info as MessageTypeAttribute;
                    return msgtype;
                }
            }
            return null;
        }

        public static int RegistClass(Type type)
        {
            int msgID = 0;
            MessageTypeAttribute msgtype = GetMessageType(type);
            if (msgtype != null && msgtype.MessageTypeID != 0)
            {
                msgID = msgtype.MessageTypeID;
            }
            else
            {
                msgID = MessageTypeFactory.HashString(type.FullName);
            }
            if (type.IsAssignableFrom(typeof(IExternalizable)))
            {
                throw new Exception("Type [" + type + "] is not a IExternalizable !");
            }
            if (typecls.ContainsKey(msgID))
            {
                Type exist = typecls.Get(msgID);
                throw new Exception("Type [" + type + "] Already have a MessageID [" + exist + "] !");
            }
            typecls.Add(msgID, type);
            typeids.Add(type, msgID);
            log.Info("Add Message Type : " + type + " -> " + msgID);
            return msgID;
        }


        public static MessageTypeFactory Instance { get { return instance; } }

        #region IExternalizableFactory 成员

        public int GetTypeID(Type type)
        {
            int ret = 0;
            if (typeids.TryGetValue(type, out ret))
            {
                return ret;
            }
            else
            {
                try
                {
                    ret = RegistClass(type);
                }
                catch (Exception err)
                {
                    log.Error(err.Message, err);
                }
            }
            return ret;
        }

        public Type GetType(int id)
        {
            Type type = null;
            if (typecls.TryGetValue(id, out type))
            {
                return type;
            }
            return null;
        }
        #endregion
    }
}