using System;
using QCRPG.Framework;
using QCRPG.TSL.FM.Packets;

namespace QCRPG.TSL.FM;

public class FMAPI : APIBase
{
	private const string uartToI2CRequiredOnBottomMessage = "requires the bottom protocol to be the UART to I2C protocol";

	private ProtocolUARTToI2C protocolUARTToI2C;

	private ConnectionSerialPort connectionSerialPort;

	private FMCustomSendPacketHandler fmCustomSendPacketHandler;

	public FMAPI(PacketListBase packetList, Logger logger, PacketReceiveMode packetReceiveMode)
		: base(packetList, logger, packetReceiveMode)
	{
		protocolUARTToI2C = new ProtocolUARTToI2C();
		protocolUARTToI2C.IgnoreInterrupts = true;
		fmCustomSendPacketHandler = new FMCustomSendPacketHandler(this, receivePacketHandler);
	}

	protected override void CreateReceivePacketHandler()
	{
		receivePacketHandler = new FMReceivePacketHandler(packetList);
		fmCustomSendPacketHandler = new FMCustomSendPacketHandler(this, receivePacketHandler);
	}

	public void OpenConnection(byte i2cSlaveAddress, string portName, int baudRate)
	{
		protocolUARTToI2C.I2CSlaveAddress = i2cSlaveAddress;
		ChangeBottomProtocol(protocolUARTToI2C);
		if (connectionSerialPort != null)
		{
			connectionSerialPort.Close();
		}
		connectionSerialPort = new ConnectionSerialPort();
		connectionSerialPort.RegisterConnectionStateChangeInfoObserver(connectionStateChangeInfoObserver);
		connectionSerialPort.Open(portName, baudRate);
		ChangeConnection(connectionSerialPort);
		ConnectionStateChangeInfo connectionStateChangeInfo = new ConnectionStateChangeInfo();
		connectionStateChangeInfo.slaveAddressChanged = true;
		connectionStateChangeInfo.slaveAddress = i2cSlaveAddress;
		connectionStateChangeInfoNotifier.NotifyObservers(connectionStateChangeInfo);
		logger.NotifyObserver(new LogMessage(LogMessageType.Info, string.Format("Opened FM connection (slaveAddress = 0x{0}, portName = {1}, dataRate = {2})", i2cSlaveAddress.ToString("X2"), portName, baudRate)));
	}

	public void CloseConnection()
	{
		if (connectionSerialPort != null)
		{
			connectionSerialPort.Close();
			logger.NotifyObserver(new LogMessage(LogMessageType.Info, "Closed FM connection"));
		}
	}

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

	protected override void SetupReceiveDataObservation()
	{
		protocolUARTToI2C.RegisterReceiveDataObserverPostProcessing(receiveDataObserver);
	}

	protected override void ShutdownReceiveDataObservation()
	{
		protocolUARTToI2C.UnregisterReceiveDataObserverPostProcessing(receiveDataObserver);
	}

	public void SetI2CSlaveAddress(byte slaveAddress)
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("SetI2CSlaveAddress() requires the bottom protocol to be the UART to I2C protocol");
		}
		protocolUARTToI2C.I2CSlaveAddress = slaveAddress;
		ConnectionStateChangeInfo connectionStateChangeInfo = new ConnectionStateChangeInfo();
		connectionStateChangeInfo.slaveAddressChanged = true;
		connectionStateChangeInfo.slaveAddress = slaveAddress;
		connectionStateChangeInfoNotifier.NotifyObservers(connectionStateChangeInfo);
	}

	public void SetI2CReadResponseTimeoutMs(int timeoutMs)
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("SetI2CReadResponseTimeoutMs() requires the bottom protocol to be the UART to I2C protocol");
		}
		protocolUARTToI2C.I2CReadResponseTimeoutMs = timeoutMs;
	}

	public void I2CWrite(byte[] data)
	{
		I2CWrite(data, data.Length);
	}

	public void I2CWrite(byte[] data, int length)
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("I2CWrite() requires the bottom protocol to be the UART to I2C protocol");
		}
		protocolUARTToI2C.SendI2CWrite(new OutgoingPacketData(data, length));
		WriteI2c writeI2c = new WriteI2c();
		writeI2c.UserFields.Length = (uint)length;
		for (int i = 0; i < length; i++)
		{
			writeI2c.UserFields.DataList[i] = data[i];
		}
		writeI2c.Flags |= 2u;
		sendPacketNotifier.NotifyObservers(writeI2c);
	}

	public void I2CWrite(byte registerAddress, byte[] data)
	{
		I2CWrite(registerAddress, data, data.Length);
	}

	public void I2CWrite(byte registerAddress, byte[] data, int length)
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("I2CWrite() requires the bottom protocol to be the UART to I2C protocol");
		}
		byte[] array = new byte[length + 1];
		array[0] = registerAddress;
		Array.Copy(data, 0, array, 1, length);
		protocolUARTToI2C.SendI2CWrite(new OutgoingPacketData(array));
		WriteI2cRegister writeI2cRegister = new WriteI2cRegister();
		writeI2cRegister.UserFields.RegisterAddress = registerAddress;
		writeI2cRegister.UserFields.Length = (uint)length;
		for (int i = 0; i < length; i++)
		{
			writeI2cRegister.UserFields.DataList[i] = data[i];
		}
		writeI2cRegister.Flags |= 2u;
		sendPacketNotifier.NotifyObservers(writeI2cRegister);
	}

	public byte I2CReadByte()
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("I2CReadByte() requires the bottom protocol to be the UART to I2C protocol");
		}
		return I2CRead(1)[0];
	}

	public byte I2CReadByte(byte registerAddress)
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("I2CReadByte() requires the bottom protocol to be the UART to I2C protocol");
		}
		return I2CRead(registerAddress, 1)[0];
	}

	public byte[] I2CRead(byte length, bool skipSendNotify = false, bool skipReceiveEnqueue = false)
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("I2CRead() requires the bottom protocol to be the UART to I2C protocol");
		}
		ReadI2c readI2c = new ReadI2c();
		readI2c.UserFields.Length = length;
		if (!skipSendNotify)
		{
			readI2c.Flags |= 2u;
			sendPacketNotifier.NotifyObservers(readI2c);
		}
		IncomingPacketData incomingPacketData = protocolUARTToI2C.SendI2CReadAndGetResponse(length, skipReceiveEnqueue);
		return incomingPacketData.bytes;
	}

	public byte[] I2CRead(byte registerAddress, byte length, bool skipReceiveEnqueue = false)
	{
		if (!(protocolBottom is ProtocolUARTToI2C))
		{
			throw new Exception("I2CRead() requires the bottom protocol to be the UART to I2C protocol");
		}
		protocolUARTToI2C.SendI2CWrite(new OutgoingPacketData(new byte[1] { registerAddress }), includeStopAndWaitForInterruptMacro: false);
		ReadI2cRegister readI2cRegister = new ReadI2cRegister();
		readI2cRegister.UserFields.RegisterAddress = registerAddress;
		readI2cRegister.UserFields.Length = length;
		readI2cRegister.Flags |= 2u;
		sendPacketNotifier.NotifyObservers(readI2cRegister);
		return I2CRead(length, skipSendNotify: true, skipReceiveEnqueue);
	}

	public bool WaitForInterrupt(int timeoutMs)
	{
		return protocolUARTToI2C.WaitForInterrupt(timeoutMs);
	}
}
