using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using VIZ.Framework.Domain;

namespace VIZ.Framework.Connection
{
    /// <summary>
    /// 固定长度Buffer包解析器
    /// </summary>
    [ConnPackageProvider(ConnPackageProviderType.TCP | ConnPackageProviderType.UDP | ConnPackageProviderType.SerialPort)]
    public class ConnFixedBufferPackageProvider : IConnPackageProvider
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(ConnFixedBufferPackageProvider));

        /// <summary>
        /// 固定长度Buffer包解析器
        /// </summary>
        /// <param name="fixedBufferSize">固定Buffer长度</param>
        /// <param name="syncHeader">同步头</param>
        public ConnFixedBufferPackageProvider(int fixedBufferSize, IEnumerable<byte> syncHeader)
        {
            this.FixedBufferSize = fixedBufferSize;
            this.buffer_index = 0;
            this.buffer_cache = new byte[fixedBufferSize];

            if (syncHeader != null)
            {
                this.SyncHeader.AddRange(SyncHeader);
            }
        }

        /// <summary>
        /// 固定Buffer长度
        /// </summary>
        public int FixedBufferSize { get; private set; }

        /// <summary>
        /// 同步头
        /// </summary>
        public List<byte> SyncHeader { get; private set; } = new List<byte>();

        /// <summary>
        /// 数据缓存
        /// </summary>
        private byte[] buffer_cache;

        /// <summary>
        /// 数据缓存索引
        /// </summary>
        private int buffer_index;

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="package">数据包</param>
        public void Execute(ConnPackageInfo package)
        {
            int package_index = 0;

            while (package_index < package.DataSize)
            {
                int copy = Math.Min(buffer_cache.Length - buffer_index, package.DataSize - package_index);
                Array.Copy(package.Data, package_index, this.buffer_cache, buffer_index, copy);
                this.buffer_index += copy;
                package_index += copy;

                if (this.buffer_index < this.FixedBufferSize)
                    continue;

                // 同步头
                int sync_index = this.getSyncIndex();
                if (sync_index < 0)
                {
                    continue;
                }
                if (sync_index > 0)
                {
                    byte[] buffer_cache_new = new byte[this.FixedBufferSize];
                    Array.Copy(this.buffer_cache, sync_index, buffer_cache_new, 0, this.FixedBufferSize - sync_index);
                    this.buffer_index -= sync_index;

                    continue;
                }

                // 处理器处理消息
                ConnFixedBufferInfo info = new ConnFixedBufferInfo();
                info.LocalIP = package.LocalIP;
                info.LocalPort = package.LocalPort;
                info.RemoteIP = package.RemoteIP;
                info.RemotePort = package.RemotePort;
                info.Buffer = this.buffer_cache;

                try
                {
                    this.Execute(info);
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                }

                // 发送消息
                ConnFixedBufferMessage msg = new ConnFixedBufferMessage();
                msg.LocalIP = package.LocalIP;
                msg.LocalPort = package.LocalPort;
                msg.RemoteIP = package.RemoteIP;
                msg.RemotePort = package.RemotePort;
                msg.PortName = package.PortName;
                msg.Provider = this;
                msg.Buffer = this.buffer_cache;

                try
                {
                    ApplicationDomain.MessageManager.Send(msg);
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                }

                // 清理缓存
                this.buffer_cache = new byte[this.FixedBufferSize];
                this.buffer_index = 0;
            }
        }

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="info">信息</param>
        protected virtual void Execute(ConnFixedBufferInfo info)
        {
            // noting to do.
        }

        /// <summary>
        /// 获取当前同步帧位序
        /// </summary>
        /// <returns>获取当前同步帧位序</returns>
        private int getSyncIndex()
        {
            if (this.SyncHeader.Count == 0)
                return 0;

            for (int i = 0; i < this.FixedBufferSize - this.SyncHeader.Count; ++i)
            {
                if (this.isSameSyncBuffer(i))
                    return i;
            }

            // 未找到同步数据
            return -1;
        }

        /// <summary>
        /// 是否是相同的同步帧buffer
        /// </summary>
        /// <param name="index">开始位序</param>
        /// <returns>是否是相同的同步帧buffer</returns>
        private bool isSameSyncBuffer(int index)
        {
            for (int i = 0; i < this.SyncHeader.Count; ++i)
            {
                if (this.buffer_cache[index + i] != this.SyncHeader[i])
                    return false;
            }

            return true;
        }
    }
}