﻿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 System.Reflection;
using ProtoBuf.Meta;
using Bit = System.BitConverter;
using Gaazar;

public struct Updater
{
    public int objID;
    public NettedInfo[] netteds;
}
public struct NettedInfo
{
    public int index;
    public MemoryStream ms;
}
[DisallowMultipleComponent]
public class NettedManager:MonoBehaviour
{
    static public NettedManager current;
    static bool basicRegistered;
    static bool assetInitiated;
    static Assembly[] asbs = new Assembly[2];
    List<Connection> connections = new List<Connection>();
    Socket socket; //目标socket
    EndPoint clientEnd; //客户端
    IPEndPoint ipEnd; //侦听端口
    Thread connectThread; //连接线程
    List<Updater> updaters = new List<Updater>();

    INettedNetwork network;
    public List<NettedIdentity> gObjPool = new List<NettedIdentity>();
    public int connectionID;

    object lockobj = new object();
    
    void Start()
    {
        current = this;
        RegisterBasicTpyes();
        InitAsset();
        //network = new NettedNetwork();
        //network.Init();
        InitSocket();
        SocketSend(Encoding.ASCII.GetBytes("CONT"));
    }
    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()
    {
        //进入接收循环
        byte[] recvData;
        int recvLen;
        while (true)
        {
            //对data清零
            recvData = new byte[4096];
            //获取客户端，获取客户端数据，用引用给客户端赋值
            recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
            //MemoryStream ms = new MemoryStream(recvData);
            string command = Encoding.ASCII.GetString(recvData, 0, 4);
            if (command == "ASID")//服务器返回的自己的ID
            {
                connectionID = Bit.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;
            }
            int senderID;
            if (command == "GTNS")
            {
                using (MemoryStream m = new MemoryStream(recvData))
                {
                    BinaryReader br = new BinaryReader(m);
                    senderID = br.ReadInt32();
                    int count = br.ReadInt32();
                    string cmpName;
                    for (int i = 0; i < count; i++)
                    {

                    }
                }
                    continue;
            }
            using (MemoryStream m = new MemoryStream(recvData))
            {
                BinaryReader br = new BinaryReader(m);
                senderID = br.ReadInt32();
                int objcount = br.ReadInt32();
                for (int i = 0; i < objcount; i++)
                {
                    Updater u = new Updater();
                    u.objID = br.ReadInt32();
                    int intdcount = br.ReadInt32();
                    List<NettedInfo> nfs = new List<NettedInfo>();
                    for (int n = 0; i < intdcount; n++)
                    {
                        NettedInfo nf = new NettedInfo();
                        nf.index = Bit.ToInt32(new byte[] { br.ReadByte(), 0, 0, 0 }, 0);
                        int len = br.ReadInt32();
                        nf.ms = new MemoryStream(br.ReadBytes(len));
                        nfs.Add(nf);
                    }
                    u.netteds = nfs.ToArray();
                    lock (lockobj)
                    {
                        updaters.Add(u);
                    }
                }
            }
        }
    }

    private void CheckOnlinePlayer(List<int> online)
    {
        List<Connection> toBeDestoryedConnections = new List<Connection>();
        //List<Connection> toBeCreate = new List<Connection>();
        Connection c = null;
        bool contained;
        for (int i = connections.Count-1; i >=0; i--)
        {
            contained = false;
            for (int n = online.Count - 1; n >= 0; n--)
            {
                if (connections[i].id == online[n])
                {
                    contained = true;
                    break;
                }
            }
            if (!contained) toBeDestoryedConnections.Add(connections[i]);
        }
        /*
        foreach (var i in online)
        {
            contained = false;
            foreach (var n in connections)
            {
                if (i == n.id)
                {
                    contained = true;
                    break;
                }
            }
            if (!contained)
            {
                connections.Add(new Connection(i));
            }
        }
        foreach (var i in connections)
        {
            contained = false;
            foreach (var n in online)
            {
                if (i.id == n)
                {
                    c = i;
                    contained = true;
                    break;
                }
            }
            if (!contained)
            {
                toBeDestoryedConnections.Add(c);
            }
        }
        foreach (var i in toBeDestoryedConnections)
        {
            connections.Remove(connections.Find(s => s.id == i.id));
        }*/
    }
    [System.Obsolete]
    bool PackObject(GameObject gObj, out byte[] bytes)
    {
        bytes = null;
        INetted[] netteds = gObj.GetComponents<INetted>();
        int count=0,c=0;
        MemoryStream ms = new MemoryStream();
        BinaryWriter bw = new BinaryWriter(ms);
        if (netteds.Length == 0) return false;
        bw.Write(gObj.GetComponent<NettedIdentity>().objID);
        foreach (var i in netteds)
        {
            byte[] bs;
            if (i.Send(out bs))
            {
                bw.Write((byte)c);
                bw.Write(bs.Length);
                bw.Write(bs);
                c++;
            }
            count++;
        }
        bw.Seek(4, SeekOrigin.Begin);
        bw.Write(c);
        bytes = ms.ToArray();
        return true;
    }
    bool Pack(out byte[] bytes)
    {
        bytes = null;
        int count=0;
        MemoryStream ms = new MemoryStream();
        BinaryWriter bw = new BinaryWriter(ms);
        bw.Write(connectionID); // sender id
        byte[] ob;
        for (int i = 0; i < gObjPool.Count; i++)
        {
            if (gObjPool[i].PackObject(out ob))
            {
                count++;
                bw.Write(ob);
            }
        }
        bw.Seek(4,SeekOrigin.Begin);
        bw.Write(count);
        return true;
    }
    bool Pack(out byte[] bytes,bool full)
    {
        bytes = null;
        int count = 0;
        MemoryStream ms = new MemoryStream();
        BinaryWriter bw = new BinaryWriter(ms);
        bw.Write(connectionID); // sender id
        byte[] ob;
        for (int i = 0; i < gObjPool.Count; i++)
        {
            gObjPool[i].PackObject(out ob, full);
            count++;
            bw.Write(ob);
        }
        bw.Seek(4, SeekOrigin.Begin);
        bw.Write(count);
        bytes = ms.ToArray();
        bw.Dispose();
        ms.Dispose();
        return true;
    }
    public static GameObject CreateInstance(string[] componentsName,int nobjID)
    {

        GameObject go = new GameObject();
        foreach (var n in componentsName)        
        {
            foreach (var i in asbs)
            {
                //if (n == "UnityEngine.MeshFilter" || n == "UnityEngine.MeshRenderer") continue;
                Type t = i.GetType(n);
                if (t == null) continue;
                go.AddComponent(t);
                break;
            }
        }
        NettedIdentity nid = go.AddComponent<NettedIdentity>();
        nid.objID = nobjID;
        go.AddComponent<NettedIdentity>();
        return go;
    }
    public static GameObject CreateInstance(string[] componentsName, int nobjID,string objName)
    {
        GameObject go = CreateInstance(componentsName, nobjID);
        go.name = objName;
        return go;
    }
    public static GameObject CreateInstance(string prefabName, int nobjID)
    {
        GameObject go = null;
        foreach (var i in AssetBundle.GetAllLoadedAssetBundles())
        {
            foreach (var n in i.GetAllAssetNames())
            {
                string fname = Path.GetFileName(n);
                if (fname == prefabName + ".prefab")
                {
                    go = Instantiate(i.LoadAsset<GameObject>(prefabName));
                }
            }
        }
        NettedIdentity nid = go.AddComponent<NettedIdentity>();
        nid.objID = nobjID;
        nid.prefabName = prefabName;
        return go;
    }

    public static GameObject CreateInstance(string prefabName,int nobjID, string objName)
    {
        GameObject go = CreateInstance(prefabName,nobjID);
        go.name = objName;
        return go;
    }
    public static GameObject CreateInstance(string prefabName,int nobjID, string[] extraComponents)
    {
        GameObject go =null;
        foreach (var i in AssetBundle.GetAllLoadedAssetBundles())
        {
            if (i.Contains(prefabName))
            {
                go = i.LoadAsset<GameObject>(prefabName);
            }
        }
        foreach (var n in extraComponents)
        {
            foreach (var i in asbs)
            {
                //if (n == "UnityEngine.MeshFilter" || n == "UnityEngine.MeshRenderer") continue;
                Type t = i.GetType(n);
                if (t == null) continue;
                go.AddComponent(t);
                break;
            }
        }
        NettedIdentity nid = go.AddComponent<NettedIdentity>();
        nid.objID = nobjID;
        nid.prefabName = prefabName;

        return go;
    }
    public static GameObject CreateInstance(string prefabName, int nobjID, string[] extraComponents, string objName)
    {
        GameObject go = CreateInstance(prefabName, nobjID, extraComponents);
        go.name = objName;
        return go;
    }

    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(BitConverter.GetBytes(connectionID),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();
    }
    public static void RegisterBasicTpyes()
    {
        if (basicRegistered) return;
        MetaType mt = RuntimeTypeModel.Default.Add(typeof(Vector3), false);
        mt.Add(1,"x").Add(2, "y").Add(3, "z");
        mt = RuntimeTypeModel.Default.Add(typeof(Quaternion), false);
        mt.Add(1, "w").Add(2, "x").Add(3, "y").Add(4, "z");
        mt = RuntimeTypeModel.Default.Add(typeof(Vector2), false);
        mt.Add(1, "x").Add(2, "y");
        mt = RuntimeTypeModel.Default.Add(typeof(Vector4), false);
        mt.Add(1, "w").Add(2, "x").Add(3, "y").Add(4, "z");
        mt = RuntimeTypeModel.Default.Add(typeof(Color), false);
        mt.Add(1, "r").Add(2, "g").Add(3, "b").Add(4, "a").AsReferenceDefault=false;
        mt = RuntimeTypeModel.Default.Add(typeof(Rigidbody), false);
        mt.Add(1, "drag").Add(2, "angularDrag").Add(3, "velocity").Add(4, "angularVelocity").Add(5, "mass").Add(6, "centerOfMass").Add(7, "isKinematic").Add(8, "useGravity");
        mt = RuntimeTypeModel.Default.Add(typeof(Transform), false);
        mt.Add(1, "localPosition").Add(2, "localRotation").Add(3, "localScale");
        
        asbs[0] = Assembly.GetExecutingAssembly();
        asbs[1] = Assembly.Load("UnityEngine");
    }
    static void InitAsset()
    {
        var ab = AssetBundle.LoadFromFile (@"AssetBundles\vehicle\insurgent.veh");
        ab = AssetBundle.LoadFromFile(@"AssetBundles\vehicle\insurgent2.veh");

        foreach (var i in AssetBundle.GetAllLoadedAssetBundles())
        {
            
            foreach (var n in i.GetAllAssetNames())
            {
        //        Debug.Log(n);
            }
        }
        //LoadAsset<Texture2D>("bodyshell");
        return;
        Mesh[] ms = ab.LoadAssetWithSubAssets<Mesh>("bodyshell");
        UnityEngine.Object g = ab.LoadAsset("insurgent.veh");
        
        
        foreach (var i in ms)
        {
            Debug.Log(i.name);
        }

    }
    public static T LoadAsset<T>(string name) where T : UnityEngine.Object //OnlyAsset not prefab
    {
        name = name.ToLower();
        foreach (var i in AssetBundle.GetAllLoadedAssetBundles())
        {
            foreach (var n in i.GetAllAssetNames())
            {
                string fname = Path.GetFileNameWithoutExtension(n);
                if (fname == name)
                {
                    //Debug.Log("AssetLoaded:"+n+" as "+typeof(T).Name);
                    return i.LoadAsset(n) as T;
                }
            }
        }
        return null;
    }



    float lastUpdateTime=0;
    private void Update()
    {

        byte[] nBytes;
        NettedIdentity nid;
        if (Time.realtimeSinceStartup - lastUpdateTime > 0.5f)//Time.realtimeSinceStartup - lastUpdateTime > 5f
        {
            lastUpdateTime = Time.realtimeSinceStartup;
            Pack(out nBytes);
            //UnityEngine.Debug.Log(nBytes.Length + "|" + BitConverter.ToString(nBytes));
        }

        lock (lockobj)
        {
            for(int i = updaters.Count-1;i>=0;i--)
            {
                //updaters[i].objID
                nid = gObjPool.Find(s => s.objID == updaters[i].objID);
                foreach (var n in updaters[i].netteds) 
                {
                    (nid.netteds[n.index] as INetted).Receive(n.ms);
                }
                
                

                updaters.RemoveAt(i);
            }
        }
    }
}

public class Connection
{
    public NettedIdentity[] children;
    public int id;
    public Connection(int _id)
    {
        id = _id;
    }
}
public interface INetted
{
    int objID { get; set; }
    NettedBehavior gObj { get; set; }
    Connection owner { get; set; }
    void Receive(System.IO.MemoryStream ms);
    bool Send(out byte[] data);

}