use crate::{
    get_register, get_register_pair, set_register, structs::set_register_pair, EmulatorState,
    Register,
};

/// Move (copy) value from source to destination register
pub fn mov(src: Register, dest: Register, state: &mut EmulatorState) {
    let data = get_register(src, state);
    set_register(dest, data, state);
}

/// Move immediate into destination register
pub fn mvi(byte: u8, dest: Register, state: &mut EmulatorState) {
    set_register(dest, byte, state);
}

/// Store accumulator using the BC or DE register pair
pub fn stax(register: Register, state: &mut EmulatorState) {
    let address = get_register_pair(register, state);
    state.memory[address as usize] = state.a;
}

/// Load accumulator using the BC or DE register pair
pub fn ldax(register: Register, state: &mut EmulatorState) {
    let address = get_register_pair(register, state);
    state.a = state.memory[address as usize];
}

/// Store a 16-bit word from H and L to the specified address
pub fn shld(address: u16, state: &mut EmulatorState) {
    state.memory[address as usize] = state.l;
    state.memory[address as usize + 1] = state.h;
}

/// Load a 16-bit word into H and L from the specified address
pub fn lhld(address: u16, state: &mut EmulatorState) {
    state.l = state.memory[address as usize];
    state.h = state.memory[address as usize + 1];
}

/* STACK */
/// Push a 16-bit value from a register pair onto the stack
pub fn push_reg(register: Register, state: &mut EmulatorState) {
    push(get_register_pair(register, state), state);
}

pub fn push(value: u16, state: &mut EmulatorState) {
    state.write_word(state.sp.overflowing_sub(1).0, value);
    (state.sp, ..) = state.sp.overflowing_sub(2);
}

/// Pop a 16-bit value from the stack into a register pair
pub fn pop_reg(register: Register, state: &mut EmulatorState) {
    set_register_pair(register, pop(state), state);
}

pub fn pop(state: &mut EmulatorState) -> u16 {
    (state.sp, ..) = state.sp.overflowing_add(2);
    state.read_word(state.sp.overflowing_sub(1).0)
}

#[cfg(test)]
mod tests {
    use crate::EmulatorState;

    #[test]
    fn push() {
        let mut state = EmulatorState {
            sp: 0x12,
            ..Default::default()
        };

        super::push(0x1234, &mut state);
        assert_eq!(state.sp, 0x10);
        assert_eq!(state.memory[0x11..=0x12], [0x34, 0x12]);
    }

    #[test]
    fn pop() {
        let mut state = EmulatorState {
            sp: 0x10,
            ..Default::default()
        };

        state.memory[0x11] = 0x34;
        state.memory[0x12] = 0x12;
        assert_eq!(super::pop(&mut state), 0x1234);
    }
}
