using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Aitex.Core.RT.DataCollection.HighPerformance
{
    internal class DataTraceDataTableCache : IDataTraceCache
    {
        #region Variables

        private readonly object _syncRoot = new();
        private readonly int _maxRowsPerFlush;
        private readonly string _tableName;

        private DataTable _dataTable;
        private readonly Queue<long> _qCachedTimestamps = new();
        private readonly int _minCachePeriodMs;
        private readonly Stopwatch _stopwatch = new();

        private readonly StringBuilder _sqlExpr = new(5000000); // preallocate 5MB
        private readonly StringBuilder _sqlValuesList = new(5000000); // preallocate 5MB

        private int _lastPersistRows = 0;
        private double _lastCachePeriodMs = 0;

        #endregion

        #region Constructors

        /// <summary>
        /// 创建数据记录器缓存对象。
        /// </summary>
        /// <param name="tableName">当前Cache对应的数据库表名称。</param>
        /// <param name="module">当前Cache对应的分组名称。</param>
        /// <param name="dataHolders">数据获取器对象集合，保存当前Cache需要缓存的数据源。</param>
        /// <param name="minCachePeriodMs">最小缓存周期，如果外部频繁调用<see cref="Cache"/>方法请求缓存数据，该周期内的重复请求被忽略，以避免高频缓存导致性能下降。</param>
        /// <param name="maxRowsPerFlush">每次持久化的最大行数，避免一次性对数据库写入太多数据造成性能问题。</param>
        /// <exception cref="ArgumentException"></exception>
        public DataTraceDataTableCache(string tableName, string module, IReadOnlyList<IDataBuffer> dataHolders, int minCachePeriodMs = 50, int maxRowsPerFlush = 2000)
        {
            Debug.Assert(dataHolders is { Count: > 0 }, "Incorrect Data Collector List.");

            Category = module;
            DataBuffers = dataHolders;
            _tableName = tableName;
            _maxRowsPerFlush = maxRowsPerFlush;
            _minCachePeriodMs = minCachePeriodMs;

            // 必须传入有效的数据收集器列表。
            if (dataHolders == null || dataHolders.Count == 0)
                throw new ArgumentException("数据收集器列表不能为空。", nameof(dataHolders));

            // 检查数据收集器列表中是否存在重复的序号。
            if (dataHolders.GroupBy(dc => dc.Index).Any(g => g.Count() > 1))
                throw new ArgumentException("数据收集器列表中存在重复的序号。", nameof(dataHolders));

            // 检查数据收集器列表中是否存在重复的名称。
            if (dataHolders.GroupBy(dc => dc.Name).Any(g => g.Count() > 1))
                throw new ArgumentException("数据收集器列表中存在重复的名称。", nameof(dataHolders));

            // 检查数据收集器列表中是否存在空的获取器。
            if (dataHolders.Any(dc => dc.Read == null))
                throw new ArgumentException("数据收集器列表中存在空的获取器。", nameof(dataHolders));

            _dataTable = CreateDataTable();



            /*DATA.Subscribe($"{ModuleName.System}.DBRC.{Module}.CachedCount", ()=> _qCachedTimestamps.Count, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{ModuleName.System}.DBRC.{Module}.PersistRows", ()=> _lastPersistRows, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{ModuleName.System}.DBRC.{Module}.PersistPeriodMs", ()=> _lastPersistPeriodMs, SubscriptionAttribute.FLAG.IgnoreSaveDB);*/
        }

        #endregion

        #region Properties

        /// <summary>
        /// 返回当前缓存服务的模组名称。
        /// </summary>
        public string Category { get; }

        /// <summary>
        /// 返回数据获取器列表。
        /// </summary>
        public IReadOnlyList<IDataBuffer> DataBuffers { get; }

        #endregion

        #region Methods

        /// <summary>
        /// 创建临时数据表。
        /// </summary>
        /// <returns></returns>
        private DataTable CreateDataTable()
        {
            lock (_syncRoot)
            {
                var dt = new DataTable(_tableName);
                dt.Columns.Add("time", typeof(long));
                foreach (var dh in DataBuffers)
                    dt.Columns.Add(dh.Name, dh.ValueType);
                
                return dt;
            }
        }


        /// <summary>
        /// 立即缓存当前数据。
        /// </summary>
        public void Cache()
        {
            lock (_syncRoot)
            {
                // 限制最大Cache频率
                if (_stopwatch.IsRunning && _stopwatch.ElapsedMilliseconds < _minCachePeriodMs)
                    return;

                //TODO 需要限制Q的最大数据量，并且检测HalfFull和Full事件，并触发警告，可能PC性能出现问题 

                _lastCachePeriodMs = _stopwatch.ElapsedMilliseconds;
                _stopwatch.Restart();
                var ts = DateTime.Now.Ticks;
                var row = _dataTable.NewRow();
                row["time"] = ts;
                var ret = Parallel.ForEach(DataBuffers, dc =>
                {
                    var ret = dc.Read();
                    row[dc.Name] = ret;
                });

                if (ret.IsCompleted)
                {
                    _dataTable.Rows.Add(row);
                }
            }
        }

        #endregion
    }
}
