﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Threading;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System;   
using ProtoBuf;
using Bit = System.BitConverter;

public class NetWorkModule : MonoBehaviour
{
    public static NetWorkModule Current;
    public GameObject testObj;

    Socket socket; //目标socket
    EndPoint clientEnd; //客户端
    IPEndPoint ipEnd; //侦听端口
    string recvStr; //接收的字符串
    string sendStr; //发送的字符串
    byte[] sendData = new byte[4096]; //发送的数据，必须为字节
    byte[] recvData = new byte[4096];
    int recvLen; //接收的数据长度
    Thread connectThread; //连接线程
    public int connectionID;
    List<ConnectionInformation> players = new List<ConnectionInformation>();
    List<INetted> nettedObjs = new List<INetted>();

    List<ConnectionInformation> needDestory;
    List<int> needSpawn;
    List<Updater> updater = new List<Updater>();
    NettedIdentity self;

    struct Updater
    {
        public int objID;
        public MemoryStream[] ms;
    }

    void InitSocket()
    {
        // 定义侦听端口,侦听任何IP
        ipEnd = new IPEndPoint(IPAddress.Any, 0);
        //定义套接字类型,在主线程中定义
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        //服务端需要绑定ip
        socket.Bind(ipEnd);
        //定义客户端
        IPEndPoint sender = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6680);
        clientEnd = (EndPoint)sender;
        //开启一个线程连接，必须的，否则主线程卡死
        connectThread = new Thread(new ThreadStart(SocketReceive));
        connectThread.Start();
    }
    void SocketSend(byte[] Data)
    {
        //清空发送缓存
        //sendData = new byte[4096];
        //数据类型转换
        //发送给指定客户端
        socket.SendTo(Data, Data.Length, SocketFlags.None, clientEnd);
    }
    void SocketReceive()
    {
        //进入接收循环
        while (true)
        {
            //对data清零
            recvData = new byte[4096];
            //获取客户端，获取客户端数据，用引用给客户端赋值
            recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
            System.Array.Resize(ref recvData, recvLen);
            //MemoryStream ms = new MemoryStream(recvData);
            string command = Encoding.ASCII.GetString(recvData, 0, 4);
            if (command == "ASID")//服务器返回的自己的ID
            {
                connectionID = BitConverter.ToInt32(recvData, 4);
                continue;
            }
            if (command == "OLST")//从服务器返回在线玩家列表
            {
                List<int> online = new List<int>();
                int count = BitConverter.ToInt32(recvData, 4);
                for (int i = 0; i < count; i++)
                {
                    online.Add(BitConverter.ToInt32(recvData, 8 + i * 4));
                }
                CheckOnlinePlayer(online);
                continue;
            }
            using (MemoryStream m = new MemoryStream(recvData))
            {
                byte[] data = m.ToArray();               
                Updater u = new Updater();
                u.objID = Bit.ToInt32(data, 0);
                int index = 8, count = Bit.ToInt32(data, 4);
                List<MemoryStream> mss = new List<MemoryStream>();
                for (int i = 0; i < count; i++)
                {
                    int len = Bit.ToInt32(data, index);index += 4;
                    MemoryStream ms = new MemoryStream(data, index, len); index += len;
                    mss.Add(ms);
                }
                u.ms = mss.ToArray();
                //Debug.Log(u.objID+" "+u.ms.Length);
                updater.Add(u);
            }

        }
    }
    void CheckOnlinePlayer(List<int> latest)
    {
        List<ConnectionInformation> disconnected = new List<ConnectionInformation>();
        bool sign = true;
        foreach (var i in players)
        {
            sign = true;
            foreach (int n in latest)
            {
                if (i.connectionID == n)
                {
                    latest.Remove(n);
                    sign = false;
                    break;
                }
            }
            if (sign)
                disconnected.Add(i);
        }
        needDestory = disconnected;
        needSpawn = latest;
    }
    void SocketQuit()
    {
        //关闭线程
        if (connectThread != null)
        {
            connectThread.Interrupt();
            connectThread.Abort();
        }
        //最后关闭socket
        if (socket != null)
            socket.Close();
    }
    public void Broadcast(byte[] data)
    {
        MemoryStream ms = new MemoryStream();
        ms.Write(Encoding.ASCII.GetBytes("BRCT"), 0, 4);
        ms.Write(data, 0, data.Length);
        SocketSend(ms.ToArray());
    }
    public void SentTo(byte[] data, int id)
    {
        MemoryStream ms = new MemoryStream();
        ms.Write(Encoding.ASCII.GetBytes("TAGT"), 0, 4);
        ms.Write(BitConverter.GetBytes(id), 4, 4);
        ms.Write(data, 8, data.Length);
        SocketSend(ms.ToArray());
    }


    void OnApplicationQuit()
    {
        SocketSend(Encoding.ASCII.GetBytes("QUIT"));
        SocketQuit();
    }
    // Start is called before the first frame update
    void Start()
    {
        Current = this;
        InitSocket();
        SocketSend(Encoding.ASCII.GetBytes("CONT"));
    }

    // Update is called once per frame
    float broadcastTime, updateTime;
    private void Update()
    {
        if (Time.realtimeSinceStartup - broadcastTime > 0.5)
        {
            broadcastTime = Time.realtimeSinceStartup;
            if (connectionID != 0)
                SocketSend(Encoding.ASCII.GetBytes("KPAL"));
        }
        if (needDestory != null)
        {
            foreach (var i in needDestory)
            {
                if (i != null)
                {
                    foreach (var n in i.children)
                    {
                        Debug.Log("remove:" + i.connectionID+","+n.gObj.name);
                        Destroy(n.gObj);
                    }
                    players.Remove(i);

                }
            }
            needDestory.Clear();
        }
        if (needSpawn != null)
        {
            foreach (var i in needSpawn)
            {
                GameObject obj = Instantiate(testObj);
                obj.name = "id:"+i;
                NettedIdentity no = obj.AddComponent<NettedIdentity>();
                //no.gObj = obj;
                PhysicNetted pn = obj.AddComponent<PhysicNetted>();
                //pn.gObj = obj;
                ConnectionInformation cinf = new ConnectionInformation(i);
                //nettedObjs.Add(no);
                //cinf.children.Add(no);
                no.objID = i;
                pn.objID = i;
                players.Add(cinf);
                if (i == connectionID) self = no;
            }
            needSpawn.Clear();
        }

        foreach (var u in updater)
        {
            foreach (var ci in players)
            {
                foreach (var o in ci.children)
                {
                    if (o.objID == u.objID)
                    {
                        INetted[] nt = o.gObj.GetComponents<INetted>();
                        for (int ind = 0; ind < nt.Length; ind++)
                        {
                            nt[ind].Receive(u.ms[ind]);
                        }
                    }
                }
            }
        }
        updater.Clear();


        if (Time.realtimeSinceStartup - updateTime > 0.033f)
        {
            updateTime = Time.realtimeSinceStartup;
            MemoryStream ms = new MemoryStream();
            byte[] data;
            using (ms)
            {
                var nettes = self.GetComponents<INetted>();
                ms.Write(Bit.GetBytes(self.objID),0,4);
                ms.Write(Bit.GetBytes(nettes.Length),0,4);
                foreach (var i in nettes)
                {
                    if (i.Send(out data))
                    {
                        ms.Write(Bit.GetBytes(data.Length), 0, 4);
                        ms.Write(data, 0, data.Length);
                    }
                    else
                    {
                        ms.Write(new byte[]{ 0,0,0,0}, 0, 4);
                    }
                }
                //SynTransform t = new SynTransform(self.transform);
                //t.connectionID = connectionID;
                //Serializer.Serialize(ms, t);
                //Debug.Log(ms.ToArray().Length);
                Broadcast(ms.ToArray());
                //Debug.Log(ms.ToArray().Length);
            }
        }
    }
}
