
const LCD_ENABLED_BIT: u8 = 7;
const WINDOW_TILE_MAP_AREA_BIT: u8 = 6;
const WINDOW_ENABLE_BIT: u8 = 5;
const BG_AND_WINDOW_TILE_DATA_AREA_BIT: u8 = 4;
const BG_TILE_MAP_AREA_BIT: u8 = 3;
const OBJECT_SIZE_BIT: u8 = 2;
const OBJECT_ENABLE_BIT: u8 = 1;
const BG_AND_WINDOW_ENABLE_BIT: u8 = 0;
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct LCDControlRegister {
    pub(crate) lcd_enabled: bool,
    pub(crate) window_tile_map_area: bool,
    pub(crate) window_enable: bool,
    pub(crate) bg_and_window_tile_area: bool,
    pub(crate) bg_tile_map_area: bool,
    pub(crate) object_size: bool,
    pub(crate) object_enable: bool,
    pub(crate) bg_and_window_enable: bool,
}
impl From<LCDControlRegister> for u8  {
    fn from(flag: LCDControlRegister) -> u8 {
        (if flag.lcd_enabled            { 1 } else { 0 }) << LCD_ENABLED_BIT |
        (if flag.window_tile_map_area   { 1 } else { 0 }) << WINDOW_TILE_MAP_AREA_BIT |
        (if flag.window_enable          { 1 } else { 0 }) << WINDOW_ENABLE_BIT |
        (if flag.bg_and_window_tile_area{ 1 } else { 0 }) << BG_AND_WINDOW_TILE_DATA_AREA_BIT |
        (if flag.bg_tile_map_area       { 1 } else { 0 }) << BG_TILE_MAP_AREA_BIT |
        (if flag.object_size            { 1 } else { 0 }) << OBJECT_SIZE_BIT |
        (if flag.object_enable          { 1 } else { 0 }) << OBJECT_ENABLE_BIT |
        (if flag.bg_and_window_enable   { 1 } else { 0 }) << BG_AND_WINDOW_ENABLE_BIT
    }
}

impl From<u8> for LCDControlRegister {
    fn from(byte: u8) -> Self {
        let lcd_enabled = ((byte >> LCD_ENABLED_BIT) & 0b1) != 0;
        let window_tile_map_area = ((byte >> WINDOW_TILE_MAP_AREA_BIT) & 0b1) != 0;
        let window_enable = ((byte >> WINDOW_ENABLE_BIT) & 0b1) != 0;
        let bg_and_window_tile_area = ((byte >> BG_AND_WINDOW_TILE_DATA_AREA_BIT) & 0b1) != 0;
        let bg_tile_map_area = ((byte >> BG_TILE_MAP_AREA_BIT) & 0b1) != 0;
        let object_size = ((byte >> OBJECT_SIZE_BIT) & 0b1) != 0;
        let object_enable = ((byte >> OBJECT_ENABLE_BIT) & 0b1) != 0;
        let bg_and_window_enable = ((byte >> BG_AND_WINDOW_ENABLE_BIT) & 0b1) != 0;

        LCDControlRegister {
            lcd_enabled,
            window_tile_map_area,
            window_enable,
            bg_and_window_tile_area,
            bg_tile_map_area,
            object_size,
            object_enable,
            bg_and_window_enable,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct FlagsRegister {
    pub(crate) zero: bool,
    pub(crate) subtract: bool,
    pub(crate) half_carry: bool,
    pub(crate) carry: bool
}


const ZERO_FLAG_BYTE_POSITION: u8 = 7;
const SUBTRACT_FLAG_BYTE_POSITION: u8 = 6;
const HALF_CARRY_FLAG_BYTE_POSITION: u8 = 5;
const CARRY_FLAG_BYTE_POSITION: u8 = 4;

impl From<FlagsRegister> for u8  {
    fn from(flag: FlagsRegister) -> u8 {
        (if flag.zero       { 1 } else { 0 }) << ZERO_FLAG_BYTE_POSITION |
            (if flag.subtract   { 1 } else { 0 }) << SUBTRACT_FLAG_BYTE_POSITION |
            (if flag.half_carry { 1 } else { 0 }) << HALF_CARRY_FLAG_BYTE_POSITION |
            (if flag.carry      { 1 } else { 0 }) << CARRY_FLAG_BYTE_POSITION
    }
}

impl From<u8> for FlagsRegister {
    fn from(byte: u8) -> Self {
        let zero = ((byte >> ZERO_FLAG_BYTE_POSITION) & 0b1) != 0;
        let subtract = ((byte >> SUBTRACT_FLAG_BYTE_POSITION) & 0b1) != 0;
        let half_carry = ((byte >> HALF_CARRY_FLAG_BYTE_POSITION) & 0b1) != 0;
        let carry = ((byte >> CARRY_FLAG_BYTE_POSITION) & 0b1) != 0;

        FlagsRegister {
            zero,
            subtract,
            half_carry,
            carry
        }
    }
}
#[derive(Debug)]
pub(crate) struct Registers {
    pub(crate) a: u8,
    pub(crate) b: u8,
    pub(crate) c: u8,
    pub(crate) d: u8,
    pub(crate) e: u8,
    pub(crate) f: FlagsRegister,
    pub(crate) h: u8,
    pub(crate) l: u8,
}

impl Registers {
    pub(crate) fn get_af(&self) -> u16 {
        (self.a as u16) << 8 | u8::from(self.f) as u16
    }
    pub(crate) fn set_af(&mut self, value: u16) {
        self.a = ((value & 0xFF00) >> 8) as u8;
        self.f = FlagsRegister::from((value & 0xFF) as u8);
    }
    pub(crate) fn get_bc(&self) -> u16 {
        (self.b as u16) << 8 | self.c as u16
    }
    pub(crate) fn set_bc(&mut self, value: u16) {
        self.b = ((value & 0xFF00) >> 8) as u8;
        self.c = (value & 0xFF) as u8;
    }
    pub(crate) fn get_de(&self) -> u16 {
        (self.d as u16) << 8 | self.e as u16
    }
    pub(crate) fn set_de(&mut self, value: u16) {
        self.d = ((value & 0xFF00) >> 8) as u8;
        self.e = (value & 0xFF) as u8;
    }
    pub(crate) fn get_hl(&self) -> u16 {
        (self.h as u16) << 8 | self.l as u16
    }
    pub(crate) fn set_hl(&mut self, value: u16) {
        self.h = ((value & 0xFF00) >> 8) as u8;
        self.l = (value & 0xFF) as u8;
    }
}