
/* $Id: sdl_v.c 8174 2007-01-17 00:20:23Z Darkvater $ */

#include "../stdafx.h"

#ifdef WITH_SDL

#include "../openttd.h"
#include "../debug.h"
#include "../functions.h"
#include "../gfx.h"
#include "../macros.h"
#include "../sdl.h"
#include "../window.h"
#include "../network.h"
#include "../variables.h"
#include "../screenshot.h"
#include "sdl_v.h"
#include <SDL.h>

static bool _all_modes;

#define MAX_DIRTY_RECTS 100
static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS+1];
static int _num_dirty_rects = MAX_DIRTY_RECTS;

#if defined(ARM)
void ShowCheatWindow(void);
//#include "../danzeff.c"
static struct GXKeyList gxKeys;
static SDL_Surface *_hw_screen;
static SDL_Surface *_sw_screen;
int square;
static unsigned int _width, _height;
bool _stretched = false, _rotated = false;
bool _keymove = false;
static float zoom_x = 1, zoom_y = 1;
#else
static SDL_Surface *_sdl_screen;
#endif

#if defined(PSP)
#include "../gui.h"
#include "../table/strings.h"
#include "../network_data.h"
#include <pspctrl.h>
#include <psppower.h>
#include <pspthreadman.h>

extern int ShowPspOSK(void);

SceCtrlData pad;
static int lx, ly, mx, my, left, right, delw;
int square, freq_level;

/* Available frequencies - cpufreq, ramfreq, busfreq */
uint16 psp_frequencies[3][3] = {
    { 222, 222, 111 },
    { 266, 266, 122 },
    { 333, 333, 133 },
};
#endif //PSP

static void SdlVideoMakeDirty(int left, int top, int width, int height)
{
	if (_num_dirty_rects < MAX_DIRTY_RECTS) {
		_dirty_rects[_num_dirty_rects].x = left;
		_dirty_rects[_num_dirty_rects].y = top;
		_dirty_rects[_num_dirty_rects].w = width;
		_dirty_rects[_num_dirty_rects].h = height;
	}
	_num_dirty_rects++;
}

static void UpdatePalette(uint start, uint count)
{
	SDL_Color pal[256];
	uint i;

	for (i = 0; i != count; i++) {
		pal[i].r = _cur_palette[start + i].r;
		pal[i].g = _cur_palette[start + i].g;
		pal[i].b = _cur_palette[start + i].b;
		pal[i].unused = 0;
	}

	SDL_CALL SDL_SetColors(_hw_screen, pal, start, count);
}

static void InitPalette(void)
{
	UpdatePalette(0, 256);
}

static void CheckPaletteAnim(void)
{
	if (_pal_last_dirty != -1) {
		UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1);
		_pal_last_dirty = -1;
	}
}

static void DrawSurfaceToScreen(void)
{
		int n = _num_dirty_rects;
		_num_dirty_rects = 0;
		if ( _stretched == true )
		{
			SDL_CALL SDL_SoftStretch(	_sw_screen, NULL,
										_hw_screen, NULL);
						
			SDL_CALL SDL_UpdateRect(_hw_screen, 0, 0, 0, 0);
		}
		else if( n >= MAX_DIRTY_RECTS )
		{
			SDL_CALL SDL_UpdateRect(_hw_screen, 0, 0, 0, 0);
		} 
		else
		{
			SDL_CALL SDL_UpdateRects(_hw_screen, n, _dirty_rects);
		}
}

static void QueryResolution(unsigned int *res_x, unsigned int *res_y)
{
	HDC screen;
	screen = GetDC(NULL);
	*res_x = GetDeviceCaps(screen, HORZRES);
	*res_y = GetDeviceCaps(screen, VERTRES);
	ReleaseDC(NULL, screen);
}

bool CreateMainSurface(int w, int h)
{
	extern const char _openttd_revision[];
	SDL_Surface *newscreen;
	char caption[50];
	
	QueryResolution(&_width, &_height);
	if( _height > _width ) 
	{
		unsigned int temp;
		_rotated = true;
		temp = _width;
		_width = _height;
		_height = temp;
	} else {
		_rotated = false;	
	}

	if( !(_width == _cur_resolution[0] && _height == _cur_resolution[1]) ) _stretched = true;
	else _stretched = false;

	newscreen = SDL_CALL SDL_SetVideoMode(_width, _height, 8, SDL_SWSURFACE | SDL_HWPALETTE | SDL_FULLSCREEN);
	if (newscreen == NULL)
		return false;

	_hw_screen = newscreen;

	if( _stretched == true )
	{
		newscreen = SDL_CALL SDL_CreateRGBSurface(SDL_SWSURFACE, _cur_resolution[0], _cur_resolution[1], 8, 0, 0, 0, 0);
		if (newscreen == NULL)
			return false;

		zoom_x = (float)_cur_resolution[0] / (float)_width;
		zoom_y = (float)_cur_resolution[1] / (float)_height;
	}

	_screen.width = _cur_resolution[0];
	_screen.height = _cur_resolution[1];
	_screen.pitch = _cur_resolution[0];

	_sw_screen = newscreen;

	InitPalette();

	snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
	SDL_CALL SDL_WM_SetCaption(caption, caption);
	SDL_CALL SDL_ShowCursor(0);

	GameSizeChanged();

	return true;
}

#if defined(PSP)
void ResetVideoMode(void)
{
	//sceGuDispBuffer(480,272,(void*)(512*272*2),512);
	pspDebugScreenInit();
	_sdl_screen = SDL_CALL SDL_SetVideoMode(479, 272, 8, SDL_HWSURFACE | SDL_HWPALETTE |  SDL_RESIZABLE );
	//InitPalette();
	//sceGuDepthRange(480,272,480,272);
	//sceGuSync(0,0);
	//GameSizeChanged();
	MarkWholeScreenDirty();
}
#endif

typedef struct VkMapping {
	uint16 vk_from;
	byte vk_count;
	byte map_to;
} VkMapping;

#define AS(x, z) {x, 0, z}
#define AM(x, y, z, w) {x, y - x, z}

static const VkMapping _vk_mapping[] = {
	// Pageup stuff + up/down
	AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),
	AS(SDLK_UP,     WKC_UP),
	AS(SDLK_DOWN,   WKC_DOWN),
	AS(SDLK_LEFT,   WKC_LEFT),
	AS(SDLK_RIGHT,  WKC_RIGHT),

	AS(SDLK_HOME,   WKC_HOME),
	AS(SDLK_END,    WKC_END),

	AS(SDLK_INSERT, WKC_INSERT),
	AS(SDLK_DELETE, WKC_DELETE),

	// Map letters & digits
	AM(SDLK_a, SDLK_z, 'A', 'Z'),
	AM(SDLK_0, SDLK_9, '0', '9'),

	AS(SDLK_ESCAPE,    WKC_ESC),
	AS(SDLK_PAUSE,     WKC_PAUSE),
	AS(SDLK_BACKSPACE, WKC_BACKSPACE),

	AS(SDLK_SPACE,     WKC_SPACE),
	AS(SDLK_RETURN,    WKC_RETURN),
	AS(SDLK_TAB,       WKC_TAB),

	// Function keys
	AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),

	// Numeric part.
	// What is the virtual keycode for numeric enter??
	AM(SDLK_KP0, SDLK_KP9, WKC_NUM_0, WKC_NUM_9),
	AS(SDLK_KP_DIVIDE,   WKC_NUM_DIV),
	AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
	AS(SDLK_KP_MINUS,    WKC_NUM_MINUS),
	AS(SDLK_KP_PLUS,     WKC_NUM_PLUS),
	AS(SDLK_KP_ENTER,    WKC_NUM_ENTER),
	AS(SDLK_KP_PERIOD,   WKC_NUM_DECIMAL)
};

static uint32 ConvertSdlKeyIntoMy(SDL_keysym *sym)
{
	const VkMapping *map;
	uint key = 0;

	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
		if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
			key = sym->sym - map->vk_from + map->map_to;
			break;
		}
	}

	// check scancode for BACKQUOTE key, because we want the key left of "1", not anything else (on non-US keyboards)
#if defined(WIN32) || defined(__OS2__)
	if (sym->scancode == 41) key = WKC_BACKQUOTE;
#elif defined(__APPLE__)
	if (sym->scancode == 10) key = WKC_BACKQUOTE;
#elif defined(__MORPHOS__)
	if (sym->scancode == 0)  key = WKC_BACKQUOTE;  // yes, that key is code '0' under MorphOS :)
#elif defined(__BEOS__)
	if (sym->scancode == 17) key = WKC_BACKQUOTE;
#elif defined(__SVR4) && defined(__sun)
	if (sym->scancode == 60) key = WKC_BACKQUOTE;
	if (sym->scancode == 49) key = WKC_BACKSPACE;
#elif defined(__sgi__)
	if (sym->scancode == 22) key = WKC_BACKQUOTE;
#else
	if (sym->scancode == 49) key = WKC_BACKQUOTE;
#endif

	// META are the command keys on mac
	if (sym->mod & KMOD_META)  key |= WKC_META;
	if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
	if (sym->mod & KMOD_CTRL)  key |= WKC_CTRL;
	if (sym->mod & KMOD_ALT)   key |= WKC_ALT;
	// these two lines really help porting hotkey combos. Uncomment to use -- Bjarni
#if 0
	DEBUG(driver, 0) ("scancode character pressed %u", sym->scancode);
	DEBUG(driver, 0) ("unicode character pressed %u", sym->unicode);
#endif
	return (key << 16) + sym->unicode;
}
static int PollEvent(void)
{
	SDL_Event ev;
	float _x, _y;

	if (!SDL_CALL SDL_PollEvent(&ev)) return -2;

	switch (ev.type) {
		case SDL_MOUSEMOTION:
			_x = ev.motion.x;
			_y = ev.motion.y;

			if( _stretched )
			{
				_x *= zoom_x;
				_y *= zoom_y;
			}

			if (_cursor.fix_at) {
				int dx = _x - _cursor.pos.x;
				int dy = _y - _cursor.pos.y;
				if (dx != 0 || dy != 0) {
					_cursor.delta.x += dx;
					_cursor.delta.y += dy;
					//SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
				}
			} else {
				_cursor.delta.x = _x - _cursor.pos.x;
				_cursor.delta.y = _y - _cursor.pos.y;
				_cursor.pos.x = _x;
				_cursor.pos.y = _y;
				_cursor.dirty = true;
			}

			_left_button_down = true;
			break;

		case SDL_MOUSEBUTTONDOWN:
			_x = ev.button.x;
			_y = ev.button.y;
	
			if( _stretched )
			{
				_x *= zoom_x;
				_y *= zoom_y;
			}

			if (_cursor.fix_at) {
				int dx = _x - _cursor.pos.x;
				int dy = _y - _cursor.pos.y;
				if (dx != 0 || dy != 0) {
					_cursor.delta.x += dx;
					_cursor.delta.y += dy;
					//SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
				}
			} else {
				_cursor.delta.x = _x - _cursor.pos.x;
				_cursor.delta.y = _y - _cursor.pos.y;
				_cursor.pos.x = _x;
				_cursor.pos.y = _y;
				_cursor.dirty = true;
			}

			switch (ev.button.button) {
				case SDL_BUTTON_LEFT:
					_left_button_down = true;
					break;

				case SDL_BUTTON_RIGHT:
					_right_button_down = true;
					_right_button_clicked = true;
					break;

				case SDL_BUTTON_WHEELUP:   _cursor.wheel--; break;
				case SDL_BUTTON_WHEELDOWN: _cursor.wheel++; break;

				default: break;
			}
			break;

		case SDL_MOUSEBUTTONUP:
			_x = ev.button.x;
			_y = ev.button.y;
	
			if( _stretched )
			{
				_x *= zoom_x;
				_y *= zoom_y;
			}

			if (_cursor.fix_at) {
				int dx = _x - _cursor.pos.x;
				int dy = _y - _cursor.pos.y;
				if (dx != 0 || dy != 0) {
					_cursor.delta.x += dx;
					_cursor.delta.y += dy;
					//SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
				}
			} else {
				_cursor.delta.x = _x - _cursor.pos.x;
				_cursor.delta.y = _y - _cursor.pos.y;
				_cursor.pos.x = _x;
				_cursor.pos.y = _y;
				_cursor.dirty = true;
			}

			if (_rightclick_emulate) {
				_right_button_down = false;
				_left_button_down = false;
				_left_button_clicked = false;
			} else if (ev.button.button == SDL_BUTTON_LEFT) {
				_left_button_down = false;
				_left_button_clicked = false;
			} else if (ev.button.button == SDL_BUTTON_RIGHT) {
				_right_button_down = false;
			}
			break;

		case SDL_ACTIVEEVENT:
			if (!(ev.active.state & SDL_APPMOUSEFOCUS)) break;

			if (ev.active.gain) { // mouse entered the window, enable cursor
				_cursor.in_window = true;
			}
			break;

		case SDL_QUIT: HandleExitGameRequest(); break;

		case SDL_KEYDOWN: /* Toggle full-screen on ALT + ENTER/F */
			HandleKeypress(ConvertSdlKeyIntoMy(&ev.key.keysym));
			break;
		
		case SDL_KEYUP:
			break;

		case SDL_VIDEORESIZE: {
			int w = clamp(ev.resize.w, 64, MAX_SCREEN_WIDTH);
			int h = clamp(ev.resize.h, 64, MAX_SCREEN_HEIGHT);
			ChangeResInGame(w, h);
			break;
		}
	}
	return -1;
}

static const char *SdlVideoStart(const char * const *parm)
{
	char buf[30];

	const char *s = SdlOpen(SDL_INIT_VIDEO);
	if (s != NULL) return s;

	SDL_CALL SDL_VideoDriverName(buf, 30);
	DEBUG(driver, 1) ("sdl: using driver '%s'", buf);
	
	_cur_resolution[0] = 320; ///10*11; //480;
	_cur_resolution[1] = 270; ///10*11; //272;

	_rightclick_emulate = true;

	CreateMainSurface(_cur_resolution[0], _cur_resolution[1]);

	MarkWholeScreenDirty();

	g_hGapiLib = GAPI_GetGapiLib();

    LINK( GXOpenInput, gxFunc.GXOpenInput,           "?GXOpenInput@@YAHXZ" )
    LINK( GXCloseInput, gxFunc.GXCloseInput,          "?GXCloseInput@@YAHXZ" )
    LINK( GXGetDefaultKeys, gxFunc.GXGetDefaultKeys,      "?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z" )


	gxFunc.GXOpenInput();
	gxKeys = gxFunc.GXGetDefaultKeys( (_rotated ? GX_LANDSCAPEKEYS : GX_NORMALKEYS) );

	SDL_CALL SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
	SDL_CALL SDL_EnableUNICODE(0);

	return NULL;
}

static void SdlVideoStop(void)
{
	gxFunc.GXCloseInput();
	SdlClose(SDL_INIT_VIDEO);
}

static void SdlVideoMainLoop(void)
{
	uint32 cur_ticks = SDL_CALL SDL_GetTicks();
	uint32 next_tick = cur_ticks + 30;
	uint32 pal_tick = 0;
	uint32 mod;
	int numkeys;
	Uint8 *keys;

	for (;;) {
		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
		InteractiveRandom(); // randomness

		while (PollEvent() == -1) {}
		if (_exit_game) return;

		mod = SDL_CALL SDL_GetModState();
		keys = SDL_CALL SDL_GetKeyState(&numkeys);
#if defined(_DEBUG)
		if (_shift_pressed)
#else
		/* Speedup when pressing tab, except when using ALT+TAB
		 * to switch to another application */
		if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
#endif
		{
			if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
		} else if (_fast_forward & 2) {
			_fast_forward = 0;
		}

		cur_ticks = SDL_CALL SDL_GetTicks();
		if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) {
			next_tick = cur_ticks + 30;

#ifndef ARM
#ifndef PSP
			_ctrl_pressed  = !!(mod & KMOD_CTRL);
#endif
			_shift_pressed = !!(mod & KMOD_SHIFT);
#ifdef _DEBUG
			_dbg_screen_rect = !!(mod & KMOD_CAPS);
#endif
#endif
			// determine which directional keys are down
#ifdef ARM
			_dirkeys =	(GetAsyncKeyState(gxKeys.vkLeft)	< 0	? 1 : 0) |
						(GetAsyncKeyState(gxKeys.vkUp)		< 0	? 2 : 0) |
						(GetAsyncKeyState(gxKeys.vkRight)	< 0	? 4 : 0) |
						(GetAsyncKeyState(gxKeys.vkDown)	< 0	? 8 : 0);

			/* Delete windows */
			if (GetAsyncKeyState(gxKeys.vkA) < 0)
			{
				DeleteNonVitalWindows();
			}

			/* Cheat Window */
			if (GetAsyncKeyState(gxKeys.vkB) < 0)
			{
				ShowCheatWindow();
			}

			if (GetAsyncKeyState(gxKeys.vkC) < 0)
			{
				_ctrl_pressed = true;
			}
			else
			{
				_ctrl_pressed = false;
			}

			if (GetAsyncKeyState(gxKeys.vkStart) < 0)
			{
				_right_button_down = true;
				_right_button_clicked = true;
			}
			else
			{
				_right_button_down = false;
			}
#else
			_dirkeys =
				(keys[SDLK_LEFT]  ? 1 : 0) |
				(keys[SDLK_UP]    ? 2 : 0) |
				(keys[SDLK_RIGHT] ? 4 : 0) |
				(keys[SDLK_DOWN]  ? 8 : 0);
#endif
			GameLoop();

			_screen.dst_ptr = _sw_screen->pixels;
			UpdateWindows();
			if (++pal_tick > 4) {
				CheckPaletteAnim();
				pal_tick = 1;
			}

			DrawSurfaceToScreen();
		} else {
			//SDL_CALL SDL_Delay(1);
			_screen.dst_ptr = _sw_screen->pixels;
			DrawTextMessage();
			DrawMouseCursor();

			DrawSurfaceToScreen();
		}
	}
}

static bool SdlVideoChangeRes(int w, int h)
{
	return CreateMainSurface(w, h);
}

static void SdlVideoFullScreen(bool full_screen)
{
#ifndef ARM
	_fullscreen = full_screen;
	GetVideoModes(); // get the list of available video modes
	if (_num_resolutions == 0 || !_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1])) {
		// switching resolution failed, put back full_screen to original status
		_fullscreen ^= true;
	}
#endif
}

const HalVideoDriver _sdl_video_driver = {
	SdlVideoStart,
	SdlVideoStop,
	SdlVideoMakeDirty,
	SdlVideoMainLoop,
	SdlVideoChangeRes,
	SdlVideoFullScreen,
};

#endif
