use crate::emulate::*;
use crate::jxx::*;
use crate::single_operand::*;
use crate::two_operand::*;

use std::fmt;

/// A container that holds all types of instructions (including emulated)
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Instruction {
    // single operand instructions
    Rrc(Rrc),
    Swpb(Swpb),
    Rra(Rra),
    Sxt(Sxt),
    Push(Push),
    Call(Call),
    Reti(Reti),

    // Jxx instructions
    Jnz(Jnz),
    Jz(Jz),
    Jlo(Jlo),
    Jc(Jc),
    Jn(Jn),
    Jge(Jge),
    Jl(Jl),
    Jmp(Jmp),

    // two operand instructions
    Mov(Mov),
    Add(Add),
    Addc(Addc),
    Subc(Subc),
    Sub(Sub),
    Cmp(Cmp),
    Dadd(Dadd),
    Bit(Bit),
    Bic(Bic),
    Bis(Bis),
    Xor(Xor),
    And(And),

    // emulated
    Adc(Adc),
    Br(Br),
    Clr(Clr),
    Clrc(Clrc),
    Clrn(Clrn),
    Clrz(Clrz),
    Dadc(Dadc),
    Dec(Dec),
    Decd(Decd),
    Dint(Dint),
    Eint(Eint),
    Inc(Inc),
    Incd(Incd),
    Inv(Inv),
    Nop(Nop),
    Pop(Pop),
    Ret(Ret),
    Rla(Rla),
    Rlc(Rlc),
    Sbc(Sbc),
    Setc(Setc),
    Setn(Setn),
    Setz(Setz),
    Tst(Tst),
}

impl Instruction {
    pub fn size(&self) -> usize {
        match self {
            Self::Rrc(inst) => inst.size(),
            Self::Swpb(inst) => inst.size(),
            Self::Rra(inst) => inst.size(),
            Self::Sxt(inst) => inst.size(),
            Self::Push(inst) => inst.size(),
            Self::Call(inst) => inst.size(),
            Self::Reti(inst) => inst.size(),
            Self::Jnz(inst) => inst.size(),
            Self::Jz(inst) => inst.size(),
            Self::Jlo(inst) => inst.size(),
            Self::Jc(inst) => inst.size(),
            Self::Jn(inst) => inst.size(),
            Self::Jge(inst) => inst.size(),
            Self::Jl(inst) => inst.size(),
            Self::Jmp(inst) => inst.size(),
            Self::Mov(inst) => inst.size(),
            Self::Add(inst) => inst.size(),
            Self::Addc(inst) => inst.size(),
            Self::Subc(inst) => inst.size(),
            Self::Sub(inst) => inst.size(),
            Self::Cmp(inst) => inst.size(),
            Self::Dadd(inst) => inst.size(),
            Self::Bit(inst) => inst.size(),
            Self::Bic(inst) => inst.size(),
            Self::Bis(inst) => inst.size(),
            Self::Xor(inst) => inst.size(),
            Self::And(inst) => inst.size(),
            Self::Adc(inst) => inst.size(),
            Self::Br(inst) => inst.size(),
            Self::Clr(inst) => inst.size(),
            Self::Clrc(inst) => inst.size(),
            Self::Clrn(inst) => inst.size(),
            Self::Clrz(inst) => inst.size(),
            Self::Dadc(inst) => inst.size(),
            Self::Dec(inst) => inst.size(),
            Self::Decd(inst) => inst.size(),
            Self::Dint(inst) => inst.size(),
            Self::Eint(inst) => inst.size(),
            Self::Inc(inst) => inst.size(),
            Self::Incd(inst) => inst.size(),
            Self::Inv(inst) => inst.size(),
            Self::Nop(inst) => inst.size(),
            Self::Pop(inst) => inst.size(),
            Self::Ret(inst) => inst.size(),
            Self::Rla(inst) => inst.size(),
            Self::Rlc(inst) => inst.size(),
            Self::Sbc(inst) => inst.size(),
            Self::Setc(inst) => inst.size(),
            Self::Setn(inst) => inst.size(),
            Self::Setz(inst) => inst.size(),
            Self::Tst(inst) => inst.size(),
        }
    }
}

impl fmt::Display for Instruction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Rrc(inst) => write!(f, "{}", inst),
            Self::Swpb(inst) => write!(f, "{}", inst),
            Self::Rra(inst) => write!(f, "{}", inst),
            Self::Sxt(inst) => write!(f, "{}", inst),
            Self::Push(inst) => write!(f, "{}", inst),
            Self::Call(inst) => write!(f, "{}", inst),
            Self::Reti(inst) => write!(f, "{}", inst),
            Self::Jnz(inst) => write!(f, "{}", inst),
            Self::Jz(inst) => write!(f, "{}", inst),
            Self::Jlo(inst) => write!(f, "{}", inst),
            Self::Jc(inst) => write!(f, "{}", inst),
            Self::Jn(inst) => write!(f, "{}", inst),
            Self::Jge(inst) => write!(f, "{}", inst),
            Self::Jl(inst) => write!(f, "{}", inst),
            Self::Jmp(inst) => write!(f, "{}", inst),
            Self::Mov(inst) => write!(f, "{}", inst),
            Self::Add(inst) => write!(f, "{}", inst),
            Self::Addc(inst) => write!(f, "{}", inst),
            Self::Subc(inst) => write!(f, "{}", inst),
            Self::Sub(inst) => write!(f, "{}", inst),
            Self::Cmp(inst) => write!(f, "{}", inst),
            Self::Dadd(inst) => write!(f, "{}", inst),
            Self::Bit(inst) => write!(f, "{}", inst),
            Self::Bic(inst) => write!(f, "{}", inst),
            Self::Bis(inst) => write!(f, "{}", inst),
            Self::Xor(inst) => write!(f, "{}", inst),
            Self::And(inst) => write!(f, "{}", inst),
            Self::Adc(inst) => write!(f, "{}", inst),
            Self::Br(inst) => write!(f, "{}", inst),
            Self::Clr(inst) => write!(f, "{}", inst),
            Self::Clrc(inst) => write!(f, "{}", inst),
            Self::Clrn(inst) => write!(f, "{}", inst),
            Self::Clrz(inst) => write!(f, "{}", inst),
            Self::Dadc(inst) => write!(f, "{}", inst),
            Self::Dec(inst) => write!(f, "{}", inst),
            Self::Decd(inst) => write!(f, "{}", inst),
            Self::Dint(inst) => write!(f, "{}", inst),
            Self::Eint(inst) => write!(f, "{}", inst),
            Self::Inc(inst) => write!(f, "{}", inst),
            Self::Incd(inst) => write!(f, "{}", inst),
            Self::Inv(inst) => write!(f, "{}", inst),
            Self::Nop(inst) => write!(f, "{}", inst),
            Self::Pop(inst) => write!(f, "{}", inst),
            Self::Ret(inst) => write!(f, "{}", inst),
            Self::Rla(inst) => write!(f, "{}", inst),
            Self::Rlc(inst) => write!(f, "{}", inst),
            Self::Sbc(inst) => write!(f, "{}", inst),
            Self::Setc(inst) => write!(f, "{}", inst),
            Self::Setn(inst) => write!(f, "{}", inst),
            Self::Setz(inst) => write!(f, "{}", inst),
            Self::Tst(inst) => write!(f, "{}", inst),
        }
    }
}
