#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorKind {
    InvalidInstruction,
    Unimplemented,
    RegisterArrayOverflow,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Error {
    pub kind: ErrorKind,
    pub index: usize,
}

pub fn validate(mut program: &[u8]) -> Result<(), Error> {
    use hbbytecode::opcode::*;

    let start = program;
    loop {
        program = match program {
            [] => return Ok(()),
            [LD..=ST, reg, _, _, _, _, _, _, _, _, _, count, ..]
                if usize::from(*reg) * 8 + usize::from(*count) > 2048 =>
            {
                return Err(Error {
                    kind: ErrorKind::RegisterArrayOverflow,
                    index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
                })
            }
            [BRC, src, dst, count, ..]
                if src.checked_add(*count).is_none() || dst.checked_add(*count).is_none() =>
            {
                return Err(Error {
                    kind: ErrorKind::RegisterArrayOverflow,
                    index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
                })
            }
            [NOP | ECALL, rest @ ..]
            | [DIR | DIRF, _, _, _, _, rest @ ..]
            | [ADD..=CMPU | BRC | ADDF..=MULF, _, _, _, rest @ ..]
            | [NEG..=NOT | CP..=SWA, _, _, rest @ ..]
            | [LI | JMP, _, _, _, _, _, _, _, _, _, rest @ ..]
            | [ADDI..=CMPUI | BMC | JEQ..=JGTU | ADDFI..=MULFI, _, _, _, _, _, _, _, _, _, _, rest @ ..]
            | [LD..=ST, _, _, _, _, _, _, _, _, _, _, _, _, rest @ ..] => rest,
            _ => {
                return Err(Error {
                    kind: ErrorKind::InvalidInstruction,
                    index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
                })
            }
        }
    }
}