//-----------------------------LICENSE NOTICE------------------------------------
//  This file is part of CPCtelera: An Amstrad CPC Game Engine
//  Copyright (C) 2016 Arnaud Bouche
//  Copyright (C) 2016 ronaldo / Fremos / Cheesetea / ByteRealms (@FranGallegoBR)
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//-------------------------------------------------------------------------------

#include "winCpctelera.h"

#ifdef _USESDL
/** SDL2 - 2.0.5 */
#pragma comment(lib,"SDL2.lib")

#define JOY_AXIS_X				0
#define JOY_AXIS_Y				1
#define JOY_MID_VALUE			(Sint16)(USHRT_MAX / 2)
#define JOY_LIMIT_VALUE			(Sint16)(JOY_MID_VALUE - 1000)

#define GetRedValue(rgb)		(rgb & 0xFF)
#define GetGreenValue(rgb)		((rgb >> 8) & 0xFF)
#define GetBlueValue(rgb)		((rgb >>16) & 0xFF)

extern DWORD wincpct_getColorHW(int pHW);
extern u8* wincpct_getRenderingBuffer();
extern u16 wincpct_getCpcKey(u16 pVKeyID);

static BOOL _joystickOK;
static SDL_Joystick* _SDLJoystick;
static SDL_Window* _SDLWnd;
static SDL_Renderer* _SDLRender;
static SDL_Texture *_SDLDisplay;

static HANDLE _EndInterruptEvent;
static HANDLE _vsyncEvent;
static BOOL _runInterrupt;

static const u8 sInterruptArea[] = { 25, 53, 52, 52, 52, 38 };

static const UCHAR sIconDataFile[] = { 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0xE8, 0x02, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x0A, 0xAA, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xBB, 0xBF, 0xBB, 0x00, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xBB, 0xBF, 0xBB, 0x0A, 0xAA, 0xAA, 0xAA, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xBB, 0xFB, 0xBF, 0xB0, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xBB, 0xBF, 0xBF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xBB, 0xBB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xBB, 0xBF, 0xBF, 0xFB, 0xBB, 0xF0, 0x08, 0xF8, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xBB, 0xFB, 0xBF, 0xBF, 0xBB, 0xF0, 0xF0, 0x80, 0xF0, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xBB, 0xBF, 0xBB, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xBB, 0xBF, 0xBB, 0xF0, 0x0F, 0xFF, 0xF0, 0xF0, 0x80, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x08, 0xF8, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0, 0x80, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFC, 0x0E, 0x1F, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xF0, 0x08, 0x07, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x03, 0xF8, 0x06, 0x00, 0x07, 0xFC, 0x0F, 0x00, 0x0F, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

void wincpct_waitEndInterrupt()
{
	WaitForSingleObject(_EndInterruptEvent, INFINITE);
}

static void wincpct_close()
{
	_runInterrupt = FALSE;
	CloseHandle(_EndInterruptEvent);
	wincpct_waitEndInterrupt();

	SDL_DestroyWindow(_SDLWnd);
	SDL_DestroyRenderer(_SDLRender);
	SDL_DestroyTexture(_SDLDisplay);
	SDL_JoystickClose(_SDLJoystick);
	SDL_Quit();
}

static int wincpct_getXAxis()
{
	if (_joystickOK)
	{
		Sint16 curX = SDL_JoystickGetAxis(_SDLJoystick, JOY_AXIS_X);
		if (curX > JOY_LIMIT_VALUE)
			return 1;

		if (curX < -JOY_LIMIT_VALUE)
			return -1;

		Sint16 pov = SDL_JoystickGetHat(_SDLJoystick, 0);
		if (pov != SDL_HAT_CENTERED)
		{
			if (pov == SDL_HAT_RIGHT)
				return 1;

			if (pov == SDL_HAT_LEFT)
				return -1;
		}
	}
	return 0;
}

static int wincpct_getYAxis()
{
	if (_joystickOK)
	{
		Sint16 curY = SDL_JoystickGetAxis(_SDLJoystick, JOY_AXIS_Y);
		if (curY > JOY_LIMIT_VALUE)
			return -1;

		if (curY < -JOY_LIMIT_VALUE)
			return 1;

		int pov = SDL_JoystickGetHat(_SDLJoystick, 0);
		if (pov != SDL_HAT_CENTERED)
		{
			if (pov == SDL_HAT_UP)
				return 1;

			if (pov == SDL_HAT_DOWN)
				return -1;
		}
	}
	return 0;
}

static int wincpct_getJoystickButton()
{
	if (_joystickOK)
	{
		if (SDL_JoystickGetButton(_SDLJoystick, 0))
			return 1;

		if (SDL_JoystickGetButton(_SDLJoystick, 1))
			return 2;
	}
	return 0;
}

static void wincpct_getAsyncJoystickState()
{
	if (_joystickOK)
	{
		SDL_JoystickUpdate();
		
		if (wincpct_getJoystickButton() != 0 || wincpct_getXAxis() != 0 || wincpct_getYAxis() != 0)
			gCurKey = TRUE;
	}
}

static void wincpct_getRectScreen(int screenPart, RECT* pRect)
{
	int pos = 0;
	for (int i = 0; i < INTERRUPT_PER_VBL; i++)
	{
		RECT rect = { 0, pos, FULL_SCREEN_CX, pos + sInterruptArea[i] * COEF };

		pos += sInterruptArea[i] * COEF;

		if (i == screenPart)
			*pRect = rect;
	}
}

static void wincpct_createPaletteCPC(SDL_Surface* pSurface)
{
	SDL_Color colors[NB_COLORS];

	for (int i = 0; i < NB_COLORS; i++)
	{
		int hw = gAmstrad._curVideoConf.gCpcPalette[i];
		uint32_t colorHW = wincpct_getColorHW(hw);

		colors[i] = *(SDL_Color*)(&colorHW);
	}

	SDL_SetPaletteColors(pSurface->format->palette, colors, 0, NB_COLORS);
}

static void wincpct_fillBorder(RECT* pRect)
{
	int hw = gAmstrad._curVideoConf.gCpcPalette[BORDER_COLOR];
	DWORD RGB = wincpct_getColorHW(hw);

	SDL_SetRenderDrawColor(_SDLRender, GetRedValue(RGB), GetGreenValue(RGB), GetBlueValue(RGB), 255);

	SDL_Rect rectFill = { pRect->left, pRect->top, pRect->right, pRect->bottom };
	SDL_RenderFillRect(_SDLRender, &rectFill);
}

static void wincpct_renderScreen(int screenPart)
{
	RECT rectPart;
	wincpct_getRectScreen(screenPart, &rectPart);

	RECT drawScreen = { 0, BORDER_UP_CY, WIDTH_SCREEN,  BORDER_UP_CY + HEIGHT_SCREEN };

	IntersectRect(&drawScreen, &drawScreen, &rectPart);

	int y = drawScreen.top / COEF;
	int cy = (drawScreen.bottom - drawScreen.top) / COEF;

	wincpct_fillBorder(&rectPart);

	if (cy > 0)
	{
		int yBitmap = y - BORDER_UP_CY / COEF;

		u8* renderBuffer = wincpct_getRenderingBuffer() + yBitmap * WIDTH_SCREEN;

		SDL_Surface* surfaceBuffer = SDL_CreateRGBSurfaceWithFormatFrom(renderBuffer,
																WIDTH_SCREEN, cy * COEF,
																8, WIDTH_SCREEN, SDL_PIXELFORMAT_INDEX8);

		wincpct_createPaletteCPC(surfaceBuffer);

		SDL_Texture* textureBuffer = SDL_CreateTextureFromSurface(_SDLRender, surfaceBuffer);
		
		SDL_Rect dest = { BORDER_CX, y * COEF, WIDTH_SCREEN, cy * COEF };
		SDL_Rect src = { 0, 0, WIDTH_SCREEN, cy };

		SDL_RenderCopy(_SDLRender, textureBuffer, &src, &dest);

		SDL_FreeSurface(surfaceBuffer); 
		SDL_DestroyTexture(textureBuffer);
	}
}

static void wincpct_redraw()
{
	SDL_SetRenderTarget(_SDLRender, NULL);

	SDL_RenderCopy(_SDLRender, _SDLDisplay, NULL, NULL);
	SDL_RenderPresent(_SDLRender);

	SDL_SetRenderTarget(_SDLRender, _SDLDisplay);
}

static void wincpct_keyEvent(u16 pKey)
{
	gCurKey = TRUE;

	unsigned short key = wincpct_getCpcKey(pKey);
	cpct_keyboardStatusBuffer[key & 0x0000F] = 0xFF ^ (key >> 8);
}

u8 wincpct_getAsyncJoyState(u16 vKey)
{
	if (vKey == VK_UP && wincpct_getYAxis() == 1)
		return 1;

	if (vKey == VK_DOWN && wincpct_getYAxis() == -1)
		return 1;

	if (vKey == VK_RIGHT && wincpct_getXAxis() == 1)
		return 1;

	if (vKey == VK_LEFT && wincpct_getXAxis() == -1)
		return 1;

	if (vKey == VK_SPACE && wincpct_getJoystickButton() == 1)
		return 1;

	if (vKey == VK_CONTROL && wincpct_getJoystickButton() == 2)
		return 1;

	return 0;
}

static void wincpct_initJoystick()
{
	_joystickOK = TRUE;

	if (SDL_NumJoysticks() == 0)
		_joystickOK = FALSE;
	else
	{
		_SDLJoystick = SDL_JoystickOpen(0);
		if (_SDLJoystick == NULL)
			_joystickOK = FALSE;
	}
}

static HICON wincpct_createIcon(PBYTE iconData, int iconSize)
{
	HICON hIcon = NULL;
	int offset = LookupIconIdFromDirectoryEx(iconData, TRUE, iconSize, iconSize, LR_DEFAULTCOLOR);

	if (offset != 0)
		hIcon = CreateIconFromResourceEx(iconData + offset, 0, TRUE, 0x30000, iconSize, iconSize, LR_DEFAULTCOLOR);

	return hIcon;
}

void wincpct_createWindowApp()
{
	SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);

	_SDLWnd = SDL_CreateWindow(	"WinCPCTelera (SDL)", 
								SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
								FULL_SCREEN_CX, FULL_SCREEN_CY,
								SDL_WINDOW_SHOWN);

	_SDLRender = SDL_CreateRenderer(_SDLWnd, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

	/* Create texture for display */
	_SDLDisplay = SDL_CreateTexture(_SDLRender, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, FULL_SCREEN_CX, FULL_SCREEN_CY);
	SDL_SetRenderTarget(_SDLRender, _SDLDisplay);

	SendMessage(GetActiveWindow(), WM_SETICON, ICON_BIG, (LPARAM)wincpct_createIcon(sIconDataFile, 32));

	wincpct_initJoystick();

	wincpct_msgLoop();
}

void wincpct_msgLoop()
{
	SDL_Event event;

	while (SDL_PollEvent(&event)) 
	{
		switch (event.type)
		{
		case SDL_KEYDOWN:
			wincpct_keyEvent(event.key.keysym.sym);
			break;

		case SDL_QUIT:
			wincpct_close();
			exit(0);
		}
	}
}

void wincpct_wait(int ms)
{
	SDL_Delay(ms);
}

static int wincpct_interruptFunction(LPVOID lpParam)
{
	SAmstrad* amstrad = (SAmstrad*)lpParam;
	DWORD time = SDL_GetTicks();

	while (_runInterrupt)
	{
		int elapse = SDL_GetTicks() - time;
		if (elapse > INTERRUPT_MS)
		{
			time = SDL_GetTicks();

			if (amstrad->_internalTimer == INTERRUPT_PER_VBL)
			{
				amstrad->_internalTimer = 0;
				SetEvent(_vsyncEvent);
				wincpct_redraw();

				wincpct_getAsyncJoystickState();
			}

			if (amstrad->_interruptFunction != NULL)
				amstrad->_interruptFunction();

			wincpct_renderScreen(amstrad->_internalTimer);

			amstrad->_internalTimer++;
		}

		wincpct_wait(1);
		SetEvent(_EndInterruptEvent);
	}

	return 0;
}

static void wincpct_createInterruptThread()
{
	gAmstrad._internalTimer = 0;
	_runInterrupt = TRUE;

	SDL_CreateThread(wincpct_interruptFunction, "CPC_INTERRUPT", &gAmstrad);
}

void wincpct_waitVSync()
{
	WaitForSingleObject(_vsyncEvent, INFINITE);
}

void wincpct_setInterruptFunction(void(*intHandler)(void))
{
	if (!_runInterrupt)
		wincpct_createInterruptThread();
	
	gAmstrad._interruptFunction = intHandler;
	WaitForSingleObject(_vsyncEvent, INFINITE);
}

void wincpct_startInterrupt()
{
	_EndInterruptEvent = CreateEvent(
		NULL,               // default security attributes
		TRUE,               // manual-reset event
		FALSE,              // initial state is nonsignaled
		TEXT("StartInterr")	// object name
	);

	_vsyncEvent = CreateEvent(
		NULL,               // default security attributes
		FALSE,               // auto-reset event
		FALSE,              // initial state is nonsignaled
		TEXT("VSync")		// object name
	);

	wincpct_createInterruptThread();
}
#endif