using CommonAI;
using CommonAI.Zone.ZoneEditor;
using CommonAIServer.Node;
using CommonLang.Net;
using CommonServer.Server;
using CommonServer.SSocket.SuperSocket;
using System;
using CommonFroms.G2D;
using CommonAIServer.Node.Interface;
using CommonLang;
using CommonLang.Log;
using CommonLang.Protocol;
using RoomService.Net.BsServer;
using System.Threading;
using CommonAIServer.Connector;
using CommonAI.Zone;
using System.IO;
using CommonLang.IO;
using CommonLang.Property;

namespace GameEditorPluginServer.Server
{
    public class ServerNode : IServerListener
    {
        protected readonly Logger log = LoggerFactory.GetLogger("ServerNode");
        private readonly ServerFactory mServerFactory;

        private readonly EditorTemplates mDataRoot;
        private readonly ZoneNodeConfig mConfig;
        private readonly ZoneNode mNode;
        private readonly SocketAcceptor mServer;
        private readonly int mSceneID;
        private readonly ServerCodec mCodec;

        public ServerNode(EditorTemplates dataroot, ZoneNodeConfig cfg, int sceneID)
        {
            this.mDataRoot = dataroot;
            this.mConfig = cfg;
            this.mSceneID = sceneID;

            this.mCodec = new ServerCodec(dataroot.Templates);

            this.mServerFactory = new ServerFactory();
            this.mServer = mServerFactory.CreateServer(mCodec) as SocketAcceptor;

            this.mNode = new ZoneNode(dataroot, cfg);
            this.mNode.OnZoneStart += MNode_OnZoneStart;
            this.mNode.OnZoneStop += MNode_OnZoneStop;
        }

        public void Start()
        {
            var data = mNode.DataRoot.LoadScene(mSceneID, true, false);
            this.mNode.Start(data, null, null);
        }

        public void Dispose()
        {
            mServer.Dispose();
            mNode.Stop();
        }

        //-------------------------------------------------------------------------------------------------

        public ZoneNodeConfig Config
        {
            get { return mConfig; }
        }
        public ZoneNode Node
        {
            get { return mNode; }
        }
        public SocketAcceptor Server
        {
            get { return mServer; }
        }
        public EditorTemplates DataRoot
        {
            get { return mDataRoot; }
        }
        public bool Running
        {
            get { return mNode.IsRunning; }
        }
        public ServerCodec Codec
        {
            get { return mCodec; }
        }

        //-------------------------------------------------------------------------------------------------
        #region ServerAndRoom

        private void InternalOpen()
        {
            try
            {
                Random random = new Random();
                int port = random.Next(10000, 60000);
                this.mServer.Open("127.0.0.1", port, this);
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
        }

        private void MNode_OnZoneStop(BaseZoneNode zone)
        {
        }
        private void MNode_OnZoneStart(BaseZoneNode zone)
        {
            new Thread(InternalOpen).Start();
        }

        void IServerListener.OnInit(IServer server)
        {
        }
        void IServerListener.OnDestory()
        {
        }
        ISessionListener IServerListener.OnSessionConnected(ISession session)
        {
            return new SessionPlayer(session as Session, mNode);
        }

        #endregion
        //-------------------------------------------------------------------------------------------------


    }

    //-------------------------------------------------------------------------------------------------

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

        private HashMap<Type, Record> mTypeBytes = new HashMap<Type, Record>();

        public ServerCodec(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);
                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;
                }
            }
        }

        public override bool doEncode(Stream output, object message)
        {
            lock (mOutputStream)
            {
                Type type = message.GetType();
                int typeInt = Factory.GetTypeID(type);
                if (typeInt == 0)
                {
                    log.Error("Unknow Protocol : >>>" + typeInt + "<<< - " + message.GetType().FullName);
                    return false;
                }
                long pos = output.Position;
                mOutputStream.SetStream(output);
                mOutputStream.PutS32(typeInt);
                IMessage nm = (IMessage)message;
                if (nm is BattleMessage)
                {
                    ((BattleMessage)nm).BeforeWrite(Templates);
                }
                nm.WriteExternal(mOutputStream);
                long len = output.Position - pos;
                Record rec;
                if (mTypeBytes.TryGetValue(type, out rec))
                {
                    rec.Bytes += len;
                    rec.Count++;
                    mTypeBytes.Put(type, rec);
                }
                else
                {
                    rec.Bytes = len;
                    rec.Count = 1;
                    mTypeBytes.Put(type, rec);
                }
                return true;
            }
        }

        public HashMap<Type, Record> GetSentTypeBytes()
        {
            lock (mOutputStream)
            {
                return new HashMap<Type, Record>(mTypeBytes);
            }
        }

        public struct Record
        {
            public long Bytes;
            public int Count;
        }
    }

    //-------------------------------------------------------------------------------------------------
    internal class SessionPlayer : IPlayer, ISessionListener
    {
        private static Logger log = LoggerFactory.GetLogger("SessionPlayer");

        private readonly ZoneNode node;
        private readonly Session session;
        private readonly SynchronizedBattleCodec codec;

        private readonly HashMap<string, object> mAttributes = new HashMap<string, object>();
        private Action<object> mRecvHandler;

        private EnterRoomRequestC2B login_data;
        private string player_uuid;
        private string display_name;
        private int unit_template_id;
        private int force;



        public SessionPlayer(Session session, ZoneNode node)
        {
            this.node = node;
            this.session = session;
            this.codec = new SynchronizedBattleCodec(node.Templates);
        }

        //----------------------------------------------------------------------------
        #region IPlayer

        public string PlayerUUID { get { return player_uuid; } }
        public string DisplayName { get { return display_name; } }
        public ZoneNode.PlayerClient BindingPlayer { get; set; }

        bool IPlayer.IsAttribute(string key)
        {
            return mAttributes.ContainsKey(key);
        }
        void IPlayer.SetAttribute(string key, object value)
        {
            mAttributes.Put(key, value);
        }
        object IPlayer.GetAttribute(string key)
        {
            return mAttributes.Get(key);
        }
        void IPlayer.Send(IMessage msg)
        {
            session.Send(msg);
        }
        void IPlayer.Listen(Action<object> handler)
        {
            mRecvHandler = handler;
        }
        void IPlayer.OnConnected(ZoneNode.PlayerClient binding)
        {
        }
        void IPlayer.OnDisconnect(ZoneNode.PlayerClient binding)
        {
            mRecvHandler = null;
        }
        #endregion
        //----------------------------------------------------------------------------
        #region ISessionListener

        void ISessionListener.OnConnected(ISession session)
        {
        }
        void ISessionListener.OnDisconnected(ISession session, bool force, string reason)
        {
            if (node != null)
            {
                node.PlayerLeave(this.BindingPlayer.Actor, (c) => { }, (e) => { }, true);
            }
        }
        void ISessionListener.OnError(ISession session, Exception err)
        {
        }
        void ISessionListener.OnSentMessage(ISession session, object message)
        {
        }
        void ISessionListener.OnReceivedMessage(ISession session, object message)
        {
            try
            {
                if (message is EnterRoomRequestC2B)
                {
                    do_EnterRoomResponseB2C(message as EnterRoomRequestC2B);
                }
                else if (message is LeaveRoomRequestC2B)
                {
                    do_LeaveRoomRequestC2B(message as LeaveRoomRequestC2B);
                }
                else if (mRecvHandler != null)
                {
                    mRecvHandler(message);
                }
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
        }

        #endregion


        private void do_EnterRoomResponseB2C(EnterRoomRequestC2B req)
        {
            this.login_data = req;
            this.player_uuid = req.PlayerUUID;
            this.display_name = req.TestToken.PlayerDisplayName;
            this.unit_template_id = req.TestToken.Data.UnitTemplateID;
            this.force = req.TestToken.Data.Force;
            var temp = node.Templates.getUnit(unit_template_id);
            node.PlayerEnter(this, temp, force, 0, 0, null,0, (c) => { }, (e)=> { });
        }

        private void do_LeaveRoomRequestC2B(LeaveRoomRequestC2B req)
        {
            this.login_data = null;
            node.PlayerLeave(this.BindingPlayer.Actor, (c) => { }, (e) => { }, false);
        }


    }

}