using System;
using CommonLang.IO;
using System.IO;
using CommonLang.Protocol;
using CommonLang.Property;
using CommonLang.Log;
using CommonLang.Net;
using CommonAI.Zone;

namespace CommonAI
{
    public class BattleCodec : INetPackageCodec
    {
        protected Logger log = LoggerFactory.GetLogger("BattleCodec");

        private TemplateManager templates = null;
        private IExternalizableFactory codec = null;

        public BattleCodec(TemplateManager templates) 
        {
            this.codec = TemplateManager.MessageCodec;
            this.templates = templates;
        }
        public TemplateManager Templates { get { return templates; } }
        public CommonLang.IO.IExternalizableFactory Factory { get { return codec; } }

        const int FIXED_HEADER_SIZE = 4;
        const int DEFAULT_BUFFER_SIZE = 1024;

        public virtual bool doDecode(Stream input, out object message)
        {
            InputStream reader = new InputStream(input, codec);
            int typeInt = reader.GetS32();
            Type type = codec.GetType(typeInt);
            if (type == null)
            {
                log.Error("Unknow Protocol : >>>" + typeInt + "<<<");
                message = null;
                return false;
            }
            else
            {
                IMessage nm = (IMessage)ReflectionUtil.CreateInstance(type);
                nm.ReadExternal(reader);
                if (nm is BattleMessage)
                {
                    ((BattleMessage)nm).EndRead(templates);
                }
                message = nm;
                return true;
            }
        }

        public virtual bool doEncode(Stream output, object message)
        {
            IMessage nm = (IMessage)message;
            OutputStream os = new OutputStream(output, codec);
            int typeInt = codec.GetTypeID(message.GetType());
            if (typeInt == 0)
            {
                log.Error("Unknow Protocol : >>>" + typeInt + "<<< - " + message.GetType().FullName);
                return false;
            }
            if (nm is BattleMessage)
            {
                ((BattleMessage)nm).BeforeWrite(templates);
            }
            os.PutS32(typeInt);
            nm.WriteExternal(os);
            return true;
        }
    }

    public class SynchronizedBattleCodec : BattleCodec
    {
        private InputStream mInputStream;
        private OutputStream mOutputStream;
        private MemoryStream mBinBufferIn;
        private MemoryStream mBinBufferOut;

        public SynchronizedBattleCodec(TemplateManager templates)
            : base(templates)
        {
            this.mInputStream = new InputStream(null, Factory);
            this.mOutputStream = new OutputStream(null, Factory);
            this.mBinBufferIn = new MemoryStream(1024);
            this.mBinBufferOut = new MemoryStream(1024);
        }

        public override bool doDecode(Stream input, out object message)
        {
            lock (mInputStream)
            {
                mInputStream.SetStream(input);
                try
                {
                    int typeInt = mInputStream.GetS32();
                    Type type = Factory.GetType(typeInt);
                    if (type == null)
                    {
                        log.Error("Unknow Protocol : >>>" + typeInt + "<<<");
                        message = null;
                        return false;
                    }
                    else
                    {
                        IMessage nm = (IMessage)ReflectionUtil.CreateInstance(type);
                        nm.ReadExternal(mInputStream);
                        if (nm is BattleMessage)
                        {
                            ((BattleMessage)nm).EndRead(Templates);
                        }
                        message = nm;
                        return true;
                    }
                }
                catch (Exception e)
                {
                    message = null;
                    log.Error("[doDecodeBin] error::" + e.Message, e);
                    return false;
                }
                finally
                {
                    mInputStream.SetStream(null);
                }
            }
        }

        public override bool doEncode(Stream output, object message)
        {
            lock (mOutputStream)
            {
                mOutputStream.SetStream(output);
                try
                {
                    IMessage nm = (IMessage)message;
                    int typeInt = Factory.GetTypeID(message.GetType());
                    if (typeInt == 0)
                    {
                        log.Error("Unknow Protocol : >>>" + typeInt + "<<< - " + message.GetType().FullName);
                        return false;
                    }
                    mOutputStream.PutS32(typeInt);
                    if (nm is BattleMessage)
                    {
                        ((BattleMessage)nm).BeforeWrite(Templates);
                    }
                    nm.WriteExternal(mOutputStream);
                    return true;
                }
                finally
                {
                    mOutputStream.SetStream(null);
                }
            }
        }

        public bool doDecodeBin(byte[] input, out object message)
        {
            lock (mBinBufferIn)
            {
                mBinBufferIn.Position = 0;
                mBinBufferIn.Write(input, 0, input.Length);
                mBinBufferIn.Position = 0;
                return doDecode(mBinBufferIn, out message);
            }
        }

        public bool doEncodeBin(out byte[] output, object message)
        {
            lock (mBinBufferOut)
            {
                mBinBufferOut.Position = 0;
                if (doEncode(mBinBufferOut, message))
                {
                    output = new byte[mBinBufferOut.Position];
                    Array.Copy(mBinBufferOut.GetBuffer(), 0, output, 0, output.Length);
                    return true;
                }
                output = null;
                return false;
            }
        }
    }
}