using System;
 using System.Net;
 using System.Net.Sockets;
 using System.Security.Cryptography;
 using System.Text;
 using System.Threading;
 using UnityEngine;
 using System.Collections.Generic;
 using System.Collections;
 /// <summary>
 /// Socket通信 与后台交互
 /// </summary>
 public class SocketCommunications : MonoBehaviour
 {
     private Socket clientSocket;//Socket定义


     private bool timeInterval;//时间间隔
     private int timeCount = 0;//时间计数
     private float mSynchronous;//时间计数 2
     private bool isReconnect = false;//是否是断线重连


     private void Awake()
     {
     }


     private void Start()
     {
     }


     /// <summary>
     /// 监控客户端与服务器端是否在链接中
     /// </summary>
     private void FixedUpdate()
     {
         HearBeatCount();
     }


     private void OnDisable()
     {
         DisConnet();
     }


     //------------外部接口------------//


     #region 公共接口端


     /// <summary>
     /// 公共接口 Socket连接
     /// </summary>
     /// <param name="serverIp">Ip地址</param>
     /// <param name="serverPort">端口号</param>
     public void PublicSocketConnect(string serverIp, int serverPort)
     {
         SocketConnect(serverIp, serverPort);
     }




     #region 心跳  证明 客户端跟服务器是在链接状态中


     #region 心跳 方法一


     /// <summary>
     /// 发送信号,说明自己还活着  心跳 功能
     /// </summary>
     private void SendHearBeat()
     {
         if (clientSocket.Connected)
         {
             Send("客户端、服务器 正在 甜蜜 牵手......");
         }
     }
     /// <summary>
     /// 心跳时间 计数
     /// </summary>
     private void HearBeatCount()
     {
         if (timeInterval)
         {
             timeCount += 1;


             if (timeCount % 3000 == 0)
             {
                 SendHearBeat();
                 timeCount = 0;


                 if (!clientSocket.Connected)
                 {
                     if (Application.isPlaying)
                     {
                         //当客户端与服务器失去联系时,进行提示 并 重新链接
                     }
                 }
             }
         }
     }


     #endregion


     #region 方法二 心跳计数


     /// <summary>
     /// 在Update中每120s的时候同步一次
     /// </summary>
     private void HearBeatCounting()
     {
         if (timeInterval)
         {
             mSynchronous += Time.deltaTime;
             //在Update中每120s的时候同步一次
             if (mSynchronous > 120.0f)
             {
                 SendHearBeat();
                 mSynchronous = 0;
             }
         }
     }


     /// <summary>
     /// 心跳  停止计数
     /// </summary>
     private void HearBeatCountStopping()
     {
         if (timeInterval)
         {
             timeInterval = false;
             mSynchronous = 0;
         }
     }


     #endregion 方法二 心跳计数


     /// <summary>
     /// 心跳  停止计数
     /// </summary>
     public void HearBeatCountStop()
     {
         if (timeInterval)
         {
             timeInterval = false;
             timeCount = 0;
         }
     }


     #endregion 心跳 证明 客户端跟服务器是在链接状态中


     #endregion 公共接口端


     //------------Socket------------//


     #region Socket 代码区域


     /// <summary>
     /// Socket连接
     /// </summary>
     /// <param name="serverIP">IP</param>
     /// <param name="serverPort">Port</param>
     private void SocketConnect(string serverIP, int serverPort)
     {
         clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
         IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
         try
         {
             clientSocket.Connect(ipe);


             if (clientSocket.Connected)
             {
                 //重连成功后,取消异常显示
                 if (isReconnect)
                 {
                     isReconnect = false;
                 }
                 ThreadReceiveSocket();//开启线程
                 ThreadHearBeat();//开启证明自己还活着
             }
         }
         catch (Exception e)
         {
             Debug.LogError("发送错误:" + e.Message);
         }
     }


     private Thread thread;


     /// <summary>
     /// 开启接收socket线程
     /// </summary>
     private void ThreadReceiveSocket()
     {
         if (clientSocket.Connected)
         {
             thread = new Thread(new ThreadStart(ReceiveSocket));
             thread.IsBackground = true;
             thread.Start();
         }
     }


     /// <summary>
     /// 发送信号,说明自己还活着  心跳 功能
     /// </summary>
     private void ThreadHearBeat()
     {
         if (clientSocket.Connected)
         {
             timeInterval = true;
         }
     }


     /// <summary>
     /// 断开连接
     /// </summary>
     public void DisConnet()
     {
         HearBeatCountStop();//心跳停止 计数


         if (clientSocket != null && clientSocket.Connected)
         {
             clientSocket.Close();
             thread.Abort();
         }
         StopAllCoroutines();
     }


 #if UNITY_EDITOR
     /// <summary>
     /// Unity3D 在编辑状态下 要断掉 socket链接 不然Unity3D 会卡死
     /// </summary>
     public void ApplicationStop()
     {
         HearBeatCountStop();//心跳停止 计数


         if (clientSocket != null && clientSocket.Connected)
         {
             clientSocket.Close();
         }
         if (thread != null)
             thread.Abort();


         StopAllCoroutines();
     }


 #endif
     /// <summary>
     /// 发送数据
     /// </summary>
     /// <param name="str">数据 字符串</param>
     private void Send(string str)
     {
         byte[] bytes = Encoding.UTF8.GetBytes(str);


         if (clientSocket == null)
             return;
         if (!clientSocket.Connected)
             return;
         if (clientSocket.Poll(0, SelectMode.SelectWrite))
         {
             try
             {
                 clientSocket.Send(bytes);
             }
             catch (Exception e)
             {
                 Debug.LogError("发送错误:" + e.Message);
             }
         }
     }


     /// <summary>
     /// 接受服务器返回的数据
     /// </summary>
     private void ReceiveSocket()
     {
         //在这个线程中接受服务器返回的数据
         while (true)
         {
             if (!clientSocket.Connected)
             {
                 //与服务器断开连接跳出循环
                 Debug.Log("Failed to clientSocket server.");
                 clientSocket.Close();
                 break;
             }
             try
             {
                 //接受数据保存至bytes当中
                 byte[] bytes = new byte[4096 * 10];
                 //Receive方法中会一直等待服务端回发消息
                 //如果没有回发会一直在这里等着。


                 int receiveLength = clientSocket.Receive(bytes, SocketFlags.None);


                 if (receiveLength <= 0)
                 {
                     clientSocket.Close();
                     break;
                 }


                 /*
                  * 这里条件可根据你的情况来判断。
                  * 因为我目前的项目先要监测包头长度,
                  * 我的包头长度是4,所以我这里有一个判断
                  */
                 byte[] receiveByte = new byte[receiveLength];
                 Buffer.BlockCopy(bytes, 0, receiveByte, 0, receiveLength);


                 if (receiveByte.Length > 4)
                 {
                     OnReceive(receiveByte);
                 }
                 else
                 {
                     Debug.Log("长度不大于2");
                 }
             }
             catch (Exception e)
             {
                 Debug.Log(e.Message);


             }
         }
     }


     /// <summary>
     /// 重新连接服务器
     /// </summary>
     /// <returns></returns>
     IEnumerator ReConnect()
     {
         yield return new WaitForSeconds(30f);
         if (Application.isPlaying)
         {
             isReconnect = true;
         }
     }


     /// <summary>
     /// 解析Socket得到的字符串
     /// </summary>
     /// <param name="str"></param>
     void DeserializeSocketInfo(string str)
     {


         Loom.RunAsync(() =>
         {
             Loom.QueueOnMainThread(() =>
             {




             });
         });
     }
     /// <summary>
     /// 将byte[] 直接转换成UTF-8类型的string
     /// </summary>
     /// <param name="buffer"></param>
     /// <returns></returns>
     private string GetUTF_8String(byte[] buffer)
     {
         string str = Encoding.GetEncoding("UTF-8").GetString(buffer);


         return str;
     }




     #region 粘包逻辑处理


     int headSize = 4;//包头长度 固定4
     byte[] surplusBuffer = null;//不完整的数据包,即用户自定义缓冲区
     /// <summary>
     /// 接收客户端发来的数据
     /// </summary>
     /// <param name="connId">每个客户的会话ID</param>
     /// <param name="bytes">缓冲区数据</param>
     /// <returns></returns>
     private void OnReceive(byte[] bytes)
     {


         //bytes 为系统缓冲区数据
         //bytesRead为系统缓冲区长度
         int bytesRead = bytes.Length;
         if (bytesRead > 0)
         {
             if (surplusBuffer == null)//判断是不是第一次接收,为空说是第一次
                 surplusBuffer = bytes;//把系统缓冲区数据放在自定义缓冲区里面
             else
             {
                 byte[] tempBytes = new byte[surplusBuffer.Length + bytes.Length];
                 Buffer.BlockCopy(surplusBuffer, 0, tempBytes, 0, surplusBuffer.Length);
                 Buffer.BlockCopy(bytes, 0, tempBytes, surplusBuffer.Length, bytes.Length);
                 surplusBuffer = tempBytes;
             }


             //已经完成读取每个数据包长度
             int haveRead = 0;
             //这里totalLen的长度有可能大于缓冲区大小的(因为 这里的surplusBuffer 是系统缓冲区+不完整的数据包)
             int totalLen = surplusBuffer.Length;
             while (haveRead <= totalLen)
             {
                 //如果在N此拆解后剩余的数据包连一个包头的长度都不够
                 //说明是上次读取N个完整数据包后,剩下的最后一个非完整的数据包
                 if (totalLen - haveRead < headSize)
                 {
                     byte[] byteSub = new byte[totalLen - haveRead];
                     //把剩下不够一个完整的数据包存起来
                     Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
                     surplusBuffer = byteSub;
                     totalLen = 0;
                     break;
                 }
                 //如果够了一个完整包,则读取包头的数据
                 byte[] headByte = new byte[headSize];
                 Buffer.BlockCopy(surplusBuffer, haveRead, headByte, 0, headSize);//从缓冲区里读取包头的字节
                                                                                  //string bodySizeStr = GetUTF_8String(headByte);
                                                                                  //int bodySize = int.Parse(bodySizeStr);
                                                                                  //Debug.Log(bodySize);
                 int bodySize = BitConverter.ToInt32(headByte, 0);//从包头里面分析出包体的长度


                 //这里的 haveRead=等于N个数据包的长度 从0开始;0,1,2,3....N
                 //如果自定义缓冲区拆解N个包后的长度 大于 总长度,说最后一段数据不够一个完整的包了,拆出来保存
                 if (haveRead + headSize + bodySize > totalLen)
                 {
                     byte[] byteSub = new byte[totalLen - haveRead];
                     Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
                     surplusBuffer = byteSub;
                     break;
                 }
                 else
                 {
                     //挨个分解每个包,解析成实际文字
                     string strc = Encoding.UTF8.GetString(surplusBuffer, haveRead + headSize, bodySize);


                     DeserializeSocketInfo(strc);//解析数据


                     //依次累加当前的数据包的长度
                     haveRead = haveRead + headSize + bodySize;


                     if (headSize + bodySize == bytesRead)//如果当前接收的数据包长度正好等于缓冲区长度,则待拼接的不规则数据长度归0
                     {
                         surplusBuffer = null;//设置空 回到原始状态
                         totalLen = 0;//清0
                     }
                 }
             }
         }
     }
     #endregion


     #endregion Socket 代码区域

  }
//---------------Loom脚本----------------//
using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System;
 using System.Threading;
 using System.Linq; 


 public class Loom : MonoBehaviour 
 {


     public static int maxThreads = 8;
     static int numThreads;


     private static Loom _current;
     private int _count;
     public static Loom Current
     {
         get
         {
             Initialize();
             return _current;
         }
     }


     void Awake()
     {
         _current = this;
         initialized = true;
     }


     static bool initialized;


     static void Initialize()
     {
         if (!initialized)
         {


             if (!Application.isPlaying)
                 return;
             initialized = true;
             var g = new GameObject("Loom");
             _current = g.AddComponent<Loom>();
         }


     }


     private List<System.Action> _actions = new List<System.Action>();
     public struct DelayedQueueItem
     {
         public float time;
         public System.Action action;
     }
     private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();


     List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();


     public static void QueueOnMainThread(System.Action action)
     {
         QueueOnMainThread(action, 0f);
     }
     public static void QueueOnMainThread(System.Action action, float time)
     {
         if (time != 0)
         {
             lock (Current._delayed)
             {
                 Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
             }
         }
         else
         {
             lock (Current._actions)
             {
                 Current._actions.Add(action);
             }
         }
     }


     public static Thread RunAsync(System.Action a)
     {
         Initialize();
         while (numThreads >= maxThreads)
         {
             Thread.Sleep(1);
         }
         Interlocked.Increment(ref numThreads);
         ThreadPool.QueueUserWorkItem(RunAction, a);
         return null;
     }


     private static void RunAction(object action)
     {
         try
         {
             ((System.Action)action)();
         }
         catch
         {
         }
         finally
         {
             Interlocked.Decrement(ref numThreads);
         }


     }




     void OnDisable()
     {
         if (_current == this)
         {


             _current = null;
         }
     }






     // Use this for initialization  
     void Start()
     {


     }


     List<System.Action> _currentActions = new List<System.Action>();


     // Update is called once per frame  
     void Update()
     {
         lock (_actions)
         {
             _currentActions.Clear();
             _currentActions.AddRange(_actions);
             _actions.Clear();
         }
         foreach (var a in _currentActions)
         {
             a();
         }
         lock (_delayed)
         {
             _currentDelayed.Clear();
             _currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
             foreach (var item in _currentDelayed)
                 _delayed.Remove(item);
         }
         foreach (var delayed in _currentDelayed)
         {
             delayed.action();
         }






     }  
 }


 /*
  * 调用案例
  */ 


 //public class LoomUse
 //{
 //    void ScaleMesh(Mesh mesh, float scale)
 //    {
 //       
 //        var vertices = mesh.vertices;
 //        //Run the action on a new thread  
 //        Loom.RunAsync(() =>
 //        {
 //            //Loop through the vertices  
 //            for (var i = 0; i < vertices.Length; i++)
 //            {
 //                //Scale the vertex  
 //                vertices[i] = vertices[i] * scale;
 //            }
 //            //Run some code on the main thread  
 //            //to update the mesh  
 //            Loom.QueueOnMainThread(() =>
 //            {
 //                //Set the vertices  
 //                mesh.vertices = vertices;
 //                //Recalculate the bounds  
 //                mesh.RecalculateBounds();
 //            });


 //        });
 //    }  
 //}