#![allow(clippy::useless_format)]
#![allow(clippy::too_many_lines)]
use std::{env, fs};

const REGISTERS: [&str; 8] = ["B", "C", "D", "E", "H", "L", "M", "A"];
const PRINT_MEMLOC: bool = true;

fn main() {
    let mut args = env::args();
    let filename = args
        .nth(1)
        .expect("Provide a path to a file to disassemble as an argument");
    let file = fs::read(filename).expect("where file");
    let mut data = file.iter();
    let mut index: u32 = 0;

    while let Some(byte) = data.next() {
        let current_index = index;
        index += 1;

        let mut next = |len: u8| {
            let mut res = String::new();
            index += len as u32;

            for _ in 0..len {
                res.insert_str(
                    0,
                    format!("{:02x}", data.next().expect("Expected data")).as_str(),
                );
            }

            res
        };

        // http://www.emulator101.com/reference/8080-by-opcode.html

        let instruction: String = match byte {
            0x00 => format!("NOP"),
            0x01 => format!("LXI    B,#${}", next(2)),
            0x02 => format!("STAX   B"),
            0x03 => format!("INX    B"),
            0x04 => format!("INR    B"),
            0x05 => format!("DCR    B"),
            0x06 => format!("MVI    B,#${}", next(1)),
            0x07 => format!("RLC"),
            0x09 => format!("DAD    B"),
            0x0a => format!("LDAX   B"),
            0x0b => format!("DCX    B"),
            0x0c => format!("INR    C"),
            0x0d => format!("DCR    C"),
            0x0e => format!("MVI    C,#${}", next(1)),
            0x0f => format!("RRC"),
            0x11 => format!("LXI    D,#${}", next(2)),
            0x12 => format!("STAX   D"),
            0x13 => format!("INX    D"),
            0x14 => format!("INR    D"),
            0x15 => format!("DCR    D"),
            0x16 => format!("MVI    D,#${}", next(1)),
            0x17 => format!("RAL"),
            0x19 => format!("DAD    D"),
            0x1a => format!("LDAX   D"),
            0x1b => format!("DCX    D"),
            0x1c => format!("INR    E"),
            0x1d => format!("DCR    E"),
            0x1e => format!("MVI    E,#${}", next(1)),
            0x1f => format!("RAR"),
            0x21 => format!("LXI    H,#${}", next(2)),
            0x22 => format!("SLHD   #${}", next(2)),
            0x23 => format!("INX    H"),
            0x24 => format!("INR    H"),
            0x25 => format!("DCR    H"),
            0x26 => format!("MVI    H,#${}", next(1)),
            0x27 => format!("DAA"),
            0x29 => format!("DAD    H"),
            0x2a => format!("LHLD   ${}", next(2)),
            0x2b => format!("DCX    H"),
            0x2c => format!("INR    L"),
            0x2d => format!("DCR    L"),
            0x2e => format!("MVI    L,#${}", next(1)),
            0x2f => format!("CMA"),
            0x31 => format!("LXI    SP,#${}", next(2)),
            0x32 => format!("STA    ${}", next(2)),
            0x33 => format!("INX    SP"),
            0x34 => format!("INR    M"),
            0x35 => format!("DCR    M"),
            0x36 => format!("MVI    M,#${}", next(1)),
            0x37 => format!("STC"),
            0x39 => format!("DAD    SP"),
            0x3a => format!("LDA    ${}", next(2)),
            0x3b => format!("DCX    SP"),
            0x3c => format!("INR    A"),
            0x3d => format!("DCR    A"),
            0x3e => format!("MVI    A,#${}", next(1)),
            0x3f => format!("CMC"),
            0x76 => format!("HLT             ; Trigger interrupt"),
            0x40..=0x7f // Test this!
                 => format!("MOV    {},{}", REGISTERS[(byte & 0b11_1000) as usize >> 3], REGISTERS[(byte & 0b111) as usize]),
            0x80..=0x87
                 => format!("ADD    {}", REGISTERS[(byte & 0b111) as usize]),
            0x88..=0x8f
                 => format!("ADC    {}", REGISTERS[(byte & 0b111) as usize]),
            0x90..=0x97
                 => format!("SUB    {}", REGISTERS[(byte & 0b111) as usize]),
            0x98..=0x9f
                 => format!("ABB    {}", REGISTERS[(byte & 0b111) as usize]),
            0x80..=0xa7
                 => format!("ANA    {}", REGISTERS[(byte & 0b111) as usize]),
            0xa8..=0xaf
                 => format!("XRA    {}", REGISTERS[(byte & 0b111) as usize]),
            0xb0..=0xb7
                 => format!("ORA    {}", REGISTERS[(byte & 0b111) as usize]),
            0xb8..=0xbf
                 => format!("CMP    {}", REGISTERS[(byte & 0b111) as usize]),
            0xc0 => format!("RNZ"),
            0xc1 => format!("POP    B"),
            0xc2 => format!("JNZ    ${}", next(2)),
            0xc3 => format!("JMP    ${}", next(2)),
            0xc4 => format!("CNZ    ${}", next(2)),
            0xc5 => format!("PUSH   B"),
            0xc6 => format!("ADI    #${}", next(1)),
            0xc7 => format!("RST    0        ; CALL $0"),
            0xc8 => format!("RZ"),
            0xc9 => format!("RET"),
            0xca => format!("JZ     ${}", next(2)),
            0xcc => format!("CZ     ${}", next(2)),
            0xcd => format!("CALL   ${}", next(2)),
            0xce => format!("ACI    #${}", next(1)),
            0xcf => format!("RST    1        ; CALL $8"),
            0xd0 => format!("RNC"),
            0xd1 => format!("POP    D"),
            0xd2 => format!("JNC    ${}", next(2)),
            0xd3 => format!("OUT    #${}", next(1)),
            0xd4 => format!("CNC    ${}", next(2)),
            0xd5 => format!("PUSH   D"),
            0xd6 => format!("SUI    #${}", next(1)),
            0xd7 => format!("RST    2        ; CALL $10"),
            0xd8 => format!("RC"),
            0xda => format!("JC     ${}", next(2)),
            0xdb => format!("IN     #${}", next(1)),
            0xdc => format!("CC     ${}", next(2)),
            0xde => format!("SBI    #${}", next(1)),
            0xdf => format!("RST    3        ; $CALL 18"),
            0xe0 => format!("RPO"),
            0xe1 => format!("POP    H"),
            0xe2 => format!("JPO    ${}", next(2)),
            0xe3 => format!("XTHL"),
            0xe4 => format!("CPO    ${}", next(2)),
            0xe5 => format!("PUSH   H"),
            0xe6 => format!("ANI    #${}", next(1)),
            0xe7 => format!("RST    4        ; CALL $20"),
            0xe8 => format!("RPE"),
            0xe9 => format!("PCHL"),
            0xea => format!("JPE    ${}", next(2)),
            0xeb => format!("XCHG"),
            0xec => format!("CPE    ${}", next(2)),
            0xee => format!("XRI    #${}", next(1)),
            0xef => format!("RST    5        ; CALL $28"),
            0xf0 => format!("RP"),
            0xf1 => format!("POP    PSW"),
            0xf2 => format!("JP     ${}", next(2)),
            0xf3 => format!("DI"),
            0xf4 => format!("CP     ${}", next(2)),
            0xf5 => format!("PUSH   PSW"),
            0xf6 => format!("ORI    #${}", next(1)),
            0xf7 => format!("RST    6        ; CALL $30"),
            0xf8 => format!("RM"),
            0xf9 => format!("SPHL"),
            0xfa => format!("JM     ${}", next(2)),
            0xfb => format!("EI"),
            0xfc => format!("CM     ${}", next(2)),
            0xfe => format!("CPI    #${}", next(1)),
            0xff => format!("RST    7        ; CALL $38"),
            _    => format!("; Unknown instruction {byte:#x}"),
            // _ => panic!("Unimplemented instruction {byte:#x}"),
        };

        if PRINT_MEMLOC {
            println!("{:04x} {}", current_index, instruction);
        } else {
            println!("{instruction}");
        }
    }
}
