using System.Buffers.Binary;
using System.Text;

using ThingsGateway.NewLife.Buffers;
using ThingsGateway.NewLife.Data;

namespace ThingsGateway.NewLife.Http;

/// <summary>WebSocket消息类型</summary>
public enum WebSocketMessageType
{
    /// <summary>附加数据</summary>
    Data = 0,

    /// <summary>文本数据</summary>
    Text = 1,

    /// <summary>二进制数据</summary>
    Binary = 2,

    /// <summary>连接关闭</summary>
    Close = 8,

    /// <summary>心跳</summary>
    Ping = 9,

    /// <summary>心跳响应</summary>
    Pong = 10,
}

/// <summary>WebSocket消息</summary>
public class WebSocketMessage : IDisposable
{
    #region 属性
    /// <summary>消息是否结束</summary>
    public Boolean Fin { get; set; }

    /// <summary>消息类型</summary>
    public WebSocketMessageType Type { get; set; }

    /// <summary>加密数据的掩码</summary>
    public Byte[]? MaskKey { get; set; }

    /// <summary>负载数据</summary>
    public IPacket? Payload { get; set; }

    /// <summary>关闭状态。仅用于Close消息</summary>
    public Int32 CloseStatus { get; set; }

    /// <summary>关闭状态描述。仅用于Close消息</summary>
    public String? StatusDescription { get; set; }
    #endregion

    #region 构造
    /// <summary>销毁。回收数据包到内存池</summary>
    public void Dispose() => Payload.TryDispose();
    #endregion

    #region 方法
    /// <summary>读取消息</summary>
    /// <param name="pk"></param>
    /// <returns></returns>
    public Boolean Read(IPacket pk)
    {
        if (pk.Length < 2) return false;

        var reader = new SpanReader(pk) { IsLittleEndian = false };
        var b = reader.ReadByte();

        Type = (WebSocketMessageType)(b & 0x7F);

        // 仅处理一个包
        Fin = (b & 0x80) == 0x80;
        if (!Fin) return false;

        var b2 = reader.ReadByte();

        var mask = (b2 & 0x80) == 0x80;

        /*
         * 数据长度
         * len < 126    单字节表示长度
         * len = 126    后续2字节表示长度，大端
         * len = 127    后续8字节表示长度
         */
        var len = (Int64)(b2 & 0x7F);
        if (len == 126)
            len = reader.ReadUInt16();
        else if (len == 127)
            len = reader.ReadInt64();

        // 如果mask，剩下的就是数据，避免拷贝，提升性能
        if (!mask)
        {
            Payload = reader.ReadPacket((Int32)len);
        }
        else
        {
            var masks = new Byte[4];
            if (mask) reader.Read(masks);
            MaskKey = masks;

            if (mask)
            {
                // 直接在数据缓冲区修改，避免拷贝
                Payload = reader.ReadPacket((Int32)len);
                var data = Payload.GetSpan();
                for (var i = 0; i < len; i++)
                {
                    data[i] = (Byte)(data[i] ^ masks[i % 4]);
                }
            }
        }

        // 特殊处理关闭消息
        if (Type == WebSocketMessageType.Close && Payload != null)
        {
            var data = Payload.GetSpan();
            CloseStatus = BinaryPrimitives.ReadUInt16BigEndian(data[..2]);
            StatusDescription = data[2..].ToStr();
        }

        return true;
    }

    /// <summary>把消息转为封包</summary>
    /// <returns></returns>
    public virtual IPacket ToPacket()
    {
        var body = Payload;
        var len = body == null ? 0 : body.Total;
        var masks = MaskKey;

        // 特殊处理关闭消息
        if (Type == WebSocketMessageType.Close)
        {
            len = 2;
            if (!StatusDescription.IsNullOrEmpty()) len += Encoding.UTF8.GetByteCount(StatusDescription);
        }

        var size = len switch
        {
            < 126 => 1 + 1,
            < 0xFFFF => 1 + 1 + 2,
            _ => 1 + 1 + 8,
        };
        if (masks != null) size += masks.Length;
        if (Type == WebSocketMessageType.Close) size += len;

        var rs = body.ExpandHeader(size);
        var writer = new SpanWriter(rs) { IsLittleEndian = false };

        writer.WriteByte((Byte)(0x80 | (Byte)Type));

        /*
         * 数据长度
         * len < 126    单字节表示长度
         * len = 126    后续2字节表示长度，大端
         * len = 127    后续8字节表示长度
         */

        if (masks == null)
        {
            if (len < 126)
            {
                writer.WriteByte((Byte)len);
            }
            else if (len < 0xFFFF)
            {
                writer.WriteByte(126);
                writer.Write((Int16)len);
            }
            else
            {
                writer.WriteByte(127);
                writer.Write((Int64)len);
            }
        }
        else
        {
            if (len < 126)
            {
                writer.WriteByte((Byte)(len | 0x80));
            }
            else if (len < 0xFFFF)
            {
                writer.WriteByte(126 | 0x80);
                writer.Write((Int16)len);
            }
            else
            {
                writer.WriteByte(127 | 0x80);
                writer.Write((Int64)len);
            }

            writer.Write(masks);

            // 掩码混淆数据。直接在数据缓冲区修改，避免拷贝
            if (body != null)
            {
                var data = body.GetSpan();
                for (var i = 0; i < len; i++)
                {
                    data[i] = (Byte)(data[i] ^ masks[i % 4]);
                }
            }
        }

        if (body?.Length > 0)
        {
            // 注意body可能是链式数据包
            //writer.Write(body.GetSpan());

            // 扩展得到的数据包，直接写入了头部，尾部数据不用拷贝也无需切片
            return rs;

            //return rs.Slice(0, writer.Position).Append(body);
        }
        else if (Type == WebSocketMessageType.Close)
        {
            writer.Write((Int16)CloseStatus);
            if (!StatusDescription.IsNullOrEmpty()) writer.Write(StatusDescription, -1);

            rs.Next = null;
        }

        return rs.Slice(0, writer.Position, true);
    }
    #endregion
}