using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WasteConsoleTest
{
    public class WSocketClientHelp
    {
        private ClientWebSocket ws = null;
        private Uri uri = null;
        private bool isUserClose = false;//是否最后由用户手动关闭
        public static string Secret = "VOVGXa8Rw7hwp4p5";
        public static string SecretHash = "wSQnkR1Eec4U5FPRA5aGDwpmH3+P6YtEK3yHYQ79r8w=";
        public static string deviceid = "08da3552-46ee-41b5-88ee-ea3f0047b57f";

        // <summary>
        /// WebSocket状态
        /// </summary>
        public WebSocketState? State { get => ws?.State; }

        /// <summary>
        /// 包含一个数据的事件
        /// </summary>
        public delegate void MessageEventHandler(object sender, string data);

        public delegate void ErrorEventHandler(object sender, Exception ex);

        /// <summary>
        /// 连接建立时触发
        /// </summary>
        public event EventHandler OnOpen;

        /// <summary>
        /// 客户端接收服务端数据时触发
        /// </summary>
        public event MessageEventHandler OnMessage;

        /// <summary>
        /// 通信发生错误时触发
        /// </summary>
        public event ErrorEventHandler OnError;

        /// <summary>
        /// 连接关闭时触发
        /// </summary>
        public event EventHandler OnClose;

        public WSocketClientHelp(string wsUrl)
        {
            uri = new Uri(wsUrl);
            ws = new ClientWebSocket();
        }

        /// <summary>
        /// 打开链接
        /// </summary>
        public void Open()
        {
            Task.Run(async () =>
            {
                if (ws.State == WebSocketState.Connecting || ws.State == WebSocketState.Open)
                    return;

                string netErr = string.Empty;
                try
                {
                    //初始化链接
                    isUserClose = false;
                    ws = new ClientWebSocket();
                    //  ws.Options.AddSubProtocol("protocol1"); //使用的协议
                    ws.Options.SetRequestHeader("device", deviceid); //设备ID
                    ws.Options.SetRequestHeader("secret", SecretHash); //设备secrethash
                    ws.Options.SetRequestHeader("time", GetTimestamp().ToString()); //时间戳
                    ws.Options.SetRequestHeader("os", "12"); //操作系统，Android ArmV7
                    ws.Options.SetRequestHeader("script", "2"); //设备支持的脚本语言运行环境,1-lua5.4 2-javascript
                    ws.Options.SetRequestHeader("baseProgrameLang", "10"); //宿主程序语言,java
                    ws.Options.SetRequestHeader("dev", "true");//开发环境,bool类型
                    await ws.ConnectAsync(uri, CancellationToken.None); //建立连接

                    if (OnOpen != null)
                        OnOpen(ws, new EventArgs());

                    Send(@"{""protocol"":""json"", ""version"":1}");//建立之后第一步进行握手，使用json类型

                    //全部消息容器
                    List<byte> bs = new List<byte>();
                    //缓冲区
                    var buffer = new byte[1024 * 4];
                    //监听Socket信息
                    WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    //是否关闭
                    while (!result.CloseStatus.HasValue)
                    {
                        //文本消息
                        if (result.MessageType == WebSocketMessageType.Text)
                        {
                            bs.AddRange(buffer.Take(result.Count));

                            //消息是否已接收完全
                            if (result.EndOfMessage)
                            {
                                var arr = RemoveSeparator(bs.ToArray());//过滤掉记录分隔符

                                //发送过来的消息
                                string userMsg = Encoding.UTF8.GetString(arr, 0, arr.Length);

                                if (OnMessage != null)
                                    OnMessage(ws, userMsg);

                                //清空消息容器
                                bs = new List<byte>();
                            }
                        }
                        //继续监听Socket信息
                        result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    }
                    ////关闭WebSocket（服务端发起）
                    //await ws.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
                }
                catch (Exception ex)
                {
                    netErr = "发生错误" + ex.Message;

                    if (OnError != null)
                        OnError(ws, ex);

                    //if (ws != null && ws.State == WebSocketState.Open)
                    //    //关闭WebSocket（客户端发起）
                    //    await ws.CloseAsync(WebSocketCloseStatus.Empty, ex.Message, CancellationToken.None);
                }
                finally
                {
                    if (!isUserClose)
                        Close(ws.CloseStatus.Value, ws.CloseStatusDescription + netErr);
                }
            });
        }

        /// <summary>
        /// 增加记录分隔符
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static byte[] AddSeparator(byte[] data)
        {
            List<byte> t = new List<byte>(data) { 0x1e };//0x1e record separator
            return t.ToArray();
        }

        //删除记录分隔符
        private static byte[] RemoveSeparator(byte[] data)
        {
            List<byte> t = new List<byte>(data);
            t.Remove(0x1e);
            return t.ToArray();
        }

        /// <summary>
        /// 获取时间戳
        /// </summary>
        /// <returns></returns>
        private int GetTimestamp()
        {
            DateTime dateTimeStart = TimeZoneInfo.ConvertTimeToUtc(new DateTime(1970, 1, 1, 8, 0, 0));
            int timestamp = Convert.ToInt32((DateTime.Now - dateTimeStart).TotalSeconds);
            return timestamp;
        }

        /// <summary>
        /// 使用连接发送文本消息
        /// </summary>
        /// <param name="ws"></param>
        /// <param name="mess"></param>
        /// <returns>是否尝试了发送</returns>
        public bool Send(string mess)
        {
            if (ws.State != WebSocketState.Open)
                return false;

            Task.Run(async () =>
            {
                Console.WriteLine($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")},发送数据:{mess}");
                var replyMess = Encoding.UTF8.GetBytes(mess);
                //发送消息,结尾必须携带记录分隔符
                await ws.SendAsync(new ArraySegment<byte>(AddSeparator(replyMess)), WebSocketMessageType.Text, true, CancellationToken.None);
            });

            return true;
        }

        /// <summary>
        /// 使用连接发送字节消息
        /// </summary>
        /// <param name="ws"></param>
        /// <param name="mess"></param>
        /// <returns>是否尝试了发送</returns>
        public bool Send(byte[] bytes)
        {
            if (ws.State != WebSocketState.Open)
                return false;

            Task.Run(async () =>
            {
                //发送消息
                await ws.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true, CancellationToken.None);
            });

            return true;
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            isUserClose = true;
            Close(WebSocketCloseStatus.NormalClosure, "用户手动关闭");
        }

        public void Close(WebSocketCloseStatus closeStatus, string statusDescription)
        {
            Task.Run(async () =>
            {
                try
                {
                    //关闭WebSocket（客户端发起）
                    await ws.CloseAsync(closeStatus, statusDescription, CancellationToken.None);
                }
                catch (Exception ex)
                {
                }

                ws.Abort();
                ws.Dispose();

                if (OnClose != null)
                    OnClose(ws, new EventArgs());
            });
        }
    }
}