using System;
using BizHawk.Common;

namespace BizHawk.Emulation.Cores.Sega.Genesis
{
	public partial class GenVDP
	{
		// TODO: make this a requirement of constructor?
		public Func<int, short> DmaReadFrom68000; // TODO make ushort

		public int DmaLength { get { return Registers[19] | (Registers[20] << 8); } }
		public int DmaMode { get { return (Registers[23] >> 6) & 3; } }

		public int DmaSource
		{
			get
			{
				if ((Registers[23] & 0x80) == 0) // 68000 -> VRAM copy mode
					return ((Registers[21] << 1) | (Registers[22] << 9) | (Registers[23] << 17)) & 0xFFFFFE;

				// Else VRAM/VRAM copy mode
				return (Registers[21] | (Registers[22] << 8)) & 0xFFFFFE;
			}
		}

		bool DmaFillModePending;

		void ExecuteVramFill(ushort data)
		{
			if (data != 0)
				Console.WriteLine("fill word is not zero {0:X4}", data);

			Log.Note("VDP", "DMA FILL REQD, WRITE {0:X4}, {1:X4} times, at {2:X4}", data, DmaLength, VdpDataAddr);

			// TODO: It should spread this out, not do it all at once.

			int length = DmaLength;
			if (length == 0)
				length = 0x10000;

			byte fillByte = (byte)(data >> 8);

			do
			{
				VRAM[VdpDataAddr] = fillByte;
				Log.Note("VDP", "VRAM DMA FILL Write: [{0:X4}] = {1:X2}", VdpDataAddr, fillByte);
				UpdatePatternBuffer(VdpDataAddr);
				VdpDataAddr += Registers[15];
			} while (--length > 0);

			// TOOD: test if the length register updated? One would assume so...
			Registers[19] = 0;
			Registers[20] = 0;

			// TODO: Source registers should be incremented also (even for Fill)

			DmaFillModePending = false;
		}

		void Execute68000VramCopy()
		{
			Log.Note("VDP", "DMA 68000 -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);

			int length = DmaLength;
			if (length == 0)
				length = 0x10000;

			int source = DmaSource;

			do
			{
				ushort value = (ushort)DmaReadFrom68000(source);
				source += 2;
				// TODO funky source behavior
				WriteVdpData(value);
			} while (--length > 0);

			Registers[19] = 0;
			Registers[20] = 0;

			// TODO: update DMA source registers.
			// TODO: find correct number of 68k cycles to burn
		}

		void ExecuteVramVramCopy()
		{
			Log.Note("VDP", "DMA VRAM -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);

			int length = DmaLength;
			if (length == 0)
				length = 0x10000;

			int source = DmaSource;

			do
			{
				byte data = VRAM[source];
				VRAM[VdpDataAddr] = data;
				UpdatePatternBuffer(VdpDataAddr);
				Log.Note("VDP", "VRAM/VRAM Copy VRAM[{0:X4}] = {1:X2}", VdpDataAddr, data);
				source = (source + 1) & 0xFFFF;
				VdpDataAddr += Registers[0xF];
			} while (--length > 0);

			Registers[19] = 0;
			Registers[20] = 0;

			// TODO: length, source registers should be updated....
			// TODO: find correct number of 68k cycles to burn
		}
	}
}