using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Channels;
using System.Linq;
using System.Net.Http;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using ProtoDataBuff;
using BossServer;
using pb = global::Google.Protobuf;

namespace clientTest.bossfight
{
    
    class Program
    {
        static readonly Random r = new Random();

        /// <summary>
        /// 消息分发
        /// </summary>
        static private Dictionary<eProtocalCommand, Action<sSocketData>> callbacks = new Dictionary<eProtocalCommand, Action<sSocketData>>();
        static void Main(string[] args)
        {

            callbacks.Add(eProtocalCommand.ScUpdateProperties, On_update);
            callbacks.Add(eProtocalCommand.ScGameOver, On_GameOver);
            var n = 1;
            var list = new Task[n];
            for (int i = 0; i < n; i++)
            {
                list[i] = Task.Run(async () => await send());
            }
            Task.Run(Dispatch);

            Task.WaitAll(list);
        }

        /// <summary>
        /// 处理客户端上报伤害请求
        /// </summary>
        static void On_update(sSocketData data)
        {
            var msg = SCUpdateProperties.Parser.ParseFrom(data._data);

            Console.WriteLine("最新HP: " + msg.BossHp);
        }

        /// <summary>
        /// 处理客户端上报伤害请求
        /// </summary>
        static void On_GameOver(sSocketData data)
        {
            var msg = SCGameOver.Parser.ParseFrom(data._data);
            Console.WriteLine("战斗结束: " + msg.BossHp);
            Task.Delay(3000).ContinueWith(t => Environment.Exit(0));
        }
        static async Task send()
        {

            var port = 6002;
            var endPoint = new IPEndPoint(IPAddress.Parse("192.168.10.86"), port);
            //var endPoint = new IPEndPoint(IPAddress.Parse("115.159.121.129"), port);

            using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                try
                {
                  await  client.ConnectAsync(endPoint);
                }
                catch (Exception ee)
                {
                    Debug.WriteLine(ee.Message);
                }
                var t = Task.Run(() => recv(client));
                await login(client);
                while (true)
                {
                    try
                    {
                        await ReportDamage(client);
                        Thread.Sleep(r.Next(1000, 3000));
                    }
                    catch (Exception e)
                    {
                        client.Close();
                        break;
                    }
                }
            }
        }

        async static Task login(Socket Sock)
        {
            try
            {
                var msg = new CSEnterFight() { Uid = Guid.NewGuid().ToString(), Zoneid = 1 };
                var data = SocketDataToBytes(BytesToSocketData(eProtocalCommand.CsLogin, IMsg2Bytes(msg)));
                await Sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
            }
            catch (Exception e)
            {
                Sock.Close();
            }
        }

        async static Task ReportDamage(Socket sock)
        {
            var msg = new CSFightReportDamage() { Damage = r.Next(-320, 0) };

            var data = SocketDataToBytes(BytesToSocketData(eProtocalCommand.CsReportDamage, IMsg2Bytes(msg)));
            await sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
        }

        static byte[] IMsg2Bytes(pb::IMessage msg)
        {
            using var ms = new MemoryStream();
            using var goutstream = new pb::CodedOutputStream(ms);
            msg.WriteTo(goutstream);
            goutstream.Flush();
            ms.Seek(0, SeekOrigin.Begin);
            return ms.ToArray();
        }



        static async void Dispatch()
        {
            while (true)
            {
                var msg = await recvDataBuffer.Reader.ReadAsync();
                if (callbacks.ContainsKey(msg._protocallType))
                {
                    callbacks[msg._protocallType](msg);
                }
                else
                {
                    // 未找到消息处理逻辑
                    Console.WriteLine("未识别的消息类型:" + msg._protocallType.ToString());
                }
            }
        }

        /// <summary>
        /// 向客户端写入消息
        /// </summary>
        static async void WriteToserver(Socket Sock)
        {
            while (true)
            {
                var msg = await sendDataBuffer.Reader.ReadAsync();
                var data = SocketDataToBytes(msg);
                await Sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
            }
        }
        /// <summary>
        /// 网络结构转数据
        /// </summary>
        /// <param name="tmpSocketData"></param>
        /// <returns></returns>
        static private byte[] SocketDataToBytes(sSocketData tmpSocketData)
        {
            byte[] _tmpBuff = new byte[tmpSocketData.PackLen];
            byte[] _tmpBuffLength = BitConverter.GetBytes(tmpSocketData.PackLen);
            byte[] _tmpDataLenght = BitConverter.GetBytes((UInt16)tmpSocketData._protocallType);

            Array.Copy(_tmpBuffLength, 0, _tmpBuff, 0, Constants.HEAD_DATA_LEN);//缓存总长度
            Array.Copy(_tmpDataLenght, 0, _tmpBuff, Constants.HEAD_DATA_LEN, Constants.HEAD_TYPE_LEN);//协议类型
            Array.Copy(tmpSocketData._data, 0, _tmpBuff, Constants.HEAD_LEN, tmpSocketData.DataLen);//协议数据

            return _tmpBuff;
        }


        /// <summary>
        /// 数据转网络结构
        /// </summary>
        /// <param name="_protocalType"></param>
        /// <param name="_data"></param>
        /// <returns></returns>
        static private sSocketData BytesToSocketData(eProtocalCommand _protocalType, byte[] _data)
        {
            sSocketData tmpSocketData = new sSocketData();
            tmpSocketData.PackLen = Constants.HEAD_LEN + _data.Length;
            // tmpSocketData._dataLength = _data.Length;
            tmpSocketData._protocallType = _protocalType;
            tmpSocketData._data = _data;
            return tmpSocketData;
        }


        /// <summary>
        /// 接收buffer
        /// </summary>
        static private Channel<sSocketData> recvDataBuffer = Channel.CreateUnbounded<sSocketData>();
        /// <summary>
        /// 发送buffer
        /// </summary>
        static private Channel<sSocketData> sendDataBuffer = Channel.CreateUnbounded<sSocketData>();

        /// <summary>
        /// 接收客户端发来的信息，客户端套接字对象
        /// </summary>
        /// <param name="socketclientpara"></param>    
        static async void recv(Socket socketServer)
        {
            socketServer.ReceiveTimeout = 800;                                             // 接收等待超时时间设为800毫秒
            var _databuffer = new DataBuffer();
            byte[] arrServerRecMsg = new byte[4096];                                       // 创建一个内存缓冲区，其大小为4k字节  
            while (true)
            {
                try
                {
                    var length = await socketServer.ReceiveAsync(new ArraySegment<byte>(arrServerRecMsg), SocketFlags.None);   // 将接收到的信息存入到内存缓冲区，并返回其字节数组的长度    
                    if (length <= 0)                                                      // 视为客户端已经close连接
                    {
                        break;
                    }
                    _databuffer.AddBuffer(arrServerRecMsg, length);                        //将收到的数据添加到缓存器中
                    while (_databuffer.GetData(out sSocketData _socketData))                         //取出一条完整数据
                    {
                        await recvDataBuffer.Writer.WriteAsync(_socketData);                 // 放入channel
                    }
                    Array.Clear(arrServerRecMsg, 0, length);
                }
                catch (SocketException e)
                {
                    if (e.ErrorCode == 10060)                                          // 超时的时候错误号码是10060
                    {
                        continue;                                                      // 继续等待  
                    }
                    break;
                }
                catch (Exception)
                {
                    break;
                }
            }

            socketServer.Close();                                                     // 关闭之前accept出来的和客户端进行通信的套接字 
        }
    }


}
