namespace VRio.Core.Protocol;

/// <summary>One framed RIO message: the command plus its (checksum-stripped) payload.</summary>
public readonly struct RioPacket
{
    public RioCommand Command { get; }
    public byte[] Payload { get; }

    public RioPacket(RioCommand command, byte[] payload)
    {
        Command = command;
        Payload = payload ?? Array.Empty<byte>();
    }

    public override string ToString() =>
        Payload.Length == 0
            ? Command.ToString()
            : $"{Command} [{string.Join(" ", Payload.Select(b => b.ToString("X2")))}]";
}

/// <summary>What a fed byte produced (see <see cref="PacketParser"/>).</summary>
public enum RioRxKind
{
    /// <summary>A complete framed packet.</summary>
    Packet,

    /// <summary>A single byte outside framing: ACK/NAK/RESTART/IDLE or garbage.</summary>
    ControlByte,

    /// <summary>A high-bit byte arrived mid-packet; the packet was abandoned.</summary>
    FramingError,
}

/// <summary>A receive event emitted by <see cref="PacketParser"/>.</summary>
public readonly struct RioRxEvent
{
    public RioRxKind Kind { get; }
    public RioPacket Packet { get; }
    public byte Byte { get; }
    public bool ChecksumValid { get; }

    private RioRxEvent(RioRxKind kind, RioPacket packet, byte b, bool checksumValid)
    {
        Kind = kind;
        Packet = packet;
        Byte = b;
        ChecksumValid = checksumValid;
    }

    public static RioRxEvent ForPacket(RioPacket packet, bool checksumValid) =>
        new(RioRxKind.Packet, packet, 0, checksumValid);

    public static RioRxEvent ForControl(byte b) =>
        new(RioRxKind.ControlByte, default, b, false);

    public static RioRxEvent ForFramingError(byte b) =>
        new(RioRxKind.FramingError, default, b, false);
}
