use crate::address_mode::AddressMode;
use crate::address_mode::AddressMode::*;
use crate::address_mode::AddressMode::*;
use crate::instruction_table::INSTRUCTION_TABLE;
use crate::op_info::OpInfo;
use crate::operand::Operand;
use crate::operation::Operation;
use crate::operation::Operation::*;
use log::{debug, trace};

#[derive(Debug, PartialEq)]
pub struct Instruction {
    pub op: Operation,
    pub mode: AddressMode,
    pub operand: Operand,
}

impl Instruction {
    pub fn opinfo(bytes: &[u8]) -> Option<OpInfo> {
        debug!("DECODING : {bytes:?}");
        let opcode = bytes.get(0).copied()?;
        Some(INSTRUCTION_TABLE[opcode as usize].clone())?
    }

    pub fn decode(bytes: &[u8]) -> Option<Instruction> {
        let info = Instruction::opinfo(bytes)?;

        let operand = match info.length {
            2 => Operand::Byte(*bytes.get(1)?),
            3 => Operand::Word(u16::from_le_bytes([*bytes.get(1)?, *bytes.get(2)?])),
            _ => Operand::None,
        };

        let instruction = Instruction {
            op: info.operation,
            mode: info.mode,
            operand,
        };

        Some(instruction)
    }
}
