use bitmask::bitmask;

use crate::{chip8_constants::CHIP8_MEMORY_SIZE, parts::Display::Chip8Display};

enum Chip8StartOffset {
    STANDARD,
    ETI600,
    OTHER,
}

struct Chip8Registers {
    V: [u8; 16],
    I: u16,
    DelayTimer: u16,
    SoundTimer: u16,
    StackPointer: u8,
    ProgramCounter: u16,
}

impl Default for Chip8Registers {
    fn default() -> Self {
        Chip8Registers {
            DelayTimer: 60,
            V: [
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00,
            ],
            I: 0x00,
            SoundTimer: 60,
            StackPointer: 255,
            ProgramCounter: 0x200,
        }
    }
}

enum Chip8Instruction {
    SYS(u16),
    CLS,
    RET,
    JMP(u16),
    CALL(u16),
    SNEQ(u8, u16),
    SNNE(u8, u8),
    SNRE(u8, u8),
    STOR(u8, u16),
    ADD(u8, u16),
    MOV(u8, u8),
    OR(u8, u8),
    AND(u8, u8),
    XOR(u8, u8),
    ADC(u8, u8),
    SBC(u8, u8),
    RSR(u8, u8),
    LSR(u8, u8),
    SNE(u8, u8),
    STO(i16),
    JMPI(u16),
    RND(u8, u8),
    SETD(u8),
    SETT(u8),
    BCD(u8),
}

enum Chip8Keys {
    KEY0,
    KEY1,
    KEY2,
    KEY3,
    KEY4,
    KEY5,
    KEY6,
    KEY7,
    KEY8,
    KEY9,
    KEYA,
    KEYB,
    KEYC,
    KEYD,
    KEYE,
    KEYF,
}

pub fn display_video_memory(system_memory: [u8; CHIP8_MEMORY_SIZE as usize]) {
    // Assumes memory addresses from
}

const ZERO: u16 = 0b0000000000000000;
const BOTTOM_BYTE: u16 = 0b0000000011111111;
const TOP_BYTE: u16 = 0b1111111100000000;
const NIBBLE0: u16 = 0b1111000000000000;
const NIBBLE1: u16 = 0b0000111100000000;
const NIBBLE2: u16 = 0b0000000011110000;
const NIBBLE3: u16 = 0b0000000000001111;
const NIBBLE0_BALANCE: u16 = 0x0FFF;
const PMSK_0000: i16 = 0x0000;
const PMSK_00X0: i16 = 0x00F0;
const PMSK_0X00: i16 = 0x0F00;
const PMSK_0XXX: i16 = 0x0FFF;

// AND against the possible operand to find which one we have
bitmask! {
    mask Chip8Instructions: u16 where flags Chip8InstructionFlags {
        SYS       = 0x00,
        CLR       = 0b0000000011100000,
        RTS       = 0b0000000011101110,
        JUMP      = 0b0001000000000000,
        CALL      = 0b0010000000000000,
        SKE       = 0b0011000000000000,
        SKNE      = 0b0100000000000000,
        SKRE      = 0b0101000000000000,
        LOAD      = 0b0110000000000000,
        ADD       = 0b0111000000000000,
        MOVE      = 0b1000000000000000,
        OR        = 0b1000000000000001,
        AND       = 0b1000000000000010,
        XOR       = 0b1000000000000011,
        ADDR      = 0b1000000000000100,
        SUB       = 0b1000000000000101,
        SHR       = 0b1000000000000110,
        SHL       = 0b1000000000001110,
        SKRNE     = 0b1001000000000000,
        LOADI     = 0b1010000000000000,
        JUMPI     = 0b1011000000000000,
        RAND      = 0b1100000000000000,
        DRAW      = 0b1101000000000000,
        SKPR      = 0b1110000000001110,
        SKUP      = 0b1111000010100001,
        MOVED     = 0b1111000000000111, 
        KEYD      = 0b1111000000001010,  
        LOADD     = 0b1111000000010101,  
        LOADS     = 0b1111000000011000, 
        ADDI      = 0b1111000000011110, 
        LDSPR     = 0b1111000000101001,  
        BCD       = 0b1111000000110011, 
        STOR      = 0b1111000001010101,  
        MEMCPY    = 0b1111000001100101   
    }
}

enum Chip8Asm {
    SYS(u16),
    CLR(),
    RTS(),
    JUMP(u16),
    CALL(u16),
    SKE(u8, u8),
    SKNE(u8, u8),
    SKRE(u8, u8),
    LOAD(u8, u8),
    ADD(u8, u8),
    MOVE(u8, u8),
    OR(u8, u8),
    AND(u8, u8),
    XOR(u8, u8),
    ADDR(u8, u8),
    SUB(u8, u8),
    SHR(u8, u8),
    SHL(u8, u8),
    SKRNE(u8, u8),
    LOADI(u16),
    JUMPI(u16),
    RAND(u8, u8),
    DRAW(u8, u8, u8),
    SKPR(u8),
    SKUP(u8),
    MOVED(u8),
    KEYD(u8),
    LOADD(u8),
    LOADS(u8),
    ADDI(u8),
    LDSPR(u8),
    BCD(u8),
    STOR(u8),
    READ(u8),
}

struct Chip8Cpu {}

struct Chip8System {
    registers: Chip8Registers,
    system_memory: [u8; 2048]
}

impl Chip8System {
    pub fn tick(mut self) {
        println!(" Ticking Chip8System");

        let next_instruction = self.system_memory[self.registers.ProgramCounter as usize] as u16;
        println!("READ INSTRUCTION {next_instruction}");
        self.registers.ProgramCounter += 0x2;

        &self.delay_timer_tick();
        &self.sound_timer_tick();

        // self.screen.tick();
    }

    fn delay_timer_tick(&mut self) {
        self.registers.DelayTimer = self.registers.DelayTimer - 1;

    }

    fn sound_timer_tick(&mut self) {
        self.registers.SoundTimer = self.registers.SoundTimer - 1;

    }
}
