using BizHawk.Common;

namespace BizHawk.Emulation.Cores.Nintendo.NES
{
	// RacerMate II
	// 64KB PRGROM, 64KB CHRRAM(!), CHRRAM is battry backed (!!)
	// the "ram protect" function is not emulated.  the notes i found said that it
	// defaults to off, the control regs are write only, and cannot be reenabled.  so...

	// todo: special controller, and IRQ is possibly wrong
	internal sealed class Mapper168 : NesBoardBase
	{
		private int prg = 0;
		private int chr = 0;
		private int irqclock = 2048;

		public override bool Configure(EDetectionOrigin origin)
		{
			switch (Cart.BoardType)
			{
				case "MAPPER168":
				case "UNL-RACERMATE":
					break;
				default:
					return false;
			}
			AssertPrg(64);
			Cart.ChrSize = 0; //AssertChr(0); //shitty dumps
			Cart.VramSize = 64; //AssertVram(64); //shitty dumps
			Cart.WramSize = 0; //AssertWram(0); // shitty dumps
			//AssertBattery(true); // battery is handled directly
			SetMirrorType(Cart.PadH, Cart.PadV);
			return true;
		}

		public override byte ReadPrg(int addr)
		{
			return addr >= 0x4000
				? Rom[addr + 0x8000]
				: Rom[addr + (prg << 14)];
		}

		// the chr reg on hardware is supposedly bitscrambled and then inverted from
		// what would be expected.  since it doesn't make a difference and i don't know
		// of any clear source on what it's actually supposed to be, ignore.
		private int Scramble(int chr)
		{
			return chr;
		}

		public override void WritePrg(int addr, byte value)
		{
			if (addr < 0x4000)
			{
				chr = value & 15;
				prg = value >> 6 & 3;
			}
			else if (addr == 0x7080) // ack
				IrqSignal = false;
			else if (addr == 0x7000) // start count
				irqclock = 0;
		}

		public override byte ReadPpu(int addr)
		{
			if (addr < 0x1000)
				return Vram[addr | Scramble(0) << 12];
			if (addr < 0x2000)
				return Vram[(addr & 0xfff) | Scramble(chr) << 12];
			return base.ReadPpu(addr);
		}

		public override void WritePpu(int addr, byte value)
		{
			if (addr < 0x1000)
				Vram[addr | Scramble(0) << 12] = value;
			else if (addr < 0x2000)
				Vram[(addr & 0xfff) | Scramble(chr) << 12] = value;
			else
				base.WritePpu(addr, value);
		}

		public override byte[] SaveRam => Vram;

		public override void SyncState(Serializer ser)
		{
			base.SyncState(ser);
			ser.Sync(nameof(prg), ref prg);
			ser.Sync(nameof(chr), ref chr);
			ser.Sync(nameof(irqclock), ref irqclock);
		}

		public override void ClockCpu()
		{
			if (irqclock == 2048 - 1)
			{
				irqclock++;
				IrqSignal = true;
			}
			else if (irqclock < 2048 - 1)
			{
				irqclock++;
			}
		}
	}
}
