use crate::instruction::Instruction;
use crate::mode::Mode;

pub type OpCode = Option<(Instruction, Mode)>;

static OP_CODES: [OpCode; 256] = [
    Some((Instruction::Break, Mode::Implied)),
    Some((Instruction::OrWithAccumulator, Mode::XIndirect)),
    None,
    None,
    None,
    Some((Instruction::OrWithAccumulator, Mode::ZeroPage)),
    Some((Instruction::ArithmeticShiftLeft, Mode::ZeroPage)),
    None,
    Some((Instruction::PushProcessorStatus, Mode::Implied)),
    Some((Instruction::OrWithAccumulator, Mode::Immediate)),
    Some((Instruction::ArithmeticShiftLeft, Mode::Accumulator)),
    None,
    None,
    Some((Instruction::OrWithAccumulator, Mode::Absolute)),
    Some((Instruction::ArithmeticShiftLeft, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfPlus, Mode::Relative)),
    Some((Instruction::OrWithAccumulator, Mode::IndirectY)),
    None,
    None,
    None,
    Some((Instruction::OrWithAccumulator, Mode::ZeroPageX)),
    Some((Instruction::ArithmeticShiftLeft, Mode::ZeroPageX)),
    None,
    Some((Instruction::ClearCarry, Mode::Implied)),
    Some((Instruction::OrWithAccumulator, Mode::AbsoluteY)),
    None,
    None,
    None,
    Some((Instruction::OrWithAccumulator, Mode::AbsoluteX)),
    Some((Instruction::ArithmeticShiftLeft, Mode::AbsoluteX)),
    None,
    Some((Instruction::JumpSubroutine, Mode::Absolute)),
    Some((Instruction::AndWithAccumulator, Mode::XIndirect)),
    None,
    None,
    Some((Instruction::BitSet, Mode::ZeroPage)),
    Some((Instruction::AndWithAccumulator, Mode::ZeroPage)),
    Some((Instruction::RotateLeft, Mode::ZeroPage)),
    None,
    Some((Instruction::PullProcessorStatus, Mode::Implied)),
    Some((Instruction::AndWithAccumulator, Mode::Immediate)),
    Some((Instruction::RotateLeft, Mode::Accumulator)),
    None,
    Some((Instruction::BitSet, Mode::Absolute)),
    Some((Instruction::AndWithAccumulator, Mode::Absolute)),
    Some((Instruction::RotateLeft, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfMinus, Mode::Relative)),
    Some((Instruction::AndWithAccumulator, Mode::IndirectY)),
    None,
    None,
    None,
    Some((Instruction::AndWithAccumulator, Mode::ZeroPageX)),
    Some((Instruction::RotateLeft, Mode::ZeroPageX)),
    None,
    Some((Instruction::SetCarry, Mode::Implied)),
    Some((Instruction::AndWithAccumulator, Mode::AbsoluteY)),
    None,
    None,
    None,
    Some((Instruction::AndWithAccumulator, Mode::AbsoluteX)),
    Some((Instruction::RotateLeft, Mode::AbsoluteX)),
    None,
    Some((Instruction::ReturnFromInterrupt, Mode::Implied)),
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::XIndirect)),
    None,
    None,
    None,
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::ZeroPage)),
    Some((Instruction::LogicalShiftRight, Mode::ZeroPage)),
    None,
    Some((Instruction::PushAccumulator, Mode::Implied)),
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::Immediate)),
    Some((Instruction::LogicalShiftRight, Mode::Accumulator)),
    None,
    Some((Instruction::Jump, Mode::Absolute)),
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::Absolute)),
    Some((Instruction::LogicalShiftRight, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfOverflowClear, Mode::Relative)),
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::IndirectY)),
    None,
    None,
    None,
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::ZeroPageX)),
    Some((Instruction::LogicalShiftRight, Mode::ZeroPageX)),
    None,
    Some((Instruction::ClearInterrupt, Mode::Implied)),
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::AbsoluteY)),
    None,
    None,
    None,
    Some((Instruction::ExclusiveOrWithAccumulator, Mode::AbsoluteX)),
    Some((Instruction::LogicalShiftRight, Mode::AbsoluteX)),
    None,
    Some((Instruction::ReturnFromSubroutine, Mode::Implied)),
    Some((Instruction::AddWithCarry, Mode::XIndirect)),
    None,
    None,
    None,
    Some((Instruction::AddWithCarry, Mode::ZeroPage)),
    Some((Instruction::RotateRight, Mode::ZeroPage)),
    None,
    Some((Instruction::PullAccumulator, Mode::Implied)),
    Some((Instruction::AddWithCarry, Mode::Immediate)),
    Some((Instruction::RotateRight, Mode::Accumulator)),
    None,
    Some((Instruction::Jump, Mode::Indirect)),
    Some((Instruction::AddWithCarry, Mode::Absolute)),
    Some((Instruction::RotateRight, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfOverflowSet, Mode::Relative)),
    Some((Instruction::AddWithCarry, Mode::IndirectY)),
    None,
    None,
    None,
    Some((Instruction::AddWithCarry, Mode::ZeroPageX)),
    Some((Instruction::RotateRight, Mode::ZeroPageX)),
    None,
    Some((Instruction::SetInterruptDisable, Mode::Implied)),
    Some((Instruction::AddWithCarry, Mode::AbsoluteY)),
    None,
    None,
    None,
    Some((Instruction::AddWithCarry, Mode::AbsoluteX)),
    Some((Instruction::RotateRight, Mode::AbsoluteX)),
    None,
    None,
    Some((Instruction::StoreAccumulator, Mode::XIndirect)),
    None,
    None,
    Some((Instruction::StoreY, Mode::ZeroPage)),
    Some((Instruction::StoreAccumulator, Mode::ZeroPage)),
    Some((Instruction::StoreX, Mode::ZeroPage)),
    None,
    Some((Instruction::DecrementY, Mode::Implied)),
    None,
    Some((Instruction::TransferXToAccumulator, Mode::Accumulator)),
    None,
    Some((Instruction::StoreY, Mode::Absolute)),
    Some((Instruction::StoreAccumulator, Mode::Absolute)),
    Some((Instruction::StoreX, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfCarryClear, Mode::Relative)),
    Some((Instruction::StoreAccumulator, Mode::IndirectY)),
    None,
    None,
    Some((Instruction::StoreY, Mode::ZeroPageX)),
    Some((Instruction::StoreAccumulator, Mode::ZeroPageX)),
    Some((Instruction::StoreX, Mode::ZeroPageY)),
    None,
    Some((Instruction::TransferYToAccumulator, Mode::Implied)),
    Some((Instruction::StoreAccumulator, Mode::AbsoluteY)),
    Some((Instruction::TransferXToStackPointer, Mode::Accumulator)),
    None,
    None,
    Some((Instruction::StoreAccumulator, Mode::AbsoluteX)),
    None,
    None,
    Some((Instruction::LoadY, Mode::Immediate)),
    Some((Instruction::LoadAccumulator, Mode::XIndirect)),
    Some((Instruction::LoadX, Mode::Immediate)),
    None,
    Some((Instruction::LoadY, Mode::ZeroPage)),
    Some((Instruction::LoadAccumulator, Mode::ZeroPage)),
    Some((Instruction::LoadX, Mode::ZeroPage)),
    None,
    Some((Instruction::TransferAccumulatorToY, Mode::Implied)),
    Some((Instruction::LoadAccumulator, Mode::Immediate)),
    Some((Instruction::TransferAccumulatorToX, Mode::Accumulator)),
    None,
    Some((Instruction::LoadY, Mode::Absolute)),
    Some((Instruction::LoadAccumulator, Mode::Absolute)),
    Some((Instruction::LoadX, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfCarrySet, Mode::Relative)),
    Some((Instruction::LoadAccumulator, Mode::IndirectY)),
    None,
    None,
    Some((Instruction::LoadY, Mode::ZeroPageX)),
    Some((Instruction::LoadAccumulator, Mode::ZeroPageX)),
    Some((Instruction::LoadX, Mode::ZeroPageY)),
    None,
    Some((Instruction::ClearOverflow, Mode::Implied)),
    Some((Instruction::LoadAccumulator, Mode::AbsoluteY)),
    Some((Instruction::TransferStackPointerToX, Mode::Accumulator)),
    None,
    Some((Instruction::LoadY, Mode::AbsoluteX)),
    Some((Instruction::LoadAccumulator, Mode::AbsoluteX)),
    Some((Instruction::LoadX, Mode::AbsoluteY)),
    None,
    Some((Instruction::CompareWithY, Mode::Immediate)),
    Some((Instruction::CompareWithAccumulator, Mode::XIndirect)),
    None,
    None,
    Some((Instruction::CompareWithY, Mode::ZeroPage)),
    Some((Instruction::CompareWithAccumulator, Mode::ZeroPage)),
    Some((Instruction::Decrement, Mode::ZeroPage)),
    None,
    Some((Instruction::IncrementY, Mode::Implied)),
    Some((Instruction::CompareWithAccumulator, Mode::Immediate)),
    Some((Instruction::DecrementX, Mode::Accumulator)),
    None,
    Some((Instruction::CompareWithY, Mode::Absolute)),
    Some((Instruction::CompareWithAccumulator, Mode::Absolute)),
    Some((Instruction::Decrement, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfNotEqual, Mode::Relative)),
    Some((Instruction::CompareWithAccumulator, Mode::IndirectY)),
    None,
    None,
    None,
    Some((Instruction::CompareWithAccumulator, Mode::ZeroPageX)),
    Some((Instruction::Decrement, Mode::ZeroPageX)),
    None,
    Some((Instruction::ClearDecimal, Mode::Implied)),
    Some((Instruction::CompareWithAccumulator, Mode::AbsoluteY)),
    None,
    None,
    None,
    Some((Instruction::CompareWithAccumulator, Mode::AbsoluteX)),
    Some((Instruction::Decrement, Mode::AbsoluteX)),
    None,
    Some((Instruction::CompareWithX, Mode::Immediate)),
    Some((Instruction::SubtractWithCarry, Mode::XIndirect)),
    None,
    None,
    Some((Instruction::CompareWithX, Mode::ZeroPage)),
    Some((Instruction::SubtractWithCarry, Mode::ZeroPage)),
    Some((Instruction::Increment, Mode::ZeroPage)),
    None,
    Some((Instruction::IncrementX, Mode::Implied)),
    Some((Instruction::SubtractWithCarry, Mode::Immediate)),
    Some((Instruction::NoOperation, Mode::Accumulator)),
    None,
    Some((Instruction::CompareWithX, Mode::Absolute)),
    Some((Instruction::SubtractWithCarry, Mode::Absolute)),
    Some((Instruction::Increment, Mode::Absolute)),
    None,
    Some((Instruction::BranchIfEqual, Mode::Relative)),
    Some((Instruction::SubtractWithCarry, Mode::IndirectY)),
    None,
    None,
    None,
    Some((Instruction::SubtractWithCarry, Mode::ZeroPageX)),
    Some((Instruction::Increment, Mode::ZeroPageX)),
    None,
    Some((Instruction::SetDecimal, Mode::Implied)),
    Some((Instruction::SubtractWithCarry, Mode::AbsoluteY)),
    None,
    None,
    None,
    Some((Instruction::SubtractWithCarry, Mode::AbsoluteX)),
    Some((Instruction::Increment, Mode::AbsoluteX)),
    None,
];

pub fn get(opcode: u8) -> OpCode {
    OP_CODES[opcode as usize]
}
