#pragma once
#include "stdafx.h"
#include "EmulationSettings.h"
#include "Console.h"

class DrawCommand
{
private:
	int _frameCount;
	uint32_t* _argbBuffer;
	OverscanDimensions _overscan;
	uint32_t _lineWidth;
	uint32_t _startFrame;

protected:
	bool _useIntegerScaling;
	float _xScale;
	int _yScale;

	virtual void InternalDraw() = 0;
	__forceinline void DrawPixel(uint32_t x, uint32_t y, int color)
	{
		if(x < _overscan.Left || x >= _overscan.Left + _overscan.GetScreenWidth() ||
			y < _overscan.Top || y >= _overscan.Top + _overscan.GetScreenHeight()) {
			//In overscan (out of bounds), skip drawing
			return;
		}

		uint32_t alpha = (color & 0xFF000000);
		if(alpha > 0) {
			if(_yScale == 1) {
				if(alpha != 0xFF000000) {
					BlendColors((uint8_t*)&_argbBuffer[(y - _overscan.Top)*_lineWidth + (x - _overscan.Left)], (uint8_t*)&color);
				} else {
					_argbBuffer[(y - _overscan.Top)*_lineWidth + (x - _overscan.Left)] = color;
				}
			} else {
				int xPixelCount = _useIntegerScaling ? _yScale : (int)((x + 1)*_xScale) - (int)(x*_xScale);
				x = (int)(x * (_useIntegerScaling ? _yScale : _xScale));
				y = (int)(y * _yScale);
				int top = (int)(_overscan.Top * _yScale);
				int left = (int)(_overscan.Left * _xScale);

				for(int i = 0; i < _yScale; i++) {
					for(int j = 0; j < xPixelCount; j++) {
						if(alpha != 0xFF000000) {
							BlendColors((uint8_t*)&_argbBuffer[(y - top)*_lineWidth + i*_lineWidth + (x - left)+j], (uint8_t*)&color);
						} else {
							_argbBuffer[(y - top)*_lineWidth + i*_lineWidth + (x - left) +j] = color;
						}
					}
				}
			}
		}
	}

	__forceinline void BlendColors(uint8_t output[4], uint8_t input[4])
	{
		uint8_t alpha = input[3] + 1;
		uint8_t invertedAlpha = 256 - input[3];
		output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8);
		output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8);
		output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8);
		output[3] = 0xFF;
	}

public:
	DrawCommand(int startFrame, int frameCount, bool useIntegerScaling = false)
	{ 
		_frameCount = frameCount > 0 ? frameCount : -1;
		_startFrame = startFrame;
		_useIntegerScaling = useIntegerScaling;
	}

	virtual ~DrawCommand()
	{
	}

	void Draw(uint32_t* argbBuffer, OverscanDimensions &overscan, uint32_t lineWidth, uint32_t frameNumber)
	{
		if(_startFrame <= frameNumber) {
			_argbBuffer = argbBuffer;
			_overscan = overscan;
			_lineWidth = lineWidth;
			_yScale = lineWidth / overscan.GetScreenWidth();
			_xScale = (float)lineWidth / overscan.GetScreenWidth();

			InternalDraw();

			_frameCount--;
		}
	}

	bool Expired()
	{
		return _frameCount == 0;
	}
};
