using BizHawk.Common;
using BizHawk.Common.NumberExtensions;

namespace BizHawk.Emulation.Cores.Nintendo.NES
{
	public sealed class Mapper50 : NES.NESBoardBase
	{
		//http://wiki.nesdev.com/w/index.php/INES_Mapper_050

		byte prg_bank;
		int prg_bank_mask_8k;
		bool irq_enable;
		ushort irq_counter = 0;
		bool irq_ready = false;
		int ppu_cyclecount = 0;
		public override bool Configure(NES.EDetectionOrigin origin)
		{
			//analyze board type
			switch (Cart.board_type)
			{
				case "MAPPER050":
					break;
				default:
					return false;
			}
			prg_bank = 0;
			prg_bank_mask_8k = Cart.prg_size / 8 - 1;
			SetMirrorType(EMirrorType.Vertical);
			return true;
		}

		public override void SyncState(Serializer ser)
		{
			ser.Sync("ppu_cyclecount", ref ppu_cyclecount);
			ser.Sync("prg_bank", ref prg_bank);
			ser.Sync("irq_enable", ref irq_enable);
			ser.Sync("irq_counter", ref irq_counter);
			ser.Sync("irq_ready", ref irq_ready);
			base.SyncState(ser);
		}

		public override void WriteEXP(int addr, byte value)
		{
			addr &= 0x0120;
			if (addr == 0x0020)
			{
				prg_bank = (byte)(((value & 1) << 2) | ((value & 2) >> 1) | ((value & 4) >> 1) | (value & 8));
			}
			else if (addr == 0x0120)
			{
				irq_enable = value.Bit(0);
				if (!irq_enable)
				{
					SyncIRQ(irq_enable);
					irq_counter = 0;
				}
			}
		}

		public override byte ReadPRG(int addr)
		{
			if (addr < 0x2000)
			{
				return ROM[(0x08 * 0x2000) + (addr & 0x1FFF)];
			}
			else if (addr < 0x4000)
			{
				return ROM[(0x09 * 0x2000) + (addr & 0x1FFF)];
			}
			else if (addr < 0x6000)
			{
				int bank = (prg_bank & prg_bank_mask_8k);
				return ROM[(bank * 0x2000) + (addr & 0x1FFF)];
			}
			else
			{
				return ROM[(0x0B * 0x2000) + (addr & 0x1FFF)];
			}
		}

		public override byte ReadWRAM(int addr)
		{
			return ROM[(0x0F * 0x2000) + (addr & 0x1FFF)];
		}

		private void IRQ_Ready()
		{
			base.SyncIRQ(irq_ready);
		}

		public override void  ClockCPU()
		{
			if (irq_enable)
			{
				irq_counter += 1;
				if (irq_counter == 0x1000)
					SyncIRQ(true);
			}
		}

		/*
		public override void ClockPPU()
		{
			ppu_cyclecount++;
			if (ppu_cyclecount >= 3)
			{
				ppu_cyclecount = 0;
				IRQ_Tick();
				base.ClockPPU();
			}
		}*/
	}
}
