using System.Text;

namespace ThingsGateway.NewLife.Log
{
    /// <summary>跟踪流。包装一个基础数据流，主要用于重写Read/Write等行为，跟踪程序操作数据流的过程</summary>
    public class TraceStream : Stream
    {
        #region 属性
        /// <summary>基础流</summary>
        public Stream BaseStream { get; set; }

        /// <summary>跟踪的成员</summary>
        public ICollection<String> TraceMembers { get; set; }

        /// <summary>是否小端字节序。x86系列则采用Little-Endian方式存储数据；网络协议都是Big-Endian；</summary>
        /// <remarks>
        /// 网络协议都是Big-Endian；
        /// Java编译的都是Big-Endian；
        /// Motorola的PowerPC是Big-Endian；
        /// x86系列则采用Little-Endian方式存储数据；
        /// ARM同时支持 big和little，实际应用中通常使用Little-Endian。
        /// </remarks>
        public Boolean IsLittleEndian { get; set; }

        private static readonly String[] DefaultTraceMembers = new String[] { "Write", "WriteByte", "Read", "ReadByte", "BeginRead", "BeginWrite", "EndRead", "EndWrite", "Seek", "Close", "Flush", "SetLength", "SetPosition" };

        /// <summary>显示位置的步长，位移超过此长度后输出位置。默认16，设为0不输出位置</summary>
        public Int32 ShowPositionStep { get; set; }
        #endregion

        #region 基本读写方法
        /// <summary>写入</summary>
        /// <param name="buffer">缓冲区</param>
        /// <param name="offset">偏移</param>
        /// <param name="count">数量</param>
        public override void Write(Byte[] buffer, Int32 offset, Int32 count)
        {
            RaiseAction("Write", buffer, offset, count);

            BaseStream.Write(buffer, offset, count);
        }

        /// <summary>写入一个字节</summary>
        /// <param name="value">数值</param>
        public override void WriteByte(Byte value)
        {
            RaiseAction("WriteByte", value);

            BaseStream.WriteByte(value);
        }

        /// <summary>读取</summary>
        /// <param name="buffer">缓冲区</param>
        /// <param name="offset">偏移</param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
        {
            var n = BaseStream.Read(buffer, offset, count);

            RaiseAction("Read", buffer, offset, count, n);

            return n;
        }

        /// <summary>读取一个字节</summary>
        /// <returns></returns>
        public override Int32 ReadByte()
        {
            var n = BaseStream.ReadByte();

            RaiseAction("ReadByte", n);

            return n;
        }
        #endregion

        #region 异步读写方法
        /// <summary>异步开始读</summary>
        /// <param name="buffer">缓冲区</param>
        /// <param name="offset">偏移</param>
        /// <param name="count">数量</param>
        /// <param name="callback"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public override IAsyncResult BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback? callback, Object? state)
        {
            RaiseAction("BeginRead", offset, count);

            return BaseStream.BeginRead(buffer, offset, count, callback, state);
        }

        /// <summary>异步开始写</summary>
        /// <param name="buffer">缓冲区</param>
        /// <param name="offset">偏移</param>
        /// <param name="count">数量</param>
        /// <param name="callback"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public override IAsyncResult BeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback? callback, Object? state)
        {
            RaiseAction("BeginWrite", offset, count);

            return BaseStream.BeginWrite(buffer, offset, count, callback, state);
        }

        /// <summary>异步读结束</summary>
        /// <param name="asyncResult"></param>
        /// <returns></returns>
        public override Int32 EndRead(IAsyncResult asyncResult)
        {
            RaiseAction("EndRead");

            return BaseStream.EndRead(asyncResult);
        }

        /// <summary>异步写结束</summary>
        /// <param name="asyncResult"></param>
        public override void EndWrite(IAsyncResult asyncResult)
        {
            RaiseAction("EndWrite");

            BaseStream.EndWrite(asyncResult);
        }
        #endregion

        #region 其它方法
        /// <summary>设置流位置</summary>
        /// <param name="offset">偏移</param>
        /// <param name="origin"></param>
        /// <returns></returns>
        public override Int64 Seek(Int64 offset, SeekOrigin origin)
        {
            RaiseAction("Seek", offset, origin);

            return BaseStream.Seek(offset, origin);
        }

        /// <summary>关闭数据流</summary>
        public override void Close()
        {
            RaiseAction("Close");

            BaseStream.Close();
        }

        /// <summary>刷新缓冲区</summary>
        public override void Flush()
        {
            RaiseAction("Flush");

            BaseStream.Flush();
        }

        /// <summary>设置长度</summary>
        /// <param name="value">数值</param>
        public override void SetLength(Int64 value)
        {
            RaiseAction("SetLength", value);

            BaseStream.SetLength(value);
        }
        #endregion

        #region 属性
        /// <summary>可读</summary>
        public override Boolean CanRead { get { return BaseStream.CanRead; } }

        /// <summary>可搜索</summary>
        public override Boolean CanSeek { get { return BaseStream.CanSeek; } }

        /// <summary>可超时</summary>
        public override Boolean CanTimeout { get { return BaseStream.CanTimeout; } }

        /// <summary>可写</summary>
        public override Boolean CanWrite { get { return BaseStream.CanWrite; } }

        /// <summary>可读</summary>
        public override Int32 ReadTimeout { get { return BaseStream.ReadTimeout; } set { BaseStream.ReadTimeout = value; } }

        /// <summary>读写超时</summary>
        public override Int32 WriteTimeout { get { return base.WriteTimeout; } set { base.WriteTimeout = value; } }

        /// <summary>长度</summary>
        public override Int64 Length { get { return BaseStream.Length; } }

        /// <summary>位置</summary>
        public override Int64 Position
        {
            get { return BaseStream.Position; }
            set
            {
                RaiseAction("SetPosition", value);

                BaseStream.Position = value;
            }
        }
        #endregion

        #region 构造
        /// <summary>实例化跟踪流</summary>
        public TraceStream() : this(null) { }

        /// <summary>实例化跟踪流</summary>
        /// <param name="stream"></param>
        public TraceStream(Stream? stream)
        {
            TraceMembers = new HashSet<String>(DefaultTraceMembers, StringComparer.OrdinalIgnoreCase);
            IsLittleEndian = true;
            ShowPositionStep = 16;
            Encoding = Encoding.UTF8;

            if (stream == null) stream = new MemoryStream();
            BaseStream = stream;
            UseConsole = true;

            if (!UseConsole) OnAction += XTrace_OnAction;
        }
        #endregion

        #region 事件
        /// <summary>操作时触发</summary>
        public event EventHandler<EventArgs<String, Object[]>>? OnAction;

        private Int64 lastPosition = -1;

        private void RaiseAction(String action, params Object[] args)
        {
            if (OnAction != null)
            {
                if (!TraceMembers.Contains(action)) return;

                if (ShowPositionStep > 0)
                {
                    var cp = Position;
                    if (lastPosition < 0)
                    {
                        lastPosition = cp;
                        OnAction(this, new EventArgs<String, Object[]>("BeginPosition", new Object[] { lastPosition }));
                    }

                    if (cp > lastPosition + ShowPositionStep)
                    {
                        lastPosition = cp;
                        OnAction(this, new EventArgs<String, Object[]>("Position", new Object[] { lastPosition }));
                    }
                }

                OnAction(this, new EventArgs<String, Object[]>(action, args));
            }
        }
        #endregion

        #region 控制台
        private Boolean _UseConsole;
        /// <summary>是否使用控制台</summary>
        public Boolean UseConsole
        {
            get { return _UseConsole; }
            set
            {
                if (value && !Runtime.IsConsole) return;
                if (value == _UseConsole) return;

                if (value)
                    OnAction += TraceStream_OnAction;
                else
                    OnAction -= TraceStream_OnAction;

                _UseConsole = value;
            }
        }

        /// <summary>编码</summary>
        public Encoding Encoding { get; set; }

        private void TraceStream_OnAction(Object? sender, EventArgs<String, Object[]> e)
        {
            var color = Console.ForegroundColor;

            // 红色动作
            Console.ForegroundColor = ConsoleColor.Red;
            var act = e.Arg1;
            if (act.Length < 8) act += "\t";
            Console.Write(act);

            // 白色十六进制
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write("\t");

            Byte[]? buffer = null;
            var offset = 0;
            var count = 0;
            if (e.Arg2.Length > 1)
            {
                if (e.Arg2[0] is Byte[]) buffer = (Byte[])e.Arg2[0];
                offset = (Int32)e.Arg2[1];
                count = (Int32)e.Arg2[^1];
            }

            if (e.Arg2.Length == 1)
            {
                var n = Convert.ToInt32(e.Arg2[0]);
                // 大于10才显示十进制
                if (n >= 10)
                    Console.Write("{0:X2} ({0})", n);
                else
                    Console.Write("{0:X2}", n);
            }
            else if (buffer != null)
            {
                if (count == 1)
                {
                    var n = Convert.ToInt32(buffer[0]);
                    // 大于10才显示十进制
                    if (n >= 10)
                        Console.Write("{0:X2} ({0})", n);
                    else
                        Console.Write("{0:X2}", n);
                }
                else
                    Console.Write(BitConverter.ToString(buffer, offset, count <= 50 ? count : 50) + (count <= 50 ? "" : "...（共" + count + "）"));
            }
            // 黄色内容
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write("\t");
            if (e.Arg2.Length == 1)
            {
                if (e.Arg2[0] != null)
                {
                    var tc = Type.GetTypeCode(e.Arg2[0].GetType());
                    if (tc != TypeCode.Object) Console.Write(e.Arg2[0]);
                }
            }
            else if (buffer != null)
            {
                if (count == 1)
                {
                    // 只显示可见字符
                    if (buffer[0] >= '0') Console.Write("{0} ({1})", Convert.ToChar(buffer[0]), Convert.ToInt32(buffer[0]));
                }
                else if (count == 2)
                    Console.Write(BitConverter.ToInt16(Format(buffer), offset));
                else if (count == 4)
                    Console.Write(BitConverter.ToInt32(Format(buffer), offset));
                else if (count < 50)
                    Console.Write(Encoding.GetString(buffer, offset, count));
            }
            Console.ForegroundColor = color;
            Console.WriteLine();
        }

        private Byte[] Format(Byte[] buffer)
        {
            if (buffer == null) throw new ArgumentNullException(nameof(buffer));
            if (buffer.Length <= 0) return buffer;

            if (IsLittleEndian) return buffer;

            // 不要改变原来的数组
            var bts = new Byte[buffer.Length];
            Buffer.BlockCopy(buffer, 0, bts, 0, bts.Length);
            Array.Reverse(bts);

            return bts;
        }
        #endregion

        #region 日志
        private void XTrace_OnAction(Object? sender, EventArgs<String, Object[]> e)
        {
            var sb = new StringBuilder();

            // 红色动作
            var act = e.Arg1;
            if (act.Length < 8) act += "\t";
            sb.AppendFormat(act);

            // 白色十六进制
            sb.AppendFormat("\t");

            Byte[]? buffer = null;
            var offset = 0;
            var count = 0;
            if (e.Arg2.Length > 1)
            {
                if (e.Arg2[0] is Byte[]) buffer = (Byte[])e.Arg2[0];
                offset = (Int32)e.Arg2[1];
                count = (Int32)e.Arg2[^1];
            }

            if (e.Arg2.Length == 1)
            {
                var n = Convert.ToInt32(e.Arg2[0]);
                // 大于10才显示十进制
                if (n >= 10)
                    sb.AppendFormat("{0:X2} ({0})", n);
                else
                    sb.AppendFormat("{0:X2}", n);
            }
            else if (buffer != null)
            {
                if (count == 1)
                {
                    var n = Convert.ToInt32(buffer[0]);
                    // 大于10才显示十进制
                    if (n >= 10)
                        sb.AppendFormat("{0:X2} ({0})", n);
                    else
                        sb.AppendFormat("{0:X2}", n);
                }
                else
                    sb.AppendFormat(BitConverter.ToString(buffer, offset, count <= 50 ? count : 50) + (count <= 50 ? "" : "...（共" + count + "）"));
            }

            // 黄色内容
            sb.AppendFormat("\t");
            if (e.Arg2.Length == 1)
            {
                if (e.Arg2[0] != null)
                {
                    var tc = Type.GetTypeCode(e.Arg2[0].GetType());
                    if (tc != TypeCode.Object) sb.AppendFormat(e.Arg2[0] + "");
                }
            }
            else if (buffer != null)
            {
                if (count == 1)
                {
                    // 只显示可见字符
                    if (buffer[0] >= '0') sb.AppendFormat("{0} ({1})", Convert.ToChar(buffer[0]), Convert.ToInt32(buffer[0]));
                }
                else if (count == 2)
                    sb.AppendFormat(BitConverter.ToInt16(Format(buffer), offset) + "");
                else if (count == 4)
                    sb.AppendFormat(BitConverter.ToInt32(Format(buffer), offset) + "");
                else if (count < 50)
                    sb.AppendFormat(Encoding.GetString(buffer, offset, count));
            }

            XTrace.WriteLine(sb.ToString());
        }
        #endregion
    }
}