using System.IO;

namespace MyNes.Core;

[BoardInfo("VRC4", 21)]
internal class Mapper021 : Board
{
	private bool prg_mode;

	private byte prg_reg0;

	private int[] chr_Reg;

	private int irq_reload;

	private int irq_counter;

	private int prescaler;

	private bool irq_mode_cycle;

	private bool irq_enable;

	private bool irq_enable_on_ak;

	internal override void HardReset()
	{
		base.HardReset();
		Switch16KPRG(PRG_ROM_16KB_Mask, PRGArea.AreaC000);
		prescaler = 341;
		chr_Reg = new int[8];
	}

	internal override void WritePRG(ref ushort address, ref byte data)
	{
		switch (address)
		{
		case 32768:
		case 32770:
		case 32772:
		case 32774:
		case 32832:
		case 32896:
		case 32960:
			prg_reg0 = data;
			Switch08KPRG(prg_mode ? (PRG_ROM_08KB_Mask - 1) : (prg_reg0 & 0x1F), PRGArea.Area8000);
			Switch08KPRG(prg_mode ? (prg_reg0 & 0x1F) : (PRG_ROM_08KB_Mask - 1), PRGArea.AreaC000);
			break;
		case 36864:
		case 36866:
		case 36928:
			switch (data & 3)
			{
			case 0:
				Switch01KNMTFromMirroring(Mirroring.Vert);
				break;
			case 1:
				Switch01KNMTFromMirroring(Mirroring.Horz);
				break;
			case 2:
				Switch01KNMTFromMirroring(Mirroring.OneScA);
				break;
			case 3:
				Switch01KNMTFromMirroring(Mirroring.OneScB);
				break;
			}
			break;
		case 36868:
		case 36870:
		case 36992:
		case 37056:
			prg_mode = (data & 2) == 2;
			Switch08KPRG(prg_mode ? (PRG_ROM_08KB_Mask - 1) : (prg_reg0 & 0x1F), PRGArea.Area8000);
			Switch08KPRG(prg_mode ? (prg_reg0 & 0x1F) : (PRG_ROM_08KB_Mask - 1), PRGArea.AreaC000);
			break;
		case 40960:
		case 40962:
		case 40964:
		case 40966:
		case 41024:
		case 41088:
		case 41152:
			Switch08KPRG(data & 0x1F, PRGArea.AreaA000);
			break;
		case 45056:
			chr_Reg[0] = (chr_Reg[0] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[0], CHRArea.Area0000);
			break;
		case 45058:
		case 45120:
			chr_Reg[0] = (chr_Reg[0] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[0], CHRArea.Area0000);
			break;
		case 45060:
		case 45184:
			chr_Reg[1] = (chr_Reg[1] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[1], CHRArea.Area0400);
			break;
		case 45062:
		case 45248:
			chr_Reg[1] = (chr_Reg[1] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[1], CHRArea.Area0400);
			break;
		case 49152:
			chr_Reg[2] = (chr_Reg[2] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[2], CHRArea.Area0800);
			break;
		case 49154:
		case 49216:
			chr_Reg[2] = (chr_Reg[2] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[2], CHRArea.Area0800);
			break;
		case 49156:
		case 49280:
			chr_Reg[3] = (chr_Reg[3] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[3], CHRArea.Area0C00);
			break;
		case 49158:
		case 49344:
			chr_Reg[3] = (chr_Reg[3] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[3], CHRArea.Area0C00);
			break;
		case 53248:
			chr_Reg[4] = (chr_Reg[4] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[4], CHRArea.Area1000);
			break;
		case 53250:
		case 53312:
			chr_Reg[4] = (chr_Reg[4] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[4], CHRArea.Area1000);
			break;
		case 53252:
		case 53376:
			chr_Reg[5] = (chr_Reg[5] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[5], CHRArea.Area1400);
			break;
		case 53254:
		case 53440:
			chr_Reg[5] = (chr_Reg[5] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[5], CHRArea.Area1400);
			break;
		case 57344:
			chr_Reg[6] = (chr_Reg[6] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[6], CHRArea.Area1800);
			break;
		case 57346:
		case 57408:
			chr_Reg[6] = (chr_Reg[6] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[6], CHRArea.Area1800);
			break;
		case 57348:
		case 57472:
			chr_Reg[7] = (chr_Reg[7] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[7], CHRArea.Area1C00);
			break;
		case 57350:
		case 57536:
			chr_Reg[7] = (chr_Reg[7] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[7], CHRArea.Area1C00);
			break;
		case 61440:
			irq_reload = (irq_reload & 0xF0) | (data & 0xF);
			break;
		case 61442:
		case 61504:
			irq_reload = (irq_reload & 0xF) | ((data & 0xF) << 4);
			break;
		case 61444:
		case 61568:
			irq_mode_cycle = (data & 4) == 4;
			irq_enable = (data & 2) == 2;
			irq_enable_on_ak = (data & 1) == 1;
			if (irq_enable)
			{
				irq_counter = irq_reload;
				prescaler = 341;
			}
			NesEmu.IRQFlags &= -9;
			break;
		case 61446:
		case 61632:
			NesEmu.IRQFlags &= -9;
			irq_enable = irq_enable_on_ak;
			break;
		}
	}

	internal override void OnCPUClock()
	{
		if (!irq_enable)
		{
			return;
		}
		if (!irq_mode_cycle)
		{
			if (prescaler > 0)
			{
				prescaler -= 3;
				return;
			}
			prescaler = 341;
			irq_counter++;
			if (irq_counter == 255)
			{
				NesEmu.IRQFlags |= 8;
				irq_counter = irq_reload;
			}
		}
		else
		{
			irq_counter++;
			if (irq_counter == 255)
			{
				NesEmu.IRQFlags |= 8;
				irq_counter = irq_reload;
			}
		}
	}

	internal override void WriteStateData(ref BinaryWriter stream)
	{
		base.WriteStateData(ref stream);
		stream.Write(prg_mode);
		stream.Write(prg_reg0);
		for (int i = 0; i < chr_Reg.Length; i++)
		{
			stream.Write(chr_Reg[i]);
		}
		stream.Write(irq_reload);
		stream.Write(irq_counter);
		stream.Write(prescaler);
		stream.Write(irq_mode_cycle);
		stream.Write(irq_enable);
		stream.Write(irq_enable_on_ak);
	}

	internal override void ReadStateData(ref BinaryReader stream)
	{
		base.ReadStateData(ref stream);
		prg_mode = stream.ReadBoolean();
		prg_reg0 = stream.ReadByte();
		for (int i = 0; i < chr_Reg.Length; i++)
		{
			chr_Reg[i] = stream.ReadInt32();
		}
		irq_reload = stream.ReadInt32();
		irq_counter = stream.ReadInt32();
		prescaler = stream.ReadInt32();
		irq_mode_cycle = stream.ReadBoolean();
		irq_enable = stream.ReadBoolean();
		irq_enable_on_ak = stream.ReadBoolean();
	}
}