namespace Universal.IO;

public class IOSerial : BaseFilter, IDisposable
{
    private static bool SerialPortExist(string portName)
    {
        foreach (string singlePortName in SerialPort.GetPortNames())
        {
            if (singlePortName.ToLower().Equals(portName.ToLower()))
                return true;
        }
        return false;
    }
    volatile bool _init = false;
    public string? Name { get; internal set; }
    private SerialConnection? _SerialPortConnection;

    public bool Initialize(string name)
    {
        if (_init)
            return false;
        _init = true;
        this.Name = name;
        return true;
    }

    public bool Open(string strPortName, int baudRate, Parity parity, int dataBits, StopBits stopBits, out SerialConnection? connection)
    {
        connection = null;
        if (!_init)
            return false;

        if (_SerialPortConnection is not null && _SerialPortConnection.SerialPort.IsOpen)
            return false;

        if (!SerialPortExist(strPortName))
            return false;

        if (baudRate <= 0)
            return false;

        if (dataBits < 5 || dataBits > 8)
            return false;

        SerialPort serialPort;
        SerialConnection? serialPortConnection = null;
        try
        {
            serialPort = new(strPortName, baudRate, parity, dataBits, stopBits);
            serialPort.Open();
            serialPortConnection = new(strPortName, serialPort);

            PrevConnector?.Connected(serialPortConnection!);
        }
        catch { return false; }

        serialPort.DataReceived += DataReceivedEvent;

        this._SerialPortConnection = serialPortConnection;
        connection = this._SerialPortConnection;
        return true;
    }

    void DataReceivedEvent(object sender, SerialDataReceivedEventArgs e)
    {
        DateTime receiveTime = DateTime.Now;

        if (this._SerialPortConnection is null)
            return;

        byte[] cache = new byte[this._SerialPortConnection.SerialPort.BytesToRead];
        try
        {
            this._SerialPortConnection.SerialPort.Read(cache, 0, cache.Length);
        }
        catch
        {
            //Port Closed
        }


        PrevReceiver?.Receive(new(cache, _SerialPortConnection, receiveTime));
    }

    public override bool Send(Data data)
    {

        if (!_init)
            return false;

        if (this._SerialPortConnection is null)
            return false;

        if (this._SerialPortConnection is null)
            return false;

        if (!this._SerialPortConnection.SerialPort.IsOpen)
            return false;

        if (!this._SerialPortConnection.SerialPort.DsrHolding)
            return false;

        if (data.RawData == null || data.RawData.Length == 0)
            return false;

        if (data.Connection is null)
            return false;

        if (data.Connection != this._SerialPortConnection)
            return false;

        try
        {
            this._SerialPortConnection.SerialPort.BaseStream.Write([.. data.RawData], 0, data.RawData.Length);
            this._SerialPortConnection.SerialPort.BaseStream.Flush();
        }
        catch
        {
            return false;
        }

        return true;
    }

    public bool Close()
    {
        if (!_init)
            return false;

        if (this._SerialPortConnection is not null)
        {
            if (this._SerialPortConnection.SerialPort.IsOpen)
                this._SerialPortConnection.SerialPort.Close();

            PrevConnector?.Disconnected(this._SerialPortConnection);
        }

        return true;
    }

    private bool disposedValue;

    protected virtual void Dispose(bool disposing)
    {
        if (disposedValue)
            return;
        if (disposing)
            this._SerialPortConnection = null;

        Close();

        disposedValue = true;
    }
    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}
