// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global

using InnovEnergy.Lib.Utils;
using static System.Environment;

namespace InnovEnergy.Lib.Protocols.Modbus.Protocol;

public class UnexpectedResponseFrameException : Exception
{

    public String Expected { get; }
    public String Actual   { get; }

    public UnexpectedResponseFrameException(Type expected, Type actual) : base(Msg(expected,actual))
    {
        Expected = expected.Name;
        Actual   = actual.Name;
    }

    private static String Msg(Object expected, Object actual)
    {
        return NewLine +
               "Unexpected response Frame! " + NewLine +
               "Expected: " + expected + NewLine +
               "Received: " + actual   + NewLine;
    }
}

public class CrcException : UnexpectedResponseFieldException
{
    public CrcException(UInt16 expected, UInt16 actual) : base("CRC", expected.ToString("X4"), actual.ToString("X4"))
    {
    }

    public CrcException(IReadOnlyList<Byte> expected, IReadOnlyList<Byte> actual) : base("CRC", CrcString(expected), CrcString(actual))
    {
        if (expected.Count != 2) throw new ArgumentException(nameof(expected));
        if (actual.Count != 2)   throw new ArgumentException(nameof(actual));
    }

    private static String CrcString(IEnumerable<Byte> bytes)
    {
        return bytes
            .Select(b => b.ToString("X2"))
            .Aggregate("", (a, b) => a + " " + b)
            .Trim();
    }

}

public class UnexpectedResponseFieldException : Exception
{
    public String Field    { get; }
    public String Expected { get; }
    public String Actual   { get; }

    public UnexpectedResponseFieldException(String field, Object? expected, Object? actual) : base(Msg(field,expected,actual))
    {
        Field    = field;
        Expected = expected.SafeToString();
        Actual   = actual.SafeToString();
    }

    private static String Msg(String field, Object? expected, Object? actual)
    {
        return NewLine +
               "Field:    " + field                   + NewLine +
               "Expected: " + expected.SafeToString() + NewLine +
               "Received: " + actual  .SafeToString() + NewLine;
    }
}

public class NotConnectedException : Exception
{
    public NotConnectedException(String message)
        : base(message)
    { }
}

public class ErrorResponseException : Exception
{
    public ExceptionCode ExceptionCode { get; }
    public FunctionCode  FunctionCode  { get; }

    public ErrorResponseException(ExceptionCode exceptionCode, FunctionCode functionCode)
    {
        ExceptionCode = exceptionCode;
        FunctionCode = functionCode;
    }
}

public class ProtocolVersionException : Exception
{
    public UInt16 ExpectedVersion { get; }
    public UInt16 ActualVersion   { get; }

    public ProtocolVersionException(UInt16 expectedVersion, UInt16 actualVersion)
    {
        ExpectedVersion = expectedVersion;
        ActualVersion = actualVersion;
    }
}

public class MessageTooShortException : Exception
{
    public MessageTooShortException():base("Message too short")
    {
    }
}