using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Threading;

namespace MesWork
{
    public partial class MesWorkForm
    {
        private class CurveCollectorState
        {
            public string OpName;
            public long CurveId;
            public string ProductNo;
            public Thread WorkerThread;
            public volatile bool IsRunning;
            public bool IsSegmentEnded;
            public bool IsPointValuesSaved;
            public int SeqNo;
            public readonly object BufferLock = new object();
            public readonly List<CurveSamplePoint> Buffer = new List<CurveSamplePoint>();
            public decimal? StableWeight;
            public int? StableStartSeq;
            public int? StableEndSeq;
            public DateTime LastCalibrationErrorLogTime;
        }

        private static readonly ConcurrentDictionary<string, CurveCollectorState> _curveCollectors
            = new ConcurrentDictionary<string, CurveCollectorState>(StringComparer.OrdinalIgnoreCase);

        private static readonly int CurveCollectorSampleIntervalMs = int.TryParse(
            ConfigurationManager.AppSettings["CurveCollectorSampleIntervalMs"], out int _sampleIntervalMs)
            ? _sampleIntervalMs : 200;

        private static readonly int CurveStableWindowSize = int.TryParse(
            ConfigurationManager.AppSettings["CurveStableWindowSize"], out int _stableWindowSize)
            ? _stableWindowSize : 20;

        private static readonly int CurveStableSkipStartPointCount = int.TryParse(
            ConfigurationManager.AppSettings["CurveStableSkipStartPointCount"], out int _skipStartPointCount)
            ? _skipStartPointCount : 0;

        /// <summary>
        /// 允许工作后启动曲线采集，每个工位同一时间只保留一个采集段。
        /// </summary>
        private void CurveCollector_Start(string opName, string productNo, string modelNo)
        {
            try
            {
                if (_curveCollectors.TryGetValue(opName, out CurveCollectorState oldState) && !oldState.IsSegmentEnded)
                {
                    if (oldState.IsRunning) return;
                    CurveCollector_EndSegment(opName, 0); // 清理上一次未正常结束的曲线
                }

                long curveId = B_DB_Opera.CurveRecord_Start(opName, productNo, modelNo, CurveCollectorSampleIntervalMs); // 先建曲线主记录
                if (curveId <= 0)
                {
                    WeighingPage?.AppendLog($"[{opName}] 曲线记录创建失败，未启动采集");
                    return;
                }

                var state = new CurveCollectorState
                {
                    OpName = opName,
                    CurveId = curveId,
                    ProductNo = productNo,
                    IsRunning = true
                };
                state.WorkerThread = new Thread(() => CurveCollector_Work(state))
                {
                    IsBackground = true,
                    Name = $"CurveCollector_{opName}"
                };

                _curveCollectors[opName] = state;
                state.WorkerThread.Start();
                WeighingPage?.AppendLog($"[{opName}] 实时重量曲线采集启动，记录ID={curveId}");
            }
            catch (Exception err)
            {
                WeighingPage?.AppendLog($"[{opName}] 曲线采集启动异常：{err.Message}");
            }
        }

        /// <summary>
        /// 采集线程：200ms读取一次实时重量，先按工具标定公式计算，再缓存到内存，保存时一次性写库。
        /// </summary>
        private void CurveCollector_Work(CurveCollectorState state)
        {
            while (state.IsRunning)
            {
                try
                {
                    decimal rawWeight = Convert.ToDecimal(ReadPLC(100220, state.OpName)); // 实时重量点位
                    if (!WeightCalibrationHelper.Apply(state.OpName, rawWeight, out decimal weight, out _, out string msg))
                    {
                        if ((DateTime.Now - state.LastCalibrationErrorLogTime).TotalSeconds >= 5)
                        {
                            state.LastCalibrationErrorLogTime = DateTime.Now;
                            WeighingPage?.AppendLog($"[{state.OpName}] 曲线采样标定失败：{msg}");
                        }
                        Thread.Sleep(CurveCollectorSampleIntervalMs);
                        continue;
                    }
                    lock (state.BufferLock)
                    {
                        state.Buffer.Add(new CurveSamplePoint
                        {
                            SeqNo = ++state.SeqNo, // 序号用于追溯最终取点区间
                            SampleTime = DateTime.Now,
                            Weight = weight
                        });
                    }
                }
                catch (Exception err)
                {
                    WeighingPage?.AppendLog($"[{state.OpName}] 曲线采样异常：{err.Message}");
                }
                Thread.Sleep(CurveCollectorSampleIntervalMs);
            }
        }

        /// <summary>
        /// 停止采样线程，但暂不关闭曲线记录，便于保存流程先计算最终重量。
        /// </summary>
        private void CurveCollector_StopSampling(string opName, bool waitingSave = false)
        {
            if (!_curveCollectors.TryGetValue(opName, out CurveCollectorState state)) return;

            state.IsRunning = false;
            if (state.WorkerThread != null && state.WorkerThread.IsAlive)
            {
                state.WorkerThread.Join(2000);
            }
            if (waitingSave)
            {
                CurveCollector_SavePointValues(state, 0, null); // 保存前先把点位字符串落库，供算法读取
            }
        }

        /// <summary>
        /// 保存完成后关闭曲线记录，并关联称重记录ID。
        /// </summary>
        private void CurveCollector_EndSegment(string opName, long weighingRecordId)
        {
            if (!_curveCollectors.TryGetValue(opName, out CurveCollectorState state)) return;

            CurveCollector_StopSampling(opName);
            if (!state.IsSegmentEnded)
            {
                CurveCollector_SavePointValues(state, weighingRecordId, state.StableWeight, state.StableStartSeq, state.StableEndSeq); // 补写称重记录ID和算法依据
                state.IsSegmentEnded = true;
                WeighingPage?.AppendLog($"[{opName}] 实时重量曲线采集结束，记录ID={state.CurveId}，点数={state.SeqNo}");
            }
            _curveCollectors.TryRemove(opName, out _);
        }

        /// <summary>
        /// 优先用已标定曲线点计算稳定重量；失败时调用方继续使用PLC锁定重量。
        /// </summary>
        private bool CurveCollector_TryGetStableWeight(string opName, out decimal weight, out int pointCount, out string msg)
        {
            weight = 0;
            pointCount = 0;
            msg = "未启动曲线采集";

            if (!_curveCollectors.TryGetValue(opName, out CurveCollectorState state))
            {
                return false;
            }

            CurveCollector_SavePointValues(state, 0, null); // 确保数据库已有当前点位字符串
            if (!B_DB_Opera.CurveRecord_QueryLatest(opName, state.ProductNo, out string pointValues, out _))
            {
                msg = "未查询到当前产品曲线记录";
                return false;
            }

            List<decimal> weights = CurveCollector_ParsePointValues(pointValues); // 从数据库点位字符串还原曲线
            pointCount = weights.Count;
            if (CurveCollector_CalculateStableWeight(weights, out weight, out int startSeq, out int endSeq))
            {
                state.StableWeight = weight;
                state.StableStartSeq = startSeq; // 记录最终采用窗口起点
                state.StableEndSeq = endSeq; // 记录最终采用窗口终点
                msg = "";
                return true;
            }

            msg = $"曲线点数不足或无法计算，当前点数={pointCount}";
            return false;
        }

        private List<CurveSamplePoint> CurveCollector_GetPoints(CurveCollectorState state)
        {
            lock (state.BufferLock)
            {
                return state.Buffer.OrderBy(p => p.SeqNo).ToList();
            }
        }

        private string CurveCollector_BuildPointValues(List<CurveSamplePoint> points)
        {
            return string.Join("|", points.Select(p => p.Weight.ToString("F3", CultureInfo.InvariantCulture)));
        }

        private void CurveCollector_SavePointValues(CurveCollectorState state, long weighingRecordId, decimal? finalWeight)
        {
            CurveCollector_SavePointValues(state, weighingRecordId, finalWeight, null, null);
        }

        private void CurveCollector_SavePointValues(CurveCollectorState state, long weighingRecordId,
            decimal? finalWeight, int? finalStartSeq, int? finalEndSeq)
        {
            if (state.IsPointValuesSaved && weighingRecordId <= 0 && !finalWeight.HasValue) return;

            List<CurveSamplePoint> points = CurveCollector_GetPoints(state);
            string pointValues = CurveCollector_BuildPointValues(points); // 例：50.120|50.115|50.118
            B_DB_Opera.CurveRecord_Save(state.CurveId, weighingRecordId, points.Count, pointValues, finalWeight, finalStartSeq, finalEndSeq);
            state.IsPointValuesSaved = true;
        }

        private List<decimal> CurveCollector_ParsePointValues(string pointValues)
        {
            var result = new List<decimal>();
            if (string.IsNullOrWhiteSpace(pointValues)) return result;

            foreach (string item in pointValues.Split('|'))
            {
                if (decimal.TryParse(item, NumberStyles.Number, CultureInfo.InvariantCulture, out decimal value))
                {
                    result.Add(value);
                }
            }
            return result;
        }

        /// <summary>
        /// 跳过前置配置点数后，找连续稳定窗口中极差最小的一段，去掉一个最大值和一个最小值后取平均。
        /// </summary>
        private bool CurveCollector_CalculateStableWeight(List<decimal> weights, out decimal stableWeight, out int startSeq, out int endSeq)
        {
            stableWeight = 0;
            startSeq = 0;
            endSeq = 0;
            int skipStartPointCount = Math.Max(0, CurveStableSkipStartPointCount);
            if (weights == null || weights.Count - skipStartPointCount < CurveStableWindowSize) return false;

            int bestStart = 0; // 0-based窗口起点，保存时转成1-based序号
            decimal bestRange = decimal.MaxValue;
            for (int i = skipStartPointCount; i <= weights.Count - CurveStableWindowSize; i++)
            {
                decimal min = weights[i];
                decimal max = weights[i];
                for (int j = i + 1; j < i + CurveStableWindowSize; j++)
                {
                    if (weights[j] < min) min = weights[j];
                    if (weights[j] > max) max = weights[j];
                }

                decimal range = max - min;
                if (range <= bestRange)
                {
                    bestRange = range;
                    bestStart = i; // 极差相同时取靠后窗口，更接近保存时刻
                }
            }

            List<decimal> window = weights.Skip(bestStart).Take(CurveStableWindowSize).ToList();
            decimal windowMin = window.Min();
            decimal windowMax = window.Max();
            bool removedMin = false;
            bool removedMax = false;
            decimal sum = 0;
            int count = 0;
            foreach (decimal value in window)
            {
                if (!removedMin && value == windowMin)
                {
                    removedMin = true;
                    continue;
                }
                if (!removedMax && value == windowMax)
                {
                    removedMax = true; // 只剔除一个最大值，避免过度过滤
                    continue;
                }
                sum += value;
                count++;
            }

            if (count <= 0) return false;
            stableWeight = Math.Round(sum / count, 3, MidpointRounding.AwayFromZero); // 剩余18点平均值
            startSeq = bestStart + 1; // 转为数据库可读的1-based序号
            endSeq = bestStart + CurveStableWindowSize;
            return true;
        }
    }
}
