using System;
using base_kcp;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using dotNetty_kcp.thread;
using DotNetty.Buffers;
using fec;
using fec.fec;

namespace dotNetty_kcp
{
    public class ServerChannelHandler:ChannelHandlerAdapter
    {
        private readonly IChannelManager _channelManager;

        private readonly ChannelConfig _channelConfig ;

        private readonly IExecutorPool _executorPool;

        private readonly KcpListener _kcpListener;
        private readonly IScheduleThread _scheduleThread;


        public ServerChannelHandler(IChannelManager channelManager, ChannelConfig channelConfig, IExecutorPool executorPool, KcpListener kcpListener, IScheduleThread scheduleThread)
        {
            _channelManager = channelManager;
            _channelConfig = channelConfig;
            _executorPool = executorPool;
            _kcpListener = kcpListener;
            _scheduleThread = scheduleThread;
        }

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            Console.WriteLine(exception);
        }


        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            var msg = (DatagramPacket) message;
            var channel = context.Channel;
            var ukcp = _channelManager.get(msg);
            var content = msg.Content;
            User user;
            if (ukcp != null)
            {
                user = ukcp.user();
                //每次收到消息重绑定地址
                user.RemoteAddress = msg.Sender;
                ukcp.read(content);
                return;
            }
            
            
            //如果是新连接第一个包的sn必须为0
            var sn = getSn(content,_channelConfig);
            if(sn!=0)
            {
                msg.Release();
                return;
            }

            var messageExecutor = _executorPool.GetAutoMessageExecutor();
            KcpOutput kcpOutput = new KcpOutPutImp();

            ReedSolomon reedSolomon = null;
            if(_channelConfig.FecDataShardCount!=0&&_channelConfig.FecParityShardCount!=0){
                reedSolomon = ReedSolomon.create(_channelConfig.FecDataShardCount,_channelConfig.FecParityShardCount);
            }

            ukcp = new Ukcp(kcpOutput,_kcpListener,messageExecutor,reedSolomon,_channelConfig);

            user = new User(channel,msg.Sender,msg.Recipient);
            ukcp.user(user);
            _channelManager.New(msg.Sender,ukcp,msg);
            
            messageExecutor.execute(new ConnectTask(ukcp, _kcpListener));
            
            ukcp.read(content);

            var scheduleTask = new ScheduleTask(_channelManager,ukcp,_scheduleThread);
            _scheduleThread.schedule(scheduleTask, TimeSpan.FromMilliseconds(ukcp.getInterval()));
        }
        
        
        private int getSn(IByteBuffer byteBuf,ChannelConfig channelConfig){
            var headerSize = 0;
            if (channelConfig.Crc32Check)
            {
                headerSize+=Ukcp.HEADER_CRC;
            }
            if(channelConfig.FecDataShardCount!=0&&channelConfig.FecParityShardCount!=0){
                headerSize+= Fec.fecHeaderSizePlus2;
            }
            var sn = byteBuf.GetIntLE(byteBuf.ReaderIndex+Kcp.IKCP_SN_OFFSET+headerSize);
            return sn;
        }


    }
}