using System;
using System.Diagnostics;
using System.Xml;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Communications;

namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps;

public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSensor
{
    #region Variables
    
    protected readonly bool IsSimMode;
    protected bool IsEnableLog;
    protected TempBasFunction TempBasFunction;

    private readonly Random _rndTempGen = new();
    private readonly R_TRIG _rTrigReadTempFailed = new();
    private PeriodicJob _tReadTemp;
    //private Misc.FilterTypes _filterType;
    //private TempDataFilter[] _tempFilters;

    private readonly Stopwatch _swSimSineWave = new ();
    private const double SINE_T = 5.0;
    private const double SINE_F = 2.0 * Math.PI * (1 / SINE_T);

    #endregion
    
    #region Ctor

    protected TempSensorBase(string module, XmlElement node, string ioModule = "")
        : base(module, node, ioModule)
    {
        IsSimMode = SC.SafeGetValue("System.IsSimulatorMode", false);
        
        RTrigs.Add(_rTrigReadTempFailed);
        
        var maxChStr = node.GetAttribute("MaxChannels");
        if (!string.IsNullOrEmpty(maxChStr) && int.TryParse(maxChStr, out var maxCh))
            MaxChannels = maxCh;
        else
            MaxChannels = 4;
        
        var minTempStr = node.GetAttribute("MinimalTemp");
        if (!string.IsNullOrEmpty(minTempStr) && double.TryParse(minTempStr, out var minTemp))
            MinimalTemp = minTemp;
        else
            MinimalTemp = 600.0;

        if(IsSimMode)
            _swSimSineWave.Start();
    }

    #endregion
    
    #region Properties

    public virtual string Address { get; protected set; }
    
    public virtual bool IsConnected { get; }
    
    public double MinimalTemp { get; }
    
    public int MaxChannels { get; }
    
    public double[] Temp { get; private set; }

    #endregion

    #region Methods

    /// <summary>
    /// 生成随机温度。
    /// </summary>
    /// <returns></returns>
    private double[] RandomTemps(double baseTemp = 800, double peakPeak = 200, double jitter = 50.0)
    {
        var rndTemps = new double[MaxChannels];
        for (var i = 0; i < MaxChannels; i++)
        {
            var t = _swSimSineWave.Elapsed.TotalSeconds; // current moment
            var tempSine = peakPeak * Math.Sin(SINE_F * t + (i * Math.PI / 3)); //temperature following sine wave with 60 deg(π/3 rad) phase-diff per channel
            tempSine += baseTemp + tempSine + _rndTempGen.NextDouble() * jitter; // add jitter
            rndTemps[i] = tempSine;
        }

        return rndTemps;
    }

    private bool DoTempReadThread()
    {
        // get temp. points according to the "IsSimulatorMode" system config.
        var temps = IsSimMode ? RandomTemps() : HandleReadTemp();
        
        _rTrigReadTempFailed.CLK = temps == null || temps.Length != MaxChannels;
        if (_rTrigReadTempFailed.Q)
        {
            LOG.Error(temps != null
                ? $"{this} {MaxChannels} temperature data wanted but {temps.Length} points read."
                : $"{this} None temperature data read from controller.");
        }

        // Too less temp. points read from the sensor.
        if (_rTrigReadTempFailed.M)
            return true;

        // Get the right temp value from the filter
        for (var i = 0; i < MaxChannels; i++)
        {
            Temp[i] = temps[i];

            //_tempFilters[i].AddRawTemp(temps![i]);

            //Temp[i] = _filterType switch
            //{
            //    Misc.FilterTypes.None => _tempFilters[i].Raw,
            //    Misc.FilterTypes.MAF => _tempFilters[i].FilteredMAF,
            //    _ => _tempFilters[i].Raw
            //};
        }

        return true;
    }
    
    private void InitTempDataFilter()
    {
        //_tempFilters = new TempDataFilter[MaxChannels];
        Temp = new double[MaxChannels];
        
        for (var i = 0; i < MaxChannels; i++)
        {
            //_tempFilters[i] = new TempDataFilter(this, (i + 1).ToString(), MinimalTemp, ScBasePath);
            Temp[i] = MinimalTemp;

            var ch = i;
            DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}", () => Temp[ch]);
            //DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}MAF", () => _tempFilters[ch].FilteredMAF);
            //DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}Raw", () => _tempFilters[ch].Raw);
        }
    }
    
    protected virtual double[] HandleReadTemp()
    {
        return new double[MaxChannels];
    }
    
    protected virtual bool HandleInitialize()
    {
        return true;
    }
    
    public bool Initialize()
    {
        try
        {
            if (!SC.GetValue<bool>($"{ScBasePath}.{Name}.EnableDevice"))
            {
                IsEnabled = false;
                return true;
            }

            IsEnableLog = SC.SafeGetValue($"TempSensors.EnableLogMessage", false);
            
            //_filterType = ConvertToFilterType(SC.SafeGetStringValue($"{ScBasePath}.FilterType", "None"));
            
            var pollInvMs = SC.SafeGetValue($"{ScBasePath}.PollInterval", 100);
            

            // 系统参数变更回调
            SC.RegisterValueChangedCallback($"{ScBasePath}.PollInterval",
                (obj) => { _tReadTemp.ChangeInterval((int)obj); });

            //SC.RegisterValueChangedCallback($"{ScBasePath}.FilterType",
            //    (obj) => { _filterType = ConvertToFilterType(obj?.ToString() ?? ""); });

            SC.RegisterValueChangedCallback($"TempSensors.EnableLogMessage",
                (obj) =>
                {
                    IsEnableLog = bool.TryParse(obj.ToString(), out var en) && en;
                });
            
            TempBasFunction = new TempBasFunction(Name, MinimalTemp, MaxChannels);
            TempBasFunction.SetPm1Pm2IoForInterlock(false);

            InitTempDataFilter();

            // 执行派生类初始化方法
            var userInit = HandleInitialize();

            _tReadTemp = new PeriodicJob(pollInvMs, DoTempReadThread, $"{Name}", true);
            return userInit;
        }
        catch (Exception e)
        {
            LOG.Error(e.Message, e);
            return false;
        }
    }
    
    public virtual bool Connect()
    {
        return true;
    }

    public virtual bool Disconnect()
    {
        return true;
    }
    
    public virtual void Terminate()
    {
    }

    public virtual void Reset()
    {
        foreach (var rt in RTrigs)
            rt.RST = true;
    }

    public override string ToString()
    {
        return $"{Module}.{Name}";
    }

    #endregion
}