#ifndef bus_h
#define bus_h

class Bus : Mmio
{
public:
	enum Type { SMS_ROM_GENERIC = 11, SMS_ROM_CODEMASTERS = 12, SMS_ROM_KOREAN = 13, SMS_BIOS = 14, SMS_ROM_KOREAN_8K = 15, SMS_NO_BANK_SWITCHING = 16 };
	void prepare(Type);

	u8 read_from(u16 addr);
	void write_to(u16 addr, u8 value);
	u8 mmio_read(u8 pos);
	void mmio_write(u8 pos, u8 value);

	void power();
	Type getType() {
		return type;
	}

	bool biosDetected;
	bool romDetected;

	struct MemMmio {
		u8 route(u8 addr);
		u8 read(u8 addr);
		void write(u8 addr, u8 value);
		void map(u8 pos, Mmio* access);
		Mmio* _mmio[8];
	} mmio;

	bool isWorkRamDisabled() {
		return (port_3e & 0x10) != 0;
	}
	//Expansion Port and Card are not present
	//all games are handled as cartridge games
	bool isCartridgeEnabled() {
		return	(port_3e & 0x40) == 0;
	}
	bool isBiosEnanbeld() {
		return (port_3e & 0x08) == 0;
	}
	bool isIoDisabled() {
		return (port_3e & 0x04) != 0;
	}

	Bus(_System* _sys, Cpu& cpu, Vdp& vdp);
	~Bus() {}

private:
	enum MapType { direct, linear };
	enum Slot { Cartridge = 0, Bios = 1, Unmapped = 2, Wram = 3 };

	void mapUpdated() {
		page0update = page1update = page2update = wramupdate = false;
	}

	_System* _sys;
	Cpu& cpu;
	Vdp& vdp;
	Type type;
	Type romtype;
	u8 port_3e;

	struct {
		u8 ramSelector;
		u8 page0bank;
		u8 page1bank;
		u8 page2bank;
		u8 page3bank;
		u8 page4bank;
	} reg[2];

	bool page0update;
	bool page1update;
	bool page2update;
	bool wramupdate;

	struct Slots {
		MemMap* access;
		MapType mapType;
	} slot[4];

	struct Mapper {
		MemMap* access;
		u32 offset;
	} mapper[65536];

	struct MemoryControl : MemMap {
		MemoryControl(Bus* bus) : bus(bus) {}
		Bus* bus;
		u8 read(u32 addr);
		void write(u32 addr, u8 value);
	} mc;

	struct MemUnmapped : MemMap {
		u8 read(u32 addr);
		void write(u32 addr, u8 value) {}
		MemUnmapped() {}
	} unmapped;

	u32 getSize() {
		Slot _slot = getRequestedSlot();
		MemMap* access = slot[_slot].access;
		return access->size();
	}

	Slot getRequestedSlot() {
		if ( isCartridgeEnabled() && romDetected) {
			return Slot::Cartridge;
		}
		if ( isBiosEnanbeld() && biosDetected) {
			return Slot::Bios;
		}
		return Slot::Unmapped; //expansion port or card enabled, or bios or cartridge not detected
	}

	void map( MapType, u8, u8, u8, u8, MemMap&, u32 = 0);
	void mapWram();
	void mapSram(u8 bankLo, u8 bankHi, Slot _slot);
	void mapSystem(u8 data = 0);
	u8 calcSwift(u8 bank);
	void mapRamSelectorWrite(u8 value);
	u8 mapRamSelectorRead();
	void mapSlot2Write(u8 value);
	u8 mapSlot2Read();
	void mapSlot1Write(u8 value);
	u8 mapSlot1Read();
	void mapSlot0Write(u8 value);
	u8 mapSlot0Read();
	u32 mirror(u32 index, u32 size);

	void mapKorean8kWrite(u32 addr, u8 value);
	u8 mapKorean8kRead(u32 addr);
};

#endif
