using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using QCRPG.Framework;
using QCRPG.TSL.ZigBee.Packets;

namespace QCRPG.TSL.ZigBee;

public class ZigBeeAPI : APIBase
{
	private const int responseWaitTimeMs = 1000;

	private const int resetWaitTimeMs = 3000;

	private const int skipResponseCheckDelayMs = 50;

	private ProtocolTABI protocolTABI;

	private ProtocolDiag protocolDiag;

	private ConnectionSerialPort connectionSerialPort;

	private bool deviceEmulationMode;

	private string sharedConnectionPortName = "";

	private bool decodePacket = true;

	private PacketDecodeInfo packetDecodeInfo = new PacketDecodeInfo();

	public Dictionary<string, IntPtr> ExeginDevicePtrList = new Dictionary<string, IntPtr>();

	public ZigBeeAPI(PacketListBase packetList, Logger logger, PacketReceiveMode packetReceiveMode, bool deviceEmulationMode)
		: base(packetList, logger, packetReceiveMode)
	{
		this.deviceEmulationMode = deviceEmulationMode;
		protocolTABI = new ProtocolTABI();
		protocolTABI.autoDetectProtocolIDByteToUse = deviceEmulationMode;
		protocolDiag = new ProtocolDiag();
		protocolDiag.FtmCrcLength = 0;
	}

	public override void AddToSharedSerialConnection(string portName)
	{
		if (connectionSerialPort != null && connectionSerialPort.IsOpen())
		{
			throw new Exception("Serial port connection is open, refusing to add to shared connection");
		}
		if (GetSharedConnectionPortName() != "" && GetSharedConnectionPortName() != portName)
		{
			RemoveFromSharedSerialConnection(sharedConnectionPortName);
		}
		protocolTABI.Reset();
		ChangeBottomProtocol(protocolTABI, usePacketDataReceiver: false);
		APIBase.sharedConnectionManager.AddRoute(portName, 16, protocolBottom);
		APIBase.sharedConnectionManager.AddRoute(portName, 32, protocolBottom);
		ChangeConnection(APIBase.sharedConnectionManager.GetConnection(portName));
		logger.NotifyObserver(new LogMessage(LogMessageType.Info, $"Opened 15.4 to shared connection (portName = {portName})"));
		sharedConnectionPortName = portName;
		APIBase.sharedConnectionClosedMap[portName].Add(delegate
		{
			sharedConnectionPortName = "";
		});
	}

	public override void RemoveFromSharedSerialConnection(string portName)
	{
		if (connectionSerialPort != null && connectionSerialPort.IsOpen())
		{
			throw new Exception("Serial port connection is open, refusing to remove from shared connection");
		}
		APIBase.sharedConnectionManager.RemoveRoute(portName, 16);
		logger.NotifyObserver(new LogMessage(LogMessageType.Info, $"Removed 15.4 from shared connection (portName = {portName})"));
		sharedConnectionPortName = "";
	}

	public string GetSharedConnectionPortName()
	{
		if (sharedConnectionPortName == "")
		{
			return sharedConnectionPortName;
		}
		if (!APIBase.IsSharedConnectionActiveAndOpen(sharedConnectionPortName))
		{
			sharedConnectionPortName = "";
		}
		return sharedConnectionPortName;
	}

	private void SharedPortClosedHandler()
	{
		if (APIBase.sharedConnectionClosedMap.ContainsKey(sharedConnectionPortName))
		{
			APIBase.sharedConnectionClosedMap[sharedConnectionPortName].Remove(SharedPortClosedHandler);
		}
		sharedConnectionPortName = "";
	}

	public override void AddToSharedDiagConnection(string portName, string diagType)
	{
		if (connectionSerialPort != null && connectionSerialPort.IsOpen())
		{
			throw new Exception("Serial port connection is open, refusing to add to shared connection");
		}
		if (GetSharedConnectionPortName() != "" && GetSharedConnectionPortName() != portName)
		{
			RemoveFromSharedDiagConnection(sharedConnectionPortName);
		}
		ShutdownReceiveDataObservation();
		protocolTABI.Reset();
		ChangeTopProtocol(protocolTABI);
		ChangeBottomProtocol(protocolDiag, usePacketDataReceiver: false);
		AddSharedDiagConnectionRoutes(portName, protocolDiag.FtmCommandCode, protocolDiag.LogCodes);
		SetupReceiveDataObservation();
		ChangeConnection(APIBase.sharedConnectionManager.GetConnection(portName));
		((ConnectionDiag)connection).ConfigureAsyncReceive(protocolDiag.LogCodes);
		logger.NotifyObserver(new LogMessage(LogMessageType.Info, $"Added 15.4 to shared diag connection (portName = {portName})"));
		sharedConnectionPortName = portName;
		APIBase.sharedConnectionClosedMap[portName].Add(delegate
		{
			sharedConnectionPortName = "";
		});
	}

	public override void RemoveFromSharedDiagConnection(string portName)
	{
		if (connectionSerialPort != null && connectionSerialPort.IsOpen())
		{
			throw new Exception("Serial port connection is open, refusing to remove from shared connection");
		}
		RemoveSharedDiagConnectionRoutes(portName, protocolDiag.FtmCommandCode, protocolDiag.LogCodes);
		logger.NotifyObserver(new LogMessage(LogMessageType.Info, $"Removed 15.4 from shared diag connection (portName = {portName})"));
		sharedConnectionPortName = "";
	}

	protected override void CreateReceivePacketHandler()
	{
		receivePacketHandler = new ZigBeeReceivePacketHandler(packetList);
	}

	public override void Send(PacketBase packet)
	{
		ZigBeeOutgoingPacketData zigBeeOutgoingPacketData = new ZigBeeOutgoingPacketData(packet.ToByteArray());
		if (packet.Type == PacketType.Command)
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Command;
		}
		else if (packet.Type == PacketType.ResponseAsCommand)
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.ResponseAsCommand;
		}
		else if (packet.Type == PacketType.Notification || packet.Type == PacketType.Response)
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Event;
		}
		else if (packet.Type == PacketType.Tabi20Command)
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Tabi20Command;
		}
		else if (packet.Type == PacketType.Tabi20Event)
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Tabi20Event;
		}
		else
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Raw;
		}
		packetDataSender.SendSync(zigBeeOutgoingPacketData);
		packet.Flags |= 2u;
		sendPacketNotifier.NotifyObservers(packet);
	}

	public override void SendAsync(PacketBase packet)
	{
		ZigBeeOutgoingPacketData zigBeeOutgoingPacketData = new ZigBeeOutgoingPacketData(packet.ToByteArray());
		if (packet.Type == PacketType.Command)
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Command;
		}
		else if (packet.Type == PacketType.Notification || packet.Type == PacketType.Response)
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Event;
		}
		else
		{
			zigBeeOutgoingPacketData.ZigBeePacketType = ZigBeePacketDataType.Raw;
		}
		packetDataSender.SendAsync(zigBeeOutgoingPacketData);
		packet.Flags |= 2u;
		sendPacketNotifier.NotifyObservers(packet);
	}

	public void Send(ZigBeeOutgoingPacketData ZigBeeOutgoingPacketData, bool performValidPacketCheck = false)
	{
		packetDataSender.SendSync(ZigBeeOutgoingPacketData);
		if (performValidPacketCheck)
		{
			try
			{
				PacketBase packetBase = packetList.BytesToPacket(ZigBeeOutgoingPacketData.bytes);
				packetBase.Flags |= 2u;
				sendPacketNotifier.NotifyObservers(packetBase);
			}
			catch (Exception)
			{
			}
		}
	}

	public void OpenConnection(string portName, int baudRate, Handshake handshake)
	{
		if (GetSharedConnectionPortName() != "")
		{
			throw new Exception("Already added to shared connection, refusing to try to open");
		}
		if (connectionSerialPort != null)
		{
			connectionSerialPort.Close();
		}
		ShutdownReceiveDataObservation();
		protocolTABI.Reset();
		ChangeBottomProtocol(protocolTABI);
		SetupReceiveDataObservation();
		connectionSerialPort = new ConnectionSerialPort();
		connectionSerialPort.RegisterConnectionStateChangeInfoObserver(connectionStateChangeInfoObserver);
		connectionSerialPort.Open(portName, baudRate, handshake, dtrEnable: true);
		ChangeConnection(connectionSerialPort);
		logger.NotifyObserver(new LogMessage(LogMessageType.Info, $"Opened 15.4 connection (portName = {portName}, dataRate = {baudRate})"));
	}

	public void CloseConnection()
	{
		if (GetSharedConnectionPortName() != "")
		{
			if (protocolBottom is ProtocolDiag)
			{
				RemoveFromSharedDiagConnection(sharedConnectionPortName);
			}
			else
			{
				RemoveFromSharedSerialConnection(sharedConnectionPortName);
			}
		}
		if (connectionSerialPort != null)
		{
			connectionSerialPort.Close();
			logger.NotifyObserver(new LogMessage(LogMessageType.Info, "Closed 15.4 connection"));
		}
	}

	public bool IsConnectionOpen()
	{
		if (connectionSerialPort == null)
		{
			return false;
		}
		return connectionSerialPort.IsOpen();
	}

	protected override void SetupReceiveDataObservation()
	{
		if (protocolBottom is ProtocolTABI)
		{
			protocolTABI.RegisterReceiveDataObserverPostProcessing(receiveDataObserver);
		}
		else if (protocolBottom is ProtocolDiag)
		{
			protocolDiag.RegisterReceiveDataObserverPostProcessing(receiveDataObserver);
		}
	}

	protected override void ShutdownReceiveDataObservation()
	{
		if (protocolBottom is ProtocolTABI)
		{
			protocolTABI.UnregisterReceiveDataObserverPostProcessing(receiveDataObserver);
		}
		else if (protocolBottom is ProtocolDiag)
		{
			protocolDiag.UnregisterReceiveDataObserverPostProcessing(receiveDataObserver);
		}
	}

	public void WriteNVM(string filename, bool skipResponseChecks = false)
	{
		logger.NotifyObserver(new LogMessage(LogMessageType.Info, "Starting 15.4 NVM download"));
		NVMFileReader nVMFileReader = new NVMFileReader();
		List<NVMTag> list = nVMFileReader.ReadTags(filename);
		ModeAgnosticDataReceiver modeAgnosticDataReceiver = new ModeAgnosticDataReceiver(this);
		modeAgnosticDataReceiver.Startup();
		try
		{
			List<PacketBase> list2 = null;
			foreach (NVMTag item in list)
			{
				VsNvmSet vsNvmSet = new VsNvmSet();
				vsNvmSet.UserFields.Nvmtag = (uint)item.tagNumber;
				vsNvmSet.UserFields.Nvmlength = (uint)item.tagLength;
				for (int i = 0; i < item.tagLength; i++)
				{
					vsNvmSet.UserFields.NvmvalueList[item.tagLength - 1 - i] = item.tagValue[i];
				}
				Send(vsNvmSet);
				if (!skipResponseChecks)
				{
					list2 = modeAgnosticDataReceiver.ReceiveAll(vsNvmSet.ExpectedResponseTypes, 1000);
					VsNvmSetConfirmation vsNvmSetConfirmation = (VsNvmSetConfirmation)PacketHelper.GetPacketTypeFromList(typeof(VsNvmSetConfirmation), list2);
					if (vsNvmSetConfirmation.UserFields.Nvmtag != item.tagNumber)
					{
						logger.NotifyObserver(new LogMessage(LogMessageType.Warning, "Tag number doesn't match in event"));
					}
					if (vsNvmSetConfirmation.UserFields.Status != 0)
					{
						logger.NotifyObserver(new LogMessage(LogMessageType.Error, "Status is not success (0x00"));
					}
				}
				else
				{
					Thread.Sleep(50);
				}
			}
			Thread.Sleep(500);
			MlmeReset mlmeReset = new MlmeReset();
			mlmeReset.UserFields.Reset = 2u;
			Send(mlmeReset);
			if (!skipResponseChecks)
			{
				list2 = modeAgnosticDataReceiver.ReceiveAll(mlmeReset.ExpectedResponseTypes, 3000);
				MlmeResetConfirmation packet = (MlmeResetConfirmation)PacketHelper.GetPacketTypeFromList(typeof(MlmeResetConfirmation), list2);
				CheckStatus(packet);
			}
			else
			{
				Thread.Sleep(50);
			}
			logger.NotifyObserver(new LogMessage(LogMessageType.Info, "Finished 15.4 NVM download"));
		}
		finally
		{
			modeAgnosticDataReceiver.Shutdown();
		}
	}

	public void SendMLMEReset()
	{
		MlmeReset packet = new MlmeReset();
		Send(packet);
	}

	public void SetDecodePacket(bool decodePacket, bool packetResponse, bool conciseLog = false)
	{
		this.decodePacket = decodePacket;
		packetDecodeInfo.PacketDecode = decodePacket;
		packetDecodeInfo.PacketResponse = packetResponse;
		packetDecodeInfo.ConciseLog = conciseLog;
		packetDecodeNotifier.NotifyObservers(packetDecodeInfo);
	}

	private void CheckStatus(PacketBase packet)
	{
		foreach (PacketFieldInfo fieldInfo in packet.Info.FieldInfoList)
		{
			if (fieldInfo.ParameterName == "Status")
			{
				ErrorCodeEnum errorCodeEnum = ErrorCodeInfo.ValueToErrorCode(Convert.ToUInt32(fieldInfo.Value, 16));
				if (errorCodeEnum != 0)
				{
					throw new Exception("Status field indicates error: " + errorCodeEnum);
				}
				return;
			}
		}
		throw new Exception("Packet type doesn't have a status field: " + packet.GetType().ToString());
	}
}
