use std::{fmt, sync::Arc};

use bitfield::bitfield;
use log::info;

use crate::{
    CPUMMRegisters, PPU, apu::APU, controllers::Controllers, cpu::DmaState, ppu::PPUMMRegisters,
};

pub trait Memory {
    fn peek(&self, val: usize) -> Option<u8>;
    fn len(&self) -> usize;
    fn edit_ver(&self) -> usize;
}

#[derive(Debug, Clone)]
pub enum Value<R> {
    Value(u8),
    Register { reg: R, offset: u16 },
}

impl<R> Value<R> {
    pub fn reg_map(self, f: impl FnOnce(R, u16) -> u8) -> u8 {
        match self {
            Value::Value(v) => v,
            Value::Register { reg, offset } => f(reg, offset),
        }
    }
}

#[derive(Debug, Clone)]
pub enum Data<R> {
    RAM(Vec<u8>),
    ROM(Arc<[u8]>),
    Mirror(u16),
    Reg(R),
    // Disabled,
}

#[derive(Debug, Clone)]
pub struct Segment<R> {
    name: SegmentId,
    position: u16,
    size: u16,
    mem: Data<R>,
}

impl<R> Segment<R> {
    fn is(&self, id: SegmentId) -> bool {
        self.name == id
    }

    fn ram(name: SegmentId, position: u16, size: u16) -> Self {
        Self {
            name,
            position,
            size,
            mem: Data::RAM(vec![0u8; size as usize]),
        }
    }
    fn rom<Raw>(name: SegmentId, position: u16, raw: Raw) -> Self
    where
        Arc<[u8]>: From<Raw>,
    {
        let raw = Arc::from(raw);
        Self {
            name,
            position,
            size: raw.len() as u16,
            mem: Data::ROM(raw),
        }
    }
    fn reg(name: SegmentId, position: u16, size: u16, reg: R) -> Self {
        Self {
            name,
            position,
            size,
            mem: Data::Reg(reg),
        }
    }
    fn mirror(name: SegmentId, position: u16, size: u16, of: u16) -> Self {
        Self {
            name,
            position,
            size,
            mem: Data::Mirror(of),
        }
    }

    // fn take_rom(&mut self) -> Vec<u8> {
    //     match std::mem::replace(&mut self.mem, Data::Disabled) {
    //         Data::ROM(items) => items,
    //         _ => panic!("Cannot take rom since memory is not rom"),
    //     }
    // }

    // fn set_rom(&mut self, rom: Vec<u8>) {
    //     assert!(
    //         matches!(self.mem, Data::Disabled),
    //         "Cannot set non-disabled memory to rom"
    //     );
    //     self.mem = Data::ROM(rom);
    // }

    fn swap_rom(&mut self, rom: Arc<[u8]>) {
        match &mut self.mem {
            Data::ROM(items) => *items = rom,
            _ => panic!("Cannot swap rom since memory is not rom"),
        }
    }

    pub fn power_cycle(&mut self) {
        match &mut self.mem {
            Data::RAM(v) => v.fill(0),
            _ => (),
        }
    }
}

#[derive(Debug, Clone)]
pub struct MemoryMap<R> {
    edit_ver: usize,
    segments: Vec<Segment<R>>,
    // map: Remapper,
}

impl<R: Copy> MemoryMap<R> {
    pub fn read(&self, addr: u16) -> Value<R> {
        // self.edit_ver += 1;
        for segment in &self.segments {
            if segment.position <= addr && addr - segment.position < segment.size {
                return match &segment.mem {
                    Data::RAM(items) => Value::Value(items[(addr - segment.position) as usize]),
                    Data::ROM(items) => Value::Value(items[(addr - segment.position) as usize]),
                    Data::Reg(reg) => Value::Register {
                        reg: *reg,
                        offset: addr - segment.position,
                    },
                    Data::Mirror(pos) => {
                        let offset = addr - segment.position;
                        self.read(pos + offset)
                        // let s = &self.segments[*index];
                        // self.read(s.position + offset % s.size)
                    } // Data::Disabled => todo!(),
                };
            }
        }
        // TODO: Open bus
        Value::Value(0)
        // todo!("Open bus")
    }

    pub fn write(&mut self, addr: u16, val: u8) -> Option<(R, u16, u8)> {
        self.edit_ver += 1;
        for segment in &mut self.segments {
            if segment.position <= addr && addr - segment.position < segment.size {
                return match &mut segment.mem {
                    Data::RAM(items) => {
                        items[(addr - segment.position) as usize] = val;
                        None
                    }
                    Data::ROM(_items) => None,
                    Data::Reg(reg) => Some((*reg, addr - segment.position, val)),
                    Data::Mirror(pos) => {
                        let pos = *pos;
                        let offset = addr - segment.position;
                        self.write(pos + offset, val)
                        // let index = *index;
                        // let s = &self.segments[index];
                        // self.write(s.position + offset % s.size, val)
                    } // Data::Disabled => todo!(),
                };
            }
        }
        // iirc, open bus just drops writes
        None
        // todo!("Open bus (${addr:04X})")
    }

    // pub fn clear(&mut self) {
    //     for s in &mut self.segments {
    //         match &mut s.mem {
    //             Data::RAM(items) => items.fill(0),
    //             _ => (),
    //         }
    //     }
    // }

    pub fn power_cycle(&mut self) -> Self {
        for seg in &mut self.segments {
            seg.power_cycle();
        }
        let segments = std::mem::take(&mut self.segments);
        Self {
            edit_ver: self.edit_ver + 1,
            segments,
            // map: self.map.power_cycle(),
        }
    }

    fn find(&mut self, id: SegmentId) -> Option<&mut Segment<R>> {
        for s in &mut self.segments {
            if s.is(id) {
                return Some(s);
            }
        }
        None
    }

    fn peek_val(&self, addr: u16) -> Result<u8, Option<(R, u16)>> {
        for segment in &self.segments {
            if segment.position <= addr && addr - segment.position < segment.size {
                return match &segment.mem {
                    Data::RAM(items) => Ok(items[(addr - segment.position) as usize]),
                    Data::ROM(items) => Ok(items[(addr - segment.position) as usize]),
                    Data::Reg(r) => Err(Some((*r, addr - segment.position))),
                    Data::Mirror(pos) => {
                        let offset = addr - segment.position;
                        self.peek_val(pos + offset)
                    } // Data::Disabled => Err(None),
                };
            }
        }
        Err(None)
    }
}

impl<R: Copy> Memory for MemoryMap<R> {
    fn peek(&self, addr: usize) -> Option<u8> {
        self.peek_val(addr as u16).ok()
    }

    fn len(&self) -> usize {
        self.segments
            .iter()
            .map(|s| s.position as usize + s.size as usize)
            .max()
            .unwrap_or(0)
    }

    fn edit_ver(&self) -> usize {
        self.edit_ver
    }
}

bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq)]
    struct ShiftPair(u8);
    impl Debug;
    high, set_high: 7, 4;
    low, set_low: 3, 0;
}

impl ShiftPair {
    fn low_count(&self) -> usize {
        if self.low() == 0 { 0 } else { 64 << self.low() }
    }
    fn high_count(&self) -> usize {
        if self.high() == 0 {
            0
        } else {
            64 << self.high()
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq)]
struct Mapper {
    is_nes_2: bool,
    horizontal_name_table: bool,
    mapper: u16,
    sub_mapper: u8,
    prg_rom_size: u8,
    chr_rom_size: u8,

    prg_nv_ram_size: ShiftPair,
    chr_nv_ram_size: ShiftPair,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum SegmentId {
    #[allow(unused)]
    TestRam,

    InternalRam,
    InternalRamMirror,
    PpuRegisters,
    ApuIoRegisters,
    PrgRom,

    Nametable0,
    Nametable1,
    Nametable2,
    Nametable3,
    VramMirror,
    PaletteControl,
    PaletteMirror,
    PpuRom,
    PpuRam,

    // CpuBank(u32),
    PrgBank0,
    PrgBank1,
    ChrBank0,
    ChrBank1,

    PrgRam,
    NVRam,
}

impl fmt::Display for SegmentId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{self:?}")
    }
}

#[derive(Debug, Clone)]
pub struct Mapped {
    cpu: MemoryMap<CPUMMRegisters>,
    ppu: MemoryMap<PPUMMRegisters>,
    mapper: Remapper,
}

impl Mapped {
    pub fn from_rom(rom: &[u8]) -> Self {
        let (header, rom) = rom.split_at(0x10);
        let nes_20 = header[7] & 0x0C == 0x08;
        // assert!(nes_20, "Only supports nes 2.0 format");
        // if header[6] & 0b11111110 != 0 {
        //     todo!("Support other mapper flags, {:08b}", header[6]);
        // }
        let mapper = if nes_20 {
            assert_eq!(header[9], 0, "No support for larger PRG/CHR roms");
            // assert_eq!(header[10], 0, "No support for PRG-RAM/EEPROM");
            // assert_eq!(header[11], 0, "No support for CHR-RAM");
            assert!(header[12] == 0 || header[12] == 2, "Only support NTSC NES");
            Mapper {
                is_nes_2: true,
                horizontal_name_table: header[6] & (1 << 0) == 1,
                mapper: ((header[6] as u16 & 0xF0) >> 4)
                    | ((header[7] as u16 & 0xF0) >> 0)
                    | ((header[8] as u16 & 0x0F) << 8),
                sub_mapper: (header[8] & 0xF0) >> 4,
                prg_rom_size: header[4],
                chr_rom_size: header[5],
                prg_nv_ram_size: ShiftPair(header[10]),
                chr_nv_ram_size: ShiftPair(header[11]),
            }
        } else {
            Mapper {
                is_nes_2: false,
                horizontal_name_table: header[6] & (1 << 0) == 1,
                mapper: ((header[6] as u16 & 0xF0) >> 4) | ((header[7] as u16 & 0xF0) >> 0),
                sub_mapper: 0,
                prg_rom_size: header[4],
                chr_rom_size: header[5],
                prg_nv_ram_size: ShiftPair(0),
                chr_nv_ram_size: ShiftPair(0),
            }
        };
        let (prg_rom, rom) = rom.split_at(mapper.prg_rom_size as usize * 0x4000);
        let (chr_rom, _rom) = rom.split_at(mapper.chr_rom_size as usize * 0x2000);
        info!("Mapper: {}/{}", mapper.mapper, mapper.sub_mapper);
        // assert_eq!(
        //     mapper.prg_nv_ram_size.low_count(),
        //     0,
        //     "No support for PRG-RAM"
        // );
        // assert_eq!(
        //     mapper.chr_nv_ram_size.low_count(),
        //     0,
        //     "No support for CHR-RAM"
        // );
        assert_eq!(
            mapper.chr_nv_ram_size.high_count(),
            0,
            "No support for CHR-NVRAM"
        );
        // assert_eq!(rom.len(), 0);
        // let prg_rom = &file[self.cpu_offset()..self.ppu_offset()];
        let mut cpu_segments = vec![
            Segment::ram(SegmentId::InternalRam, 0x0000, 0x0800),
            Segment::mirror(SegmentId::InternalRamMirror, 0x0800, 0x1800, 0x0000),
            Segment::reg(SegmentId::PpuRegisters, 0x2000, 0x0008, CPUMMRegisters::PPU),
            Segment::reg(
                SegmentId::ApuIoRegisters,
                0x4000,
                0x0018,
                CPUMMRegisters::APU,
            ),
        ];
        // CHR-NVRAM
        if mapper.prg_nv_ram_size.high_count() > 0 {
            cpu_segments.push(Segment::ram(
                SegmentId::NVRam,
                0x6000,
                mapper.prg_nv_ram_size.high_count() as u16,
            ));
        } else if mapper.prg_nv_ram_size.low_count() > 0 {
            cpu_segments.push(Segment::ram(
                SegmentId::PrgRam,
                0x6000,
                mapper.prg_nv_ram_size.low_count() as u16,
            ));
        }
        let mut ppu_segments = vec![
            Segment::mirror(SegmentId::VramMirror, 0x3000, 0x0F00, 0x0000),
            Segment::reg(
                SegmentId::PaletteControl,
                0x3F00,
                0x0020,
                PPUMMRegisters::Palette,
            ),
            Segment::mirror(SegmentId::PaletteMirror, 0x3F20, 0x00E0, 0x3F00),
        ];
        if mapper.horizontal_name_table {
            ppu_segments.push(Segment::ram(SegmentId::Nametable0, 0x2000, 0x400));
            ppu_segments.push(Segment::ram(SegmentId::Nametable1, 0x2400, 0x400));
            ppu_segments.push(Segment::mirror(
                SegmentId::Nametable2,
                0x2800,
                0x400,
                0x2000,
            ));
            ppu_segments.push(Segment::mirror(
                SegmentId::Nametable3,
                0x2C00,
                0x400,
                0x2400,
            ));
        } else {
            ppu_segments.push(Segment::ram(SegmentId::Nametable0, 0x2000, 0x400));
            ppu_segments.push(Segment::mirror(
                SegmentId::Nametable1,
                0x2400,
                0x400,
                0x2000,
            ));
            ppu_segments.push(Segment::ram(SegmentId::Nametable2, 0x2800, 0x400));
            ppu_segments.push(Segment::mirror(
                SegmentId::Nametable3,
                0x2C00,
                0x400,
                0x2800,
            ));
        }
        let remap = if mapper.mapper == 0 {
            cpu_segments.push(Segment::rom(
                SegmentId::PrgRom,
                0x8000 + (0x8000 - prg_rom.len() as u16),
                prg_rom,
            ));
            if mapper.is_nes_2 {
                if mapper.chr_nv_ram_size.low_count() > 0 {
                    ppu_segments.push(Segment::ram(
                        SegmentId::PpuRam,
                        0,
                        mapper.chr_nv_ram_size.low_count() as u16,
                    ));
                } else {
                    ppu_segments.push(Segment::rom(SegmentId::PpuRom, 0, chr_rom));
                }
            } else {
                if chr_rom.len() == 0 {
                    ppu_segments.push(Segment::ram(SegmentId::PpuRam, 0, 0x2000));
                } else {
                    ppu_segments.push(Segment::rom(SegmentId::PpuRom, 0, chr_rom));
                }
            }
            Remapper::None
        } else if mapper.mapper == 1 && mapper.sub_mapper == 0 {
            let prg_banks: Vec<Arc<[u8]>> =
                prg_rom.chunks(0x4000).map(|ch| Arc::from(ch)).collect();
            for (i, b) in prg_banks.iter().enumerate() {
                info!("{i}: {:X?}", &b[0x3FF0..]);
            }
            cpu_segments.push(Segment::rom(
                SegmentId::PrgBank0,
                0x8000,
                prg_banks.first().unwrap().clone(),
            ));
            cpu_segments.push(Segment::rom(
                SegmentId::PrgBank1,
                0xC000,
                prg_banks.last().unwrap().clone(),
            ));
            info!("CHR_ROM: {}", chr_rom.len());
            let chr_banks: Vec<Arc<[u8]>> =
                chr_rom.chunks(0x1000).map(|ch| Arc::from(ch)).collect();
            if chr_rom.len() == 0 {
                ppu_segments.push(Segment::ram(SegmentId::PpuRam, 0, 0x2000));
            } else {
                ppu_segments.push(Segment::rom(
                    SegmentId::ChrBank0,
                    0x0000,
                    chr_banks[0].clone(),
                ));
                ppu_segments.push(Segment::rom(
                    SegmentId::ChrBank1,
                    0x1000,
                    chr_banks[1].clone(),
                ));
            }
            Remapper::MMC1 {
                shift_reg: 0,
                count: 0,
                prg_banks,
                chr_banks,
                mode: MMC1Mode::LastFixed,
                cur_prg_bank: 0,
            }
        } else {
            todo!("support mapper chip: {}/{}", mapper.mapper, mapper.sub_mapper)
        };
        Self {
            cpu: MemoryMap {
                edit_ver: 0,
                segments: cpu_segments,
            },
            ppu: MemoryMap {
                edit_ver: 0,
                segments: ppu_segments,
            },
            mapper: remap,
        }
    }

    pub fn power_cylce(&mut self) -> Self {
        // TODO: mapper needs to reset cpu and ppu mem maps
        let mut cpu = self.cpu.power_cycle();
        let mut ppu = self.ppu.power_cycle();
        Self {
            mapper: self.mapper.power_cycle(&mut cpu, &mut ppu),
            cpu,
            ppu,
        }
    }

    pub fn peek_ppu(&self, addr: u16) -> Result<u8, Option<(PPUMMRegisters, u16)>> {
        self.ppu.peek_val(addr)
    }
    pub fn ppu_edit_ver(&self) -> usize {
        self.ppu.edit_ver
    }
    pub fn peek_cpu(&self, addr: u16) -> Option<u8> {
        self.cpu.peek_val(addr).ok()
    }
    pub fn cpu_edit_ver(&self) -> usize {
        self.cpu.edit_ver
    }

    #[cfg(test)]
    pub fn test_ram() -> Self {
        Self {
            cpu: MemoryMap {
                edit_ver: 0,
                segments: vec![Segment::ram(SegmentId::TestRam, 0, 0x8000)],
            },
            ppu: MemoryMap {
                edit_ver: 0,
                segments: vec![Segment::ram(SegmentId::TestRam, 0, 0x8000)],
            },
            mapper: Remapper::None,
        }
    }
}

pub struct CpuMem<'a> {
    mem: &'a mut Mapped,
    ppu: &'a mut PPU,
    apu: &'a mut APU,
    dma: &'a mut DmaState,
    controllers: &'a mut Controllers,
}

impl<'a> CpuMem<'a> {
    pub fn new(
        mem: &'a mut Mapped,
        ppu: &'a mut PPU,
        apu: &'a mut APU,
        dma: &'a mut DmaState,
        controllers: &'a mut Controllers,
    ) -> Self {
        Self {
            mem,
            ppu,
            apu,
            dma,
            controllers,
        }
    }
    pub fn read(&mut self, addr: u16) -> u8 {
        match self.mem.mapper {
            _ => (),
        }
        match self.mem.cpu.read(addr) {
            Value::Value(v) => v,
            Value::Register { reg, offset } => match reg {
                CPUMMRegisters::PPU => self.ppu.read_reg(&mut PpuMem::new(self.mem), offset),
                CPUMMRegisters::APU => {
                    if offset == 0x014 {
                        todo!("OAM DMA")
                    } else if offset == 0x16 {
                        self.controllers.read_joy1()
                    } else if offset == 0x17 {
                        self.controllers.read_joy2()
                    } else {
                        self.apu.read_reg(offset)
                    }
                }
            },
        }
    }
    pub fn write(&mut self, addr: u16, val: u8) {
        match &mut self.mem.mapper {
            Remapper::MMC1 {
                shift_reg,
                count,
                mode,
                prg_banks,
                chr_banks,
                cur_prg_bank,
            } if addr & 0x8000 != 0 => {
                if val & 0x80 != 0 {
                    *shift_reg = 0;
                    *count = 0;
                } else if *count == 4 {
                    let val = (*shift_reg >> 1) | ((val & 0x01) << 4);
                    if (addr & 0x6000) >> 13 == 0 {
                        // TODO: fix mem layout if it's changed
                        if val & 0b01100 == 0b01000 {
                            *mode = MMC1Mode::FirstFixed;
                            let bank0 = self.mem.cpu.find(SegmentId::PrgBank0).unwrap();
                            bank0.swap_rom(prg_banks[0].clone());
                            let bank1 = self.mem.cpu.find(SegmentId::PrgBank1).unwrap();
                            bank1.swap_rom(prg_banks[*cur_prg_bank].clone());
                        } else if val & 0b01100 == 0b01100 {
                            *mode = MMC1Mode::LastFixed;
                            let bank0 = self.mem.cpu.find(SegmentId::PrgBank0).unwrap();
                            bank0.swap_rom(prg_banks[*cur_prg_bank].clone());
                            let bank1 = self.mem.cpu.find(SegmentId::PrgBank1).unwrap();
                            bank1.swap_rom(prg_banks.last().unwrap().clone());
                        } else {
                            *mode = MMC1Mode::Full;
                            let bank0 = self.mem.cpu.find(SegmentId::PrgBank0).unwrap();
                            bank0.swap_rom(prg_banks[*cur_prg_bank & !1].clone());
                            let bank1 = self.mem.cpu.find(SegmentId::PrgBank1).unwrap();
                            bank1.swap_rom(prg_banks[(*cur_prg_bank & !1) + 1].clone());
                        }
                        if val & 0b10000 == 0b10000 {
                            // TODO: CHR-ROM mode
                        }
                        // TODO: Set name table mirroring
                        if val & 0b00011 == 0b00000 {
                        } else if val & 0b00011 == 0b00001 {
                        } else if val & 0b00011 == 0b00010 {
                        } else {
                        }
                    } else if (addr & 0x6000) >> 13 == 1 {
                        if chr_banks.len() != 0 {
                            todo!("Swap CHR bank 0")
                        }
                    } else if (addr & 0x6000) >> 13 == 2 {
                        if chr_banks.len() != 0 {
                            todo!("Swap CHR bank 1")
                        }
                    } else if (addr & 0x6000) >> 13 == 3 {
                        *cur_prg_bank = (val & 0x0F) as usize;
                        // println!("Updating ROM: new {:X}", val);
                        match mode {
                            MMC1Mode::Full => {
                                let bank0 = self.mem.cpu.find(SegmentId::PrgBank0).unwrap();
                                bank0.swap_rom(prg_banks[*cur_prg_bank & !1].clone());
                                let bank1 = self.mem.cpu.find(SegmentId::PrgBank1).unwrap();
                                bank1.swap_rom(prg_banks[(*cur_prg_bank & !1) + 1].clone());
                            }
                            MMC1Mode::FirstFixed => {
                                let bank1 = self.mem.cpu.find(SegmentId::PrgBank1).unwrap();
                                // Highest bit is ignored
                                bank1.swap_rom(prg_banks[(val & 0x07) as usize].clone());
                            }
                            MMC1Mode::LastFixed => {
                                let bank0 = self.mem.cpu.find(SegmentId::PrgBank0).unwrap();
                                // Highest bit is ignored
                                bank0.swap_rom(prg_banks[(val & 0x07) as usize].clone());
                            }
                        }
                        // TODO: handle MSB, changes some stuff...
                    } else {
                        todo!("Handle reg write {} => {}", val, (addr & 0x6000) >> 13,)
                    }
                    *shift_reg = 0;
                    *count = 0;
                } else {
                    *shift_reg = (*shift_reg >> 1) | ((val & 0x01) << 4);
                    *count += 1;
                    // println!("Mapper SHR {:05b}", *shift_reg);
                }
            }
            _ => (),
        }
        match self.mem.cpu.write(addr, val) {
            Some((CPUMMRegisters::PPU, offset, val)) => {
                self.ppu.write_reg(&mut PpuMem::new(self.mem), offset, val)
            }
            Some((CPUMMRegisters::APU, offset, val)) => {
                if offset == 0x014 {
                    *self.dma = DmaState::Idle((val as u16) << 8);
                } else if offset == 0x16 {
                    self.controllers.write_joy_strobe(val);
                } else {
                    self.apu.write_reg(offset, val);
                }
            }
            _ => (),
        }
    }
}

pub struct PpuMem<'a>(&'a mut Mapped);

impl<'a> PpuMem<'a> {
    pub fn new(mem: &'a mut Mapped) -> Self {
        Self(mem)
    }
    pub fn read(&mut self, addr: u16) -> Value<PPUMMRegisters> {
        self.0.ppu.read(addr)
    }
    pub fn write(&mut self, addr: u16, val: u8, reg_fn: impl FnOnce(&PPUMMRegisters, u16, u8)) {
        match self.0.ppu.write(addr, val) {
            Some((r, o, v)) => reg_fn(&r, o, v),
            None => (),
        }
    }
}

#[derive(Debug, Clone)]
enum MMC1Mode {
    Full,
    FirstFixed,
    LastFixed,
}

#[derive(Debug, Clone)]
enum Remapper {
    None,
    MMC1 {
        shift_reg: u8,
        count: u8,
        prg_banks: Vec<Arc<[u8]>>,
        chr_banks: Vec<Arc<[u8]>>,
        mode: MMC1Mode,
        cur_prg_bank: usize,
    },
}

impl Remapper {
    fn power_cycle(
        &mut self,
        cpu: &mut MemoryMap<CPUMMRegisters>,
        _ppu: &mut MemoryMap<PPUMMRegisters>,
    ) -> Self {
        match self {
            Remapper::None => Remapper::None,
            Remapper::MMC1 {
                prg_banks,
                chr_banks,
                ..
            } => {
                let prg_banks = std::mem::take(prg_banks);
                let chr_banks = std::mem::take(chr_banks);
                cpu.find(SegmentId::PrgBank0)
                    .unwrap()
                    .swap_rom(prg_banks[0].clone());
                cpu.find(SegmentId::PrgBank1)
                    .unwrap()
                    .swap_rom(prg_banks.last().unwrap().clone());
                Remapper::MMC1 {
                    shift_reg: 0,
                    count: 0,
                    prg_banks,
                    chr_banks,
                    mode: MMC1Mode::LastFixed,
                    cur_prg_bank: 0,
                }
            }
        }
    }
}
