const BOOT_ROM_START: u16 = 0x0000;
const BOOT_ROM_END: u16 = 0x00FF;

const CART_ROM_START: u16 = 0x0000;
const CART_ROM_END: u16 = 0x7FFF;

const CART_ENTRY_POINT: u16 = 0x0100;

const CART_HEADER_START: u16 = 0x0100;
const CART_HEADER_END: u16 = 0x014F;
const CART_FIXED_START: u16 = 0x0150;
const CART_FIXED_END: u16 = 0x3FFF;
const CART_SWITCH_START: u16 = 0x4000;
const CART_SWITCH_END: u16 = 0x7FFF;

const VIDEO_RAM_START: u16 = 0x8000;
const VIDEO_RAM_END: u16 = 0x9FFF;

const SOUND_REG_START: u16 = 0xFF10;
const SOUND_REG_END: u16 = 0xFF3F;

// http://gbdev.gg8.se/wiki/articles/Video_Display
const LCD_CONTROL_REGISTER: u16 = 0xFF40; // R/W
const LCD_STATUS_REGISTER: u16 = 0xFF41; // R/W
const SCROLL_Y: u16 = 0xFF42; // R/W
const SCROLL_X: u16 = 0xFF43; // R/W
const LCD_LY: u16 = 0xFF44; // R
const LCD_LYC: u16 = 0xFF45; // R/W
const LCD_WY: u16 = 0xFF4A; // R/W
const LCD_WX: u16 = 0xFF4B; // R/W
const BG_PALETTE_DATA: u16 = 0xFF47; // R/W
const OBJECT_PALETTE_0: u16 = 0xFF48; // R/W
const OBJECT_PALETTE_1: u16 = 0xFF49; // R/W

const HIGH_RAM_START: u16 = 0xFF80;
const HIGH_RAM_END: u16 = 0xFFFE;

pub fn map_addr(addr: u16) -> Addr {
    match addr {
        BOOT_ROM_START...BOOT_ROM_END => Addr::BootRom(addr - BOOT_ROM_START),
        CART_HEADER_START...CART_HEADER_END => Addr::CartHeader(addr - CART_ROM_START),
        CART_FIXED_START...CART_FIXED_END => Addr::CartFixed(addr - CART_ROM_START),
        CART_SWITCH_START...CART_SWITCH_END => Addr::CartSwitch(addr - CART_ROM_START),
        VIDEO_RAM_START...VIDEO_RAM_END => Addr::VideoRam(addr - VIDEO_RAM_START),
        SOUND_REG_START...SOUND_REG_END => Addr::SoundRegister(addr - SOUND_REG_START),
        HIGH_RAM_START...HIGH_RAM_END => Addr::HighRam(addr - HIGH_RAM_START),
        _ => panic!("Unrecognised physical address: {:#x}", addr),
    }
}

pub enum Addr {
    BootRom(u16),
    CartHeader(u16),
    CartFixed(u16),
    CartSwitch(u16),
    VideoRam(u16),
    SoundRegister(u16),
    HighRam(u16),
}

pub fn cartridge_type(byte: u8) -> &'static str {
    match byte {
        0x00 => "ROM ONLY",
        0x01 => "MBC1",
        0x02 => "MBC1+RAM",
        0x03 => "MBC1+RAM+BATTERY",
        0x05 => "MBC2",
        0x06 => "MBC2+BATTERY",
        0x08 => "ROM+RAM",
        0x09 => "ROM+RAM+BATTERY",
        0x0B => "MMM01",
        0x0C => "MMM01+RAM",
        0x0D => "MMM01+RAM+BATTERY",
        0x0F => "MBC3+TIMER+BATTERY",
        0x10 => "MBC3+TIMER+RAM+BATTERY",
        0x11 => "MBC3",
        0x12 => "MBC3+RAM",
        0x13 => "MBC3+RAM+BATTERY",
        0x15 => "MBC4",
        0x16 => "MBC4+RAM",
        0x17 => "MBC4+RAM+BATTERY",
        0x19 => "MBC5",
        0x1A => "MBC5+RAM",
        0x1B => "MBC5+RAM+BATTERY",
        0x1C => "MBC5+RUMBLE",
        0x1D => "MBC5+RUMBLE+RAM",
        0x1E => "MBC5+RUMBLE+RAM+BATTERY",
        0x20 => "MBC6",
        0x22 => "MBC7+SENSOR+RUMBLE+RAM+BATTERY",
        0xFC => "POCKET CAMERA",
        0xFD => "BANDAI TAMA5",
        0xFE => "HuC3",
        0xFF => "HuC1+RAM+BATTERY",
        _ => panic!("Unknown Cartridge Type"),
    }
}

pub fn rom_size(byte: u8) -> &'static str {
    match byte {
        0x00 => "32KByte (no ROM banking)",
        0x01 => "64KByte (4 banks)",
        0x02 => "128KByte (8 banks)",
        0x03 => "256KByte (16 banks)",
        0x04 => "512KByte (32 banks)",
        0x05 => "1MByte (64 banks)",
        0x06 => "2MByte (128 banks)",
        0x07 => "4MByte (256 banks)",
        0x52 => "1.1MByte (72 banks)",
        0x53 => "1.2MByte (80 banks)",
        0x54 => "1.5MByte (96 banks)",
        _ => panic!("Unknown ROM Size")
    }
}

pub fn ram_size(byte: u8) -> &'static str {
    match byte {
        0x00 => "None",
        0x01 => "2 KBytes",
        0x02 => "8 Kbytes",
        0x03 => "32 KBytes (4 banks of 8KBytes each)",
        0x04 => "128 KBytes (16 banks of 8KBytes each)",
        0x05 => "64 KBytes (8 banks of 8KBytes each)",
        _ => panic!("Unknown RAM Size")
    }
}
