using BoFilTest.Utils;
using Optional;
using System;
using System.Globalization;
using System.IO.Ports;
using System.Text.RegularExpressions;

namespace BoFilTest.Domain.Modules.Balance
{
    public class SartoriusBalance : IBalance
    {
        private const int SHORT_RESPONSE_LENGTH_WITHOUT_LINE_ENDINGS = 14;
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        private readonly SerialPort port;

        public static Gram ParseBalanceResponse(string input)
        {
            if (input.Length < SHORT_RESPONSE_LENGTH_WITHOUT_LINE_ENDINGS)
            {
                throw new HardwareException("Cannot parse balance response " + input);
            }
            if (input.Length > SHORT_RESPONSE_LENGTH_WITHOUT_LINE_ENDINGS)
            {
                // Split the beginning off for the longer responses
                input.Substring(input.Length - SHORT_RESPONSE_LENGTH_WITHOUT_LINE_ENDINGS);
            }

            // Read until the gram
            var end = input.IndexOf('g');
            var value = (end != -1) ? input.Substring(0, end)  : input;
            value = Regex.Replace(value, @"\s+", "");
            return Gram.From(double.Parse(value, CultureInfo.InvariantCulture));
        }

        public SartoriusBalance(BalanceConfiguration config)
        : this(config.Port(), config.BaudRate(), config.Parity(),
              config.DataBits(), config.StopBits())
        {
        }

        public SartoriusBalance(string port, int baudRate, Parity parity, int dataBits, StopBits stopBits)
        {
            this.port = new SerialPort(port,
                baudRate, parity, dataBits, stopBits);

            Start();
        }

        public void Start()
        {
            port.NewLine = "\r\n";
            port.ReadTimeout = 1000;

            // Create the serial port with basic settings
            port.Open();
        }

        public void Cleanup()
        {
            logger.Info("Disconnecting balance");
            port.Close();
        }

        public Option<Gram> ReadValue()
        {
            // Dump everything already read
            port.DiscardInBuffer();

            // Esc + P + CRLF
            port.WriteLine("\u001BP");

            var answer = port.ReadLine();
            logger.Info("Balance read: {0}", answer);

            try
            {

                return Option.Some(ParseBalanceResponse(answer));
            }
            catch (Exception e)
            {
                logger.Info("Error while reading balance value: {0}", e.Message);
                return Option.None<Gram>();
            }
        }
    }
}
