using System;
using System.IO;
using System.Net;

namespace NModbus.Message
{
	public class ReadCoilsInputsRequest : AbstractModbusMessage, IModbusRequest
	{
		public ReadCoilsInputsRequest()
		{
		}

		public ReadCoilsInputsRequest(byte functionCode, byte slaveAddress, ushort startAddress, ushort numberOfPoints)
			: base(slaveAddress, functionCode)
		{
			StartAddress = startAddress;
			NumberOfPoints = numberOfPoints;
		}

		public ushort StartAddress
		{
			get => MessageImpl.StartAddress.Value;
			set => MessageImpl.StartAddress = value;
		}

		public override int MinimumFrameSize => 6;

		public ushort NumberOfPoints
		{
			get => MessageImpl.NumberOfPoints.Value;

			set
			{
				if (value > Modbus.MaximumDiscreteRequestResponseSize)
				{
					string msg = $"Maximum amount of data {Modbus.MaximumDiscreteRequestResponseSize} coils.";
					throw new ArgumentOutOfRangeException(nameof(NumberOfPoints), msg);
				}

				MessageImpl.NumberOfPoints = value;
			}
		}

		public override string ToString()
		{
			string msg = $"Read {NumberOfPoints} {(FunctionCode == ModbusFunctionCodes.ReadCoils ? "coils" : "inputs")} starting at address {StartAddress}.";
			return msg;
		}

		public void ValidateResponse(IModbusMessage response)
		{
			var typedResponse = (ReadCoilsInputsResponse)response;

			// best effort validation - the same response for a request for 1 vs 6 coils (same byte count) will pass validation.
			var expectedByteCount = (NumberOfPoints + 7) / 8;

			if (expectedByteCount != typedResponse.ByteCount)
			{
				string msg = $"Unexpected byte count. Expected {expectedByteCount}, received {typedResponse.ByteCount}.";
				throw new IOException(msg);
			}
		}

		protected override void InitializeUnique(byte[] frame)
		{
			StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2));
			NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4));
		}
	}
}
