using GeneralData;
using HardwareData;
using Mini8Communicator;
using Mini8CommunicatorData;

namespace Mini8CommunicatorSim;

internal class InputExtern(Mini8Input output)
{
    public Mini8Input Mini8Output { get; set; } = output;
    public AutoTuneStatus AutoTuneStatus { get; set; }
    public ActiveTuneSet ActiveTuneSet { get; set; }
    public Inhibit Inhibit { get; set; }
    public float AutoTune_P { get; set; }
    public float AutoTune_I { get; set; }
    public float AutoTune_D { get; set; }
}


public class Mini8Communicator_Simulator : IMini8Communicator
{
    private IMini8Provider? _provider;
    private string _ip = string.Empty;
    private int _port = 0;
    private readonly ManualResetEvent _pauseEvent = new(true);
    private readonly Mini8Output _inputData = new();
    private readonly Dictionary<byte, InputExtern> _outputs = [];
    private IDictionary<byte, ChannelAddress>? _addresses;

    bool IMini8Communicator.Initialize(IMini8Provider provider, object mini8, object address)
    {

        if (address is not IDictionary<byte, ChannelAddress> addresses)
            return false;

        this._addresses = addresses;
        this._provider = provider;
        return true;
    }

    bool IMini8Communicator.Open()
    {
        if (this._provider is null)
            return false;

        //this._ip = IP;
        //this._port = port;
        //this._provider.OnConnected(IP, port);
        //this._addresses = this._provider.GetUpdateList<ushort>();
        Task.Factory.StartNew(UpdateRealtimeData);
        return true;
    }

    private void UpdateRealtimeData()
    {
        if (this._provider is null || this._addresses is null)
            return;
        Random r = new();
        while (true)
        {
            foreach (ChannelAddress item in _addresses.Values)
            {
                if (!this._outputs.TryGetValue(item.ChannelIndex, out InputExtern? mini8Output) || mini8Output is null)
                    continue;

                _inputData.CollectTime = DateTime.Now;
                _inputData.PV = (float)(mini8Output.Mini8Output.SetPoint + r.NextDouble() - r.NextDouble());
                _inputData.WorkingOutput = 10;
                _inputData.AutoTuneStatus = mini8Output.AutoTuneStatus;
                _inputData.Running_P = mini8Output.Mini8Output.Running_P;
                _inputData.Running_I = mini8Output.Mini8Output.Running_I;
                _inputData.Running_D = mini8Output.Mini8Output.Running_D;
                _inputData.SetPoint = mini8Output.Mini8Output.SetPoint;
                _inputData.ActiveTuneSet = ActiveTuneSet.Running;
                _inputData.AutoTune_P = mini8Output.AutoTune_P;
                _inputData.AutoTune_I = mini8Output.AutoTune_I;
                _inputData.AutoTune_D = mini8Output.AutoTune_D;
                _inputData.Inhibit = mini8Output.Inhibit;
                _inputData.SetpointUpRate = mini8Output.Mini8Output.SetpointUpRate;
                _inputData.SetpointDownRate = mini8Output.Mini8Output.SetpointDownRate;
                _inputData.SensorBreakAlarm = TcBorken.Normal;
                _inputData.ActiveTuneSet = mini8Output.ActiveTuneSet;

                this._provider?.ChannelInfoNotify(item.ChannelIndex, _inputData);
            }
            Thread.Sleep(1000);
        }
    }

    bool IMini8Communicator.SendMini8Data(byte c, Mini8Input output)
    {
        if (!this._outputs.TryGetValue(c, out InputExtern? mini8Output) || mini8Output is null)
            mini8Output = new(output) { AutoTuneStatus = AutoTuneStatus.Ready, ActiveTuneSet = ActiveTuneSet.Running };
        else
            mini8Output.Mini8Output = output;

        this._outputs[c] = mini8Output;
        return true;
    }

    bool IMini8Communicator.EnableChannel(byte channelIndex, Inhibit inhibit)
    {
        if (!this._outputs.TryGetValue(channelIndex, out InputExtern? mini8Output) || mini8Output is null)
            return false;

        mini8Output.Inhibit = inhibit;

        return true;
    }

    bool IMini8Communicator.SelectPID(byte channelIndex, ActiveTuneSet activeTuneSet)
    {
        if (!this._outputs.TryGetValue(channelIndex, out InputExtern? mini8Output) || mini8Output is null)
            return false;
        mini8Output.ActiveTuneSet = activeTuneSet;
        return true;
    }

    bool IMini8Communicator.EnableChannelAutoTune(byte channelIndex, bool isEnable)
    {
        if (!this._outputs.TryGetValue(channelIndex, out InputExtern? mini8Output) || mini8Output is null)
            return false;

        mini8Output.AutoTuneStatus = isEnable switch
        {
            true => AutoTuneStatus.Triggered,
            false => AutoTuneStatus.Ready,
        };

        mini8Output.ActiveTuneSet = isEnable switch
        {
            true => ActiveTuneSet.AutoTune,
            false => ActiveTuneSet.Running,
        };

        if (!isEnable)
            return true;

        Task.Factory.StartNew(() =>
        {
            InputExtern timerMini8 = mini8Output;
            Random r = new();
            Thread.Sleep(2000);
            mini8Output.AutoTuneStatus = AutoTuneStatus.Tuning;
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(1000);
                timerMini8.AutoTune_P = r.Next(0, 20) + r.NextSingle();
                timerMini8.AutoTune_I = r.Next(0, 20) + r.NextSingle();
                timerMini8.AutoTune_D = r.Next(0, 20) + r.NextSingle();
            }

            timerMini8.AutoTuneStatus = AutoTuneStatus.Complete;

        });


        return true;
    }

    bool IMini8Communicator.Close()
    {
        this._provider?.OnConnected(_ip, _port);
        return true;
    }

    bool IMini8Communicator.Pause()
    {
        _pauseEvent.Reset();
        return true;
    }

    bool IMini8Communicator.StartDataCollector()
    {
        _pauseEvent.Set();
        return true;
    }

    bool IMini8Communicator.GetRealtimeDataFromDevice(out Dictionary<byte, Mini8Output>? data)
    {
        data = null;
        return true;
    }

    bool IMini8Communicator.GetRealtimeLimit(out Dictionary<byte, Mini8Limit>? data)
    {
        data = null;
        return true;
    }
}