// SPDX-License-Identifier: LGPL-2.1-or-later OR GPL-2.0-or-later OR MPL-2.0
// SPDX-FileCopyrightText: 2026 Gabriel Marcano <gabemarcano@yahoo.com>

use std::fmt;

/// Describes the type of memory mapper the cartridge uses, and any additional hardware.
#[derive(Debug, Copy, Clone)]
pub enum CartType {
    RomOnly,
    Mbc1,
    Mbc1Ram,
    Mbc1RamBattery,
    Mbc2,
    Mbc2Battery,
    RomRam,
    RomRamBattery,
    Mmm01,
    Mmm01Ram,
    Mmm01RamBattery,
    Mbc3TimerBattery,
    Mbc3TimerRamBattery,
    Mbc3,
    Mbc3Ram,
    Mbc3RamBattery,
    Mbc5,
    Mbc5Ram,
    Mbc5RamBattery,
    Mbc5Rumble,
    Mbc5RumbleRam,
    Mbc5RumbleRamBattery,
    Mbc6,
    Mbc7SensorRumbleRamBattery,
    PocketCamera,
    BandaiTama5,
    Huc3,
    Huc1RamBattery,
    Unknown(u8),
}

impl fmt::Display for CartType {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::RomOnly => write!(fmt, "ROM only")?,
            Self::Mbc1 => write!(fmt, "MBC1")?,
            Self::Mbc1Ram => write!(fmt, "MBC1 + RAM")?,
            Self::Mbc1RamBattery => write!(fmt, "MBC1 + RAM + battery")?,
            Self::Mbc2 => write!(fmt, "MBC2")?,
            Self::Mbc2Battery => write!(fmt, "MBC2 + battery")?,
            Self::RomRam => write!(fmt, "ROM + RAM")?,
            Self::RomRamBattery => write!(fmt, "ROM + RAM + battery")?,
            Self::Mmm01 => write!(fmt, "MMM01")?,
            Self::Mmm01Ram => write!(fmt, "MMM01 + RAM")?,
            Self::Mmm01RamBattery => write!(fmt, "MMM01 + RAM + battery")?,
            Self::Mbc3TimerBattery => write!(fmt, "MBC3 + Timer + battery")?,
            Self::Mbc3TimerRamBattery => write!(fmt, "MBC3 + Timer + RAM + battery")?,
            Self::Mbc3 => write!(fmt, "MBC3")?,
            Self::Mbc3Ram => write!(fmt, "MBC3 + RAM")?,
            Self::Mbc3RamBattery => write!(fmt, "MBC3 + RAM + battery")?,
            Self::Mbc5 => write!(fmt, "MBC5")?,
            Self::Mbc5Ram => write!(fmt, "MBC5 + RAM")?,
            Self::Mbc5RamBattery => write!(fmt, "MBC5 + RAM +battery")?,
            Self::Mbc5Rumble => write!(fmt, "MBC5 + rumble")?,
            Self::Mbc5RumbleRam => write!(fmt, "MBC5 + rumble + RAM")?,
            Self::Mbc5RumbleRamBattery => write!(fmt, "MBC5 + rumble + RAM + battery")?,
            Self::Mbc6 => write!(fmt, "MBC6")?,
            Self::Mbc7SensorRumbleRamBattery => {
                write!(fmt, "MBC7 + sensor + rumble + RAM + battery")?;
            }
            Self::PocketCamera => write!(fmt, "Pocket Camera")?,
            Self::BandaiTama5 => write!(fmt, "Bandai Tama5")?,
            Self::Huc3 => write!(fmt, "HUC3")?,
            Self::Huc1RamBattery => write!(fmt, "HUC1 + RAM + battery")?,
            Self::Unknown(val) => write!(fmt, "Unknown region, code {val}")?,
        }
        Ok(())
    }
}

impl From<u8> for CartType {
    fn from(data: u8) -> Self {
        match data {
            0x00 => Self::RomOnly,
            0x01 => Self::Mbc1,
            0x02 => Self::Mbc1Ram,
            0x03 => Self::Mbc1RamBattery,
            0x05 => Self::Mbc2,
            0x06 => Self::Mbc2Battery,
            0x08 => Self::RomRam,
            0x09 => Self::RomRamBattery,
            0x0B => Self::Mmm01,
            0x0C => Self::Mmm01Ram,
            0x0D => Self::Mmm01RamBattery,
            0x0F => Self::Mbc3TimerBattery,
            0x10 => Self::Mbc3TimerRamBattery,
            0x11 => Self::Mbc3,
            0x12 => Self::Mbc3Ram,
            0x13 => Self::Mbc3RamBattery,
            0x19 => Self::Mbc5,
            0x1A => Self::Mbc5Ram,
            0x1B => Self::Mbc5RamBattery,
            0x1C => Self::Mbc5Rumble,
            0x1D => Self::Mbc5RumbleRam,
            0x1E => Self::Mbc5RumbleRamBattery,
            0x20 => Self::Mbc6,
            0x22 => Self::Mbc7SensorRumbleRamBattery,
            0xFC => Self::PocketCamera,
            0xFD => Self::BandaiTama5,
            0xFE => Self::Huc3,
            0xFF => Self::Huc1RamBattery,
            _ => Self::Unknown(data),
        }
    }
}

impl From<CartType> for u8 {
    fn from(data: CartType) -> Self {
        match data {
            CartType::RomOnly => 0x00,
            CartType::Mbc1 => 0x01,
            CartType::Mbc1Ram => 0x02,
            CartType::Mbc1RamBattery => 0x03,
            CartType::Mbc2 => 0x05,
            CartType::Mbc2Battery => 0x06,
            CartType::RomRam => 0x08,
            CartType::RomRamBattery => 0x09,
            CartType::Mmm01 => 0x0B,
            CartType::Mmm01Ram => 0x0C,
            CartType::Mmm01RamBattery => 0x0D,
            CartType::Mbc3TimerBattery => 0x0F,
            CartType::Mbc3TimerRamBattery => 0x10,
            CartType::Mbc3 => 0x11,
            CartType::Mbc3Ram => 0x12,
            CartType::Mbc3RamBattery => 0x13,
            CartType::Mbc5 => 0x19,
            CartType::Mbc5Ram => 0x1A,
            CartType::Mbc5RamBattery => 0x1B,
            CartType::Mbc5Rumble => 0x1C,
            CartType::Mbc5RumbleRam => 0x1D,
            CartType::Mbc5RumbleRamBattery => 0x1E,
            CartType::Mbc6 => 0x20,
            CartType::Mbc7SensorRumbleRamBattery => 0x22,
            CartType::PocketCamera => 0xFC,
            CartType::BandaiTama5 => 0xFD,
            CartType::Huc3 => 0xFE,
            CartType::Huc1RamBattery => 0xFF,
            CartType::Unknown(data) => data,
        }
    }
}
