using System.IO;

namespace MyNes.Core;

[BoardInfo("VRC4", 25)]
internal class Mapper025 : 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 32769:
		case 32770:
		case 32771:
		case 32772:
		case 32776:
		case 32780:
			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 36872:
			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 36865:
		case 36867:
		case 36868:
		case 36876:
			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 40961:
		case 40962:
		case 40963:
		case 40964:
		case 40968:
		case 40972:
			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 45064:
			chr_Reg[0] = (chr_Reg[0] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[0], CHRArea.Area0000);
			break;
		case 45057:
		case 45060:
			chr_Reg[1] = (chr_Reg[1] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[1], CHRArea.Area0400);
			break;
		case 45059:
		case 45068:
			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 49160:
			chr_Reg[2] = (chr_Reg[2] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[2], CHRArea.Area0800);
			break;
		case 49153:
		case 49156:
			chr_Reg[3] = (chr_Reg[3] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[3], CHRArea.Area0C00);
			break;
		case 49155:
		case 49164:
			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 53256:
			chr_Reg[4] = (chr_Reg[4] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[4], CHRArea.Area1000);
			break;
		case 53249:
		case 53252:
			chr_Reg[5] = (chr_Reg[5] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[5], CHRArea.Area1400);
			break;
		case 53251:
		case 53260:
			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 57352:
			chr_Reg[6] = (chr_Reg[6] & 0xF) | ((data & 0xF) << 4);
			Switch01KCHR(chr_Reg[6], CHRArea.Area1800);
			break;
		case 57345:
		case 57348:
			chr_Reg[7] = (chr_Reg[7] & 0xF0) | (data & 0xF);
			Switch01KCHR(chr_Reg[7], CHRArea.Area1C00);
			break;
		case 57347:
		case 57356:
			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 61448:
			irq_reload = (irq_reload & 0xF) | ((data & 0xF) << 4);
			break;
		case 61441:
		case 61444:
			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 61443:
		case 61452:
			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();
	}
}