using RTCommunicatorBase;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using TLVProtocal;
using UniversalNetFrame451;
using UniversalNetFrame451.IO;

namespace RTCommunicatorTLV;

public class RTCommunicator_TLV : IRTMini8Sender, ITlvProvider
{
    private IRTMini8Provider _provider;
    private ITlvCommunicatorClient _client;
    private readonly ConcurrentDictionary<byte, Timer> _DisconnectNotifyTimer = [];
    private Dictionary<byte, Dictionary<byte, HeaterType>> _HeaterTypes;

    public ConcurrentDictionary<string, ST_CHANNEL_Notify> ChannelData { get; } = [];

    public bool Initialize(IRTMini8Provider provider)
    {
        if (provider is null)
            return false;

        if (this._provider is not null)
            return false;
        string path = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "HeaterType.csv");
        if (!HeaterTypeAnalizer.Analize(path, out _HeaterTypes) || _HeaterTypes is null)
            return false;

        this._provider = provider;

        return true;
    }

    public bool StartService(string ip, int port)
    {
        if (_provider is null)
            return false;

        if (this._client is not null)
            return false;

        if (string.IsNullOrEmpty(ip) || port < 5000 || port > ushort.MaxValue)
            return false;

        ITlvCommunicatorClient client = TlvFactory.GetTlvClient();
        if (!client.Initialize(this, false, 500))
            return false;

        Task.Factory.StartNew(() =>
        {
            while (!client.Open(ip, (ushort)port))
            {
                Thread.Sleep(1000);
            }
            this._client = client;

            this._provider?.Connected(ip, port);

        });

        return true;
    }
    public bool CloseService()
    {
        this._client?.Dispose();
        this._client = null;
        this._provider = null;
        return true;
    }

    public bool SelectConfigFile(string name)
    {
        if (this._client is null)
            return false;
        if (string.IsNullOrEmpty(name))
            return false;

        byte[] bytes = Encoding.UTF8.GetBytes(name);
        TlvData data = new(1, bytes);
        return this._client?.SendNotify(data) == true;
    }

    void ITlvProvider.Received(TlvData data)
    {
        if (data.RawData is null || data.RawData.Length == 0)
            return;
        Task.Factory.StartNew(() =>
        {
            switch (data.Tag)
            {
                case Tags.CurrentTempConfigFile:
                    string fileName = Encoding.UTF8.GetString(data.RawData);
                    if (string.IsNullOrEmpty(fileName))
                        return;
                    try
                    {
                        this._provider?.CurrentTempConfigFile(fileName);
                    }
                    catch
                    {
                    }
                    break;
                case Tags.ChannelAlarmNotify:
                    if (!StructConverter.TryGetStruct(data.RawData, out ST_ALARM? alarm) || alarm is null)
                        return;

                    if (this._HeaterTypes is null)
                        return;

                    if (!this._HeaterTypes.TryGetValue(alarm.Value.Mini8Index, out var channels) || channels is null)
                        return;

                    if (!channels.TryGetValue(alarm.Value.ChannelIndex, out HeaterType heaterType))
                        return;

                    ST_ALARM_Notify alarmNotify = new()
                    {
                        Mini8Index = alarm.Value.Mini8Index,
                        ChannelIndex = alarm.Value.ChannelIndex,
                        PV = alarm.Value.PV,
                        Caps = alarm.Value.Caps,
                        Floor = alarm.Value.Floor,
                        WorkingOutput = alarm.Value.WorkingOutput,
                        AlarmType = alarm.Value.AlarmType,
                        HeaterType = heaterType,
                    };
                    try
                    {
                        this._provider?.ChannelAlarmNotify(alarmNotify);
                    }
                    catch
                    {
                    }
                    break;
                case Tags.Mini8ConnectNotify:
                    {
                        byte index = data.RawData[0];
                        this._DisconnectNotifyTimer.TryRemove(index, out Timer timer);
                        timer?.Dispose();
                        try
                        {
                            this._provider?.Mini8ConnectNotify(index);
                        }
                        catch
                        {
                        }
                        break;
                    }
                case Tags.Mini8DisconnectNotify:
                    {
                        byte index = data.RawData[0];
                        if (!this._DisconnectNotifyTimer.ContainsKey(index))
                            this._DisconnectNotifyTimer[index] = new(DisconnectNotifyTimerCallBack, index, 0, 1000);
                        break;
                    }
                case Tags.ChannelRealtimeDataNotify:
                    if (!StructConverter.TryGetStruct(data.RawData, out ST_CHANNEL? channel) || channel is null)
                        return;

                    string key = $"{channel.Value.Mini8Index}-{channel.Value.ChannelIndex}";

                    if (this.ChannelData.TryGetValue(key, out ST_CHANNEL_Notify notify) || notify is null)
                    {
                        notify = new()
                        {
                            Mini8Index = channel.Value.Mini8Index,
                            ChannelIndex = channel.Value.ChannelIndex,
                            PV = channel.Value.PV,
                            Caps = channel.Value.Caps,
                            Floor = channel.Value.Floor,
                            WorkingOutput = channel.Value.WorkingOutput,

                        };
                        this.ChannelData[key] = notify;
                    }
                    else
                    {
                        notify.Mini8Index = channel.Value.Mini8Index;
                        notify.ChannelIndex = channel.Value.ChannelIndex;
                        notify.PV = channel.Value.PV;
                        notify.Caps = channel.Value.Caps;
                        notify.Floor = channel.Value.Floor;
                        notify.WorkingOutput = channel.Value.WorkingOutput;
                    }

                    try
                    {
                        this._provider?.ChannelRealtimeNotify(notify);
                    }
                    catch
                    {
                    }
                    break;
                default:
                    break;
            }
        });
    }

    TlvData ITlvProvider.RequestReply(TlvData tlvData)
    {
        return tlvData;
    }

    void ITlvProvider.Connected(TcpConnection connection)
    {
        this._provider.Connected(connection.RemoteEndPoint.Address.ToString(), connection.RemoteEndPoint.Port);
    }

    void ITlvProvider.Disconnected(TcpConnection connection)
    {
        this._provider.DisConnected(connection.RemoteEndPoint.Address.ToString(), connection.RemoteEndPoint.Port);
    }

    private void DisconnectNotifyTimerCallBack(object state)
    {
        if (state is not byte index)
            return;

        this._provider?.Mini8DisconnectNotify(index);
    }
}
