namespace BizHawk.Emulation.Cores.Intellivision
{
	public sealed class Cartridge : ICart
	{
		private ushort[] Data = new ushort[56320];

		public int Parse(byte[] Rom)
		{
			// TODO: Determine which loading method, if either, is correct.
			int index = 0;
			// Combine every two bytes into a word.
			while (index + 1 < Rom.Length)
			{
				Data[(index / 2) + 0x2C00] = (ushort)((Rom[index++] << 8) | Rom[index++]);
			}
			/*
			for (int index = 0; index < Rom.Length; index++)
				Data[index + 0x2C00] = Rom[index];
			*/
			return Rom.Length;
		}

		public ushort? ReadCart(ushort addr)
		{
			// TODO: Check if address is RAM / ROM.
			int dest;
			switch (addr & 0xF000)
			{
				case 0x0000:
					dest = addr - 0x0400;
					if (addr <= 0x03FF)
					{
						break;
					}
					else if (addr <= 0x04FF)
					{
						// OK on all but Intellivision 2.
						return Data[dest];
					}
					else if (addr <= 0x06FF)
					{
						return Data[dest];
					}
					else if (addr <= 0x0CFF)
					{
						// OK if no Intellivoice.
						return Data[dest];
					}
					else
					{
						return Data[dest];
					}
				case 0x2000:
					dest = (addr - 0x2000) + 0x0C00;
					// OK if no ECS.
					return Data[dest];
				case 0x4000:
					dest = (addr - 0x4000) + 0x1C00;
					if (addr <= 0x47FF)
					{
						// OK if no ECS.
						return Data[dest];
					}
					else if (addr == 0x4800)
					{
						// return Data[dest];
						// For now, assume unmapped. TODO: Fix.
						return null;
					}
					else
					{
						return Data[dest];
					}
				case 0x5000:
				case 0x6000:
					dest = (addr - 0x5000) + 0x2C00;
					if (addr <= 0x5014)
					{
						return Data[dest];
					}
					else
					{
						return Data[dest];
					}
				case 0x7000:
					dest = (addr - 0x7000) + 0x4C00;
					if (addr == 0x7000)
					{
						// OK if no ECS.
						// return Data[dest];
						// For now, assume unmapped. TODO: Fix.
						return null;
					}
					else if (addr <= 0x77FF)
					{
						// OK if no ECS.
						return Data[dest];
					}
					else
					{
						// OK if no ECS.
						return Data[dest];
					}
				case 0x8000:
					dest = (addr - 0x8000) + 0x5C00;
					// OK. Avoid STIC alias at $8000-$803F.
					return Data[dest];
				case 0x9000:
				case 0xA000:
				case 0xB000:
					dest = (addr - 0x9000) + 0x6C00;
					if (addr <= 0xB7FF)
					{
						return Data[dest];
					}
					else
					{
						return Data[dest];
					}
				case 0xC000:
					dest = (addr - 0xC000) + 0x9C00;
					// OK. Avoid STIC alias at $C000-$C03F.
					return Data[dest];
				case 0xD000:
					dest = (addr - 0xD000) + 0xAC00;
					return Data[dest];
				case 0xE000:
					dest = (addr - 0xE000) + 0xBC00;
					// OK if no ECS.
					return Data[dest];
				case 0xF000:
					dest = (addr - 0xF000) + 0xCC00;
					if (addr <= 0xF7FF)
					{
						return Data[dest];
					}
					else
					{
						return Data[dest];
					}
			}
			return null;
		}

		public bool WriteCart(ushort addr, ushort value)
		{
			int dest;
			// TODO: Check if address is RAM / ROM.
			switch (addr & 0xF000)
			{
				case 0x0000:
					dest = addr - 0x0400;
					if (addr <= 0x03FF)
					{
						break;
					}
					else if (addr <= 0x04FF)
					{
						// OK on all but Intellivision 2.
						Data[dest] = value;
						return true;
					}
					else if (addr <= 0x06FF)
					{
						Data[dest] = value;
						return true;
					}
					else if (addr <= 0x0CFF)
					{
						// OK if no Intellivoice.
						Data[dest] = value;
						return true;
					}
					else
					{
						Data[dest] = value;
						return true;
					}
				case 0x2000:
					dest = (addr - 0x2000) + 0x0C00;
					// OK if no ECS.
					Data[dest] = value;
					return true;
				case 0x4000:
					dest = (addr - 0x4000) + 0x1C00;
					if (addr <= 0x47FF)
					{
						// OK if no ECS.
						Data[dest] = value;
						return true;
					}
					else if (addr == 0x4800)
					{
						// OK only if boot ROM at $7000.
						Data[dest] = value;
						return true;
					}
					else
					{
						Data[dest] = value;
						return true;
					}
				case 0x5000:
				case 0x6000:
					dest = (addr - 0x5000) + 0x2C00;
					if (addr <= 0x5014)
					{
						// OK only if boot ROM at $4800 or $7000.
						Data[dest] = value;
						return true;
					}
					else
					{
						Data[dest] = value;
						return true;
					}
				case 0x7000:
					dest = (addr - 0x7000) + 0x4C00;
					if (addr == 0x7000)
					{
						// RAM at $7000 confuses EXEC boot sequence.
						Data[dest] = value;
						return true;
					}
					else if (addr <= 0x77FF)
					{
						// OK if no ECS.
						Data[dest] = value;
						return true;
					}
					else
					{
						// Do not map RAM here due to GRAM alias.
						Data[dest] = value;
						return true;
					}
				case 0x8000:
					dest = (addr - 0x8000) + 0x5C00;
					// OK. Avoid STIC alias at $8000-$803F.
					Data[dest] = value;
					return true;
				case 0x9000:
				case 0xA000:
				case 0xB000:
					dest = (addr - 0x9000) + 0x6C00;
					if (addr <= 0xB7FF)
					{
						Data[dest] = value;
						return true;
					}
					else
					{
						// Do not map RAM here due to GRAM alias.
						Data[dest] = value;
						return true;
					}
				case 0xC000:
					dest = (addr - 0xC000) + 0x9C00;
					// OK. Avoid STIC alias at $C000-$C03F.
					Data[dest] = value;
					return true;
				case 0xD000:
					Data[(addr & 0x0FFF) + 0xAC00] = value;
					return true;
				case 0xE000:
					dest = (addr - 0xE000) + 0xBC00;
					// OK if no ECS.
					Data[dest] = value;
					return true;
				case 0xF000:
					dest = (addr - 0xF000) + 0xCC00;
					if (addr <= 0xF7FF)
					{
						Data[dest] = value;
						return true;
					}
					else
					{
						// Do not map RAM here due to GRAM alias.
						Data[dest] = value;
						return true;
					}
			}
			return false;
		}
	}
}
