using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
	sealed public partial class Vic
	{
		private int delayC;
		private int ecmPixel;
		private int pixel;
		private int pixelCounter;
		private int pixelData;
		private int pixelOwner;
		private int sprData;
		private int sprIndex;
		private int sprPixel;
		private int srC = 0;
		private int srSync = 0;
		private int videoMode;

		private void Render()
		{
			if (hblankCheckEnableL)
			{
				if (rasterX == hblankEnd)
					hblank = false;
			}
			else if (hblankCheckEnableR)
			{
				if (rasterX == hblankStart)
					hblank = true;
			}

			renderEnabled = (!hblank && !vblank);
			pixelCounter = -1;
			while (pixelCounter++ < 3)
			{

				if (delayC > 0)
					delayC--;
				else
					displayC = (srC >> 12) & 0xFFF;


				#region PRE-RENDER BORDER
				if (borderCheckLEnable && (rasterX == borderL))
				{
					if (rasterLine == borderB)
						borderOnVertical = true;
					if (rasterLine == borderT && displayEnable)
						borderOnVertical = false;
					if (!borderOnVertical)
						borderOnMain = false;
				}
				#endregion

				#region CHARACTER GRAPHICS
				switch (videoMode)
				{
					case 0:
						pixelData = sr & srMask2;
						pixel = (pixelData != 0) ? (displayC >> 8) : backgroundColor0;
						break;
					case 1:
						if ((displayC & 0x800) != 0)
						{
							// multicolor 001
							if ((srSync & srMask2) != 0)
								pixelData = sr & srMask3;

							if (pixelData == 0)
								pixel = backgroundColor0;
							else if (pixelData == srMask1)
								pixel = backgroundColor1;
							else if (pixelData == srMask2)
								pixel = backgroundColor2;
							else
								pixel = (displayC & 0x700) >> 8;
						}
						else
						{
							// standard 001
							pixelData = sr & srMask2;
							pixel = (pixelData != 0) ? (displayC >> 8) : backgroundColor0;
						}
						break;
					case 2:
						pixelData = sr & srMask2;
						pixel = (pixelData != 0) ? (displayC >> 4) : (displayC);
						break;
					case 3:
						if ((srSync & srMask2) != 0)
							pixelData = sr & srMask3;

						if (pixelData == 0)
							pixel = backgroundColor0;
						else if (pixelData == srMask1)
							pixel = (displayC >> 4);
						else if (pixelData == srMask2)
							pixel = displayC;
						else
							pixel = (displayC >> 8);
						break;
					case 4:
						pixelData = sr & srMask2;
						if (pixelData != 0)
						{
							pixel = displayC >> 8;
						}
						else
						{
							ecmPixel = (displayC) & 0xC0;
							if (ecmPixel == 0x00)
								pixel = backgroundColor0;
							else if (ecmPixel == 0x40)
								pixel = backgroundColor1;
							else if (ecmPixel == 0x80)
								pixel = backgroundColor2;
							else
								pixel = backgroundColor3;
						}
						break;
					default:
						pixelData = 0;
						pixel = 0;
						break;
				}
				pixel &= 0xF;
				sr <<= 1;
				srSync <<= 1;
#endregion

				#region SPRITES
				// render sprites
				pixelOwner = -1;
				sprIndex = 0;
				foreach (var spr in sprites)
				{
					sprData = 0;
					sprPixel = pixel;

					if (spr.x == rasterX)
					{
						spr.shiftEnable = spr.display;
						spr.xCrunch = !spr.xExpand;
						spr.multicolorCrunch = false;
					}
					else
					{
						spr.xCrunch |= !spr.xExpand;
					}

					if (spr.shiftEnable) // sprite rule 6
					{
						if (spr.multicolor)
						{
							sprData = (spr.sr & srSpriteMaskMC);
							if (spr.multicolorCrunch && spr.xCrunch && !rasterXHold)
							{
								if (spr.loaded == 0)
								{
									spr.shiftEnable = false;
								}
								spr.sr <<= 2;
								spr.loaded >>= 2;
							}
							spr.multicolorCrunch ^= spr.xCrunch;
						}
						else
						{
							sprData = (spr.sr & srSpriteMask);
							if (spr.xCrunch && !rasterXHold)
							{
								if (spr.loaded == 0)
								{
									spr.shiftEnable = false;
								}
								spr.sr <<= 1;
								spr.loaded >>= 1;
							}
						}
						spr.xCrunch ^= spr.xExpand;

						if (sprData != 0)
						{
							// sprite-sprite collision
							if (pixelOwner < 0)
							{
								if (sprData == srSpriteMask1)
									sprPixel = spriteMulticolor0;
								else if (sprData == srSpriteMask2)
									sprPixel = spr.color;
								else if (sprData == srSpriteMask3)
									sprPixel = spriteMulticolor1;
								pixelOwner = sprIndex;
							}
							else
							{
								if (!borderOnVertical)
								{
									spr.collideSprite = true;
									sprites[pixelOwner].collideSprite = true;
								}
							}

							// sprite-data collision
							if (!borderOnVertical && (pixelData >= srMask2))
							{
								spr.collideData = true;
							}

							// sprite priority logic
							if (spr.priority)
							{
								pixel = (pixelData >= srMask2) ? pixel : sprPixel;
							}
							else
							{
								pixel = sprPixel;
							}
						}
					}

					sprIndex++;
				}

#endregion

				#region POST-RENDER BORDER
				if (borderCheckREnable && (rasterX == borderR))
					borderOnMain = true;

				// border doesn't work with the background buffer
				if (borderOnMain || borderOnVertical)
					pixel = borderColor;
				#endregion

				// plot pixel if within viewing area
				if (renderEnabled)
				{
					buf[bufOffset] = palette[pixBuffer[pixBufferIndex]];
					bufOffset++;
					if (bufOffset == bufLength)
						bufOffset = 0;
				}

				pixBuffer[pixBufferIndex] = pixel;
				pixBufferIndex++;

				if (!rasterXHold)
					rasterX++;
				bitmapColumn++;
			}

			if (pixBufferIndex >= pixBufferSize)
				pixBufferIndex = 0;
		}
	}
}
