using Modbus.Device;
using System;
using System.IO.Ports;

namespace BoFilTest.Domain.Modules
{
    public class VoegtlinDeviceModbusRtuConnection : IDisposable
    {
        private readonly SerialPort serial;
        private readonly ModbusSerialMaster master;

        public VoegtlinDeviceModbusRtuConnection(string portName)
        {
            serial = new SerialPort(portName)
            {
                BaudRate = 9600,
                Parity = Parity.None,
                DataBits = 8,
                StopBits = StopBits.Two,
                ReadTimeout = 1000
            };
            serial.Open();
            serial.DiscardInBuffer();
            master = ModbusSerialMaster.CreateRtu(serial);
        }

        public ModbusSerialMaster Master => master;

        public void Dispose()
        {
            master.Dispose();
            serial.Dispose();
        }
    }

    public class VoegtlinDevice
    {
        // BOF-67 - Devices are preset with these curves
        public const int FIRST_CURVE_HW_INDEX = 2;
        public const int SECOND_CURVE_HW_INDEX = 3;

        private const int LUT_REGISTER = 0x4139;
        private const int FLOW_REGISTER = 0x0000;
        private const int SERIAL_NUMBER_REGISTER = 0x001e;
        private const int PRESSURE_REGISTER = 0x5f00;

        private const int TOTALISATOR_READ_REGISTER = 0x0004;
        private const int TOTALISATOR_WRITE_REGISTER = 0x6380;

        private const int WORDS_FOR_FLOAT = sizeof(float) / sizeof(ushort);

        private readonly ModbusMaster master;
        private readonly byte slaveAddress;

        public VoegtlinDevice(ModbusMaster master, byte slaveAddress)
        {
            this.master = master;
            this.slaveAddress = slaveAddress;
        }

        public uint ReadSerialNumber()
        {
            return ModbusUtilities.DecodeDWord(master.ReadHoldingRegisters(slaveAddress, SERIAL_NUMBER_REGISTER, WORDS_FOR_FLOAT));
        }

        public float ReadFlow()
        {
            return ModbusUtilities.DecodeFloat(master.ReadHoldingRegisters(slaveAddress, FLOW_REGISTER, WORDS_FOR_FLOAT));
        }

        public float ReadPressure()
        {
            return ModbusUtilities.DecodeFloat(master.ReadHoldingRegisters(slaveAddress, PRESSURE_REGISTER, WORDS_FOR_FLOAT));
        }

        public ushort ReadLUTIndex()
        {
            return master.ReadHoldingRegisters(slaveAddress, LUT_REGISTER, 1)[0];
        }

        public void WriteLUTIndex(ushort index)
        {
            master.WriteSingleRegister(slaveAddress, LUT_REGISTER, index);
        }

        public float ReadTotalisator()
        {
            return ModbusUtilities.DecodeFloat(master.ReadHoldingRegisters(slaveAddress, TOTALISATOR_READ_REGISTER, WORDS_FOR_FLOAT));
        }

        public void WriteTotalisator(float value)
        {
            master.WriteMultipleRegisters(slaveAddress, TOTALISATOR_WRITE_REGISTER, ModbusUtilities.EncodeFloat(value));
        }
    }
}
