namespace RioJoy.Core.Protocol;

/// <summary>
/// Builds outbound (PC → RIO) packets: <c>[command][payload…][checksum]</c>.
/// Port of <c>SendCommand</c> (riovjoy2.cpp#L1217), which copies the command
/// plus the command's fixed number of payload bytes, accumulating the 7-bit
/// checksum, then appends the checksum byte.
/// </summary>
public static class PacketBuilder
{
    /// <summary>
    /// Build a wire-ready packet for <paramref name="command"/> with the given
    /// <paramref name="payload"/>. The payload length must match the command's
    /// entry in the length table.
    /// </summary>
    public static byte[] Build(RioCommand command, byte[] payload)
    {
        if (payload is null) throw new ArgumentNullException(nameof(payload));
        int expected = RioCommandTable.PayloadLength(command);
        if (payload.Length != expected)
            throw new ArgumentException(
                $"{command} expects a {expected}-byte payload, got {payload.Length}.",
                nameof(payload));

        var packet = new byte[1 + expected + 1];
        packet[0] = (byte)command;
        Array.Copy(payload, 0, packet, 1, expected);
        packet[packet.Length - 1] = RioChecksum.Compute(packet, 0, 1 + expected);
        return packet;
    }

    /// <summary>Build a zero-payload command packet (CheckRequest etc.).</summary>
    public static byte[] Build(RioCommand command) => Build(command, EmptyPayload);

    private static readonly byte[] EmptyPayload = new byte[0];

    // --- Convenience builders for the PC → RIO commands ---------------------

    public static byte[] CheckRequest() => Build(RioCommand.CheckRequest);

    public static byte[] VersionRequest() => Build(RioCommand.VersionRequest);

    public static byte[] AnalogRequest() => Build(RioCommand.AnalogRequest);

    public static byte[] ResetRequest(RioResetTarget target) =>
        Build(RioCommand.ResetRequest, new[] { (byte)target });

    /// <summary>
    /// LampRequest: payload is <c>[lamp#, state]</c>. The state byte is a
    /// 7-bit lamp-state value (see <see cref="RioLampState"/>).
    /// </summary>
    public static byte[] LampRequest(byte lampNumber, byte state) =>
        Build(RioCommand.LampRequest, new[] { lampNumber, state });
}
