/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001  Ludvig Strigeus
 * Copyright (C) 2001/2002 The ScummVM project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/gui.cpp,v 1.60 2002/07/13 17:54:20 fingolfin Exp $
 */

#include "stdafx.h"
#include "scumm.h"
#include "sound/mididrv.h"
#include "sound/imuse.h"
#include "gui.h"
#include "guimaps.h"
#include "config-file.h"

#include <ctype.h>

#define hline(x, y, x2, color) line(x, y, x2, y, color);
#define vline(x, y, y2, color) line(x, y, x, y2, color);

#ifdef _WIN32_WCE
// Additional variables for WinCE specific GUI
#include "gapi_keys.h"
extern bool toolbar_drawn;
extern bool draw_keyboard;
extern bool get_key_mapping;
extern struct keyops keyMapping;
extern void save_key_mapping(void);
uint16 _key_mapping_required;
uint16 _current_page;
#else
#define save_key_mapping() ;
bool get_key_mapping;
uint16 _key_mapping_required;
#endif

enum {
	GUI_NONE = 0,
	GUI_RESTEXT = 1,
	GUI_IMAGE = 2,
	GUI_STAT = 3,
	GUI_CUSTOMTEXT = 4,
	GUI_VARTEXT = 5,
	GUI_ACTIONTEXT = 6,
	GUI_KEYTEXT = 7,
	GUI_SCROLLTEXT = 8,
	GUI_NEXTTEXT = 9,
	GUI_UPDOWNARROW = 10,
	GUI_CHECKBOX = 11
};

enum {
	GWF_BORDER = 1,
	GWF_CLEARBG = 2,
	GWF_PARENT = 4,
	GWF_DELAY = 8,
	GWF_DEFAULT = GWF_BORDER | GWF_CLEARBG,
	GWF_BUTTON = GWF_BORDER | GWF_CLEARBG | GWF_DELAY
};

struct GuiWidget {
	byte _type;
	byte _page;
	byte _flags;
	int16 _x, _y;
	uint16 _w, _h;
	uint16 _id;
	byte _string_number;
	uint8 _hotkey;
};

enum {
	SAVELOAD_DIALOG,
	PAUSE_DIALOG,
	SOUND_DIALOG,
	KEYS_DIALOG,
	OPTIONS_DIALOG,
	ABOUT_DIALOG,
	LAUNCHER_DIALOG,
	MISC_DIALOG
};


#define IMG_SIZE	8

// Triangles pointing up-/downwards, used for save/load dialog
static uint32 up_arrow[IMG_SIZE] = {
	0x00011000,
	0x00011000,
	0x00100100,
	0x00100100,
	0x01000010,
	0x01000010,
	0x10000001,
	0x10000001,
};

static uint32 down_arrow[IMG_SIZE] = {
	0x10000001,
	0x10000001,
	0x01000010,
	0x01000010,
	0x00100100,
	0x00100100,
	0x00011000,
	0x00011000,
};

static uint32 checked_img[IMG_SIZE] = {
	0x00000000,
	0x01000010,
	0x00100100,
	0x00011000,
	0x00011000,
	0x00100100,
	0x01000010,
	0x00000000,
};

const GuiWidget launcher_dialog[] = {
	{GUI_STAT, 0xFF, GWF_DEFAULT, 0, 0, 320, 200, 0, 0},
	{GUI_CUSTOMTEXT, 0x01, GWF_CLEARBG, 5, 180, 45, 15, 20, 12},
	{GUI_CUSTOMTEXT, 0x01, GWF_CLEARBG, 130, 180, 65, 15, 21, 17},
	{GUI_CUSTOMTEXT, 0x01, GWF_CLEARBG, 265, 180, 50, 15, 22, 7},
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};

const GuiWidget keys_dialog[] = {
	{GUI_STAT, 0xFF, GWF_DEFAULT, 30, 10, 260, 130, 0, 0},

	// First action
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 10 + 10, 15, 15, 10, 3},	// CUSTOMTEXT_PLUS
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 10 + 10, 15, 15, 11, 4},	// CUSTOMTEXT_MINUS
	{GUI_ACTIONTEXT, 0x01, GWF_BUTTON, 30 + 11 + 33 + 10, 10 + 10, 100, 15, 100, 1},
	{GUI_KEYTEXT, 0x01, 0, 30 + 11 + 33 + 120, 10 + 10 + 3, 100, 15, 1, 1},

	//Second action
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 10 + 10 + 15 + 5, 15, 15, 20, 3},	// CUSTOMTEXT_PLUS
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 10 + 10 + 15 + 5, 15, 15, 21, 4},	// CUSTOMTEXT_MINUS
	{GUI_ACTIONTEXT, 0x01, GWF_BUTTON, 30 + 10 + 33 + 10, 10 + 10 + 15 + 5, 100, 15, 101, 2},
	{GUI_KEYTEXT, 0x01, 0, 30 + 11 + 33 + 120, 10 + 10 + 15 + 5 + 3, 100, 15, 2, 2},

	//Third action
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 10 + 10 + 15 + 5 + 15 + 5, 15, 15, 30, 3},	// CUSTOMTEXT_PLUS
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 10 + 10 + 15 + 5 + 15 + 5, 15, 15, 31, 4},	// CUSTOMTEXT_MINUS
	{GUI_ACTIONTEXT, 0x01, GWF_BUTTON, 30 + 10 + 33 + 10, 10 + 10 + 15 + 5 + 15 + 5, 100, 15, 102, 3},
	{GUI_KEYTEXT, 0x01, 0, 30 + 11 + 33 + 120, 10 + 10 + 15 + 5 + 15 + 5 + 3, 100, 15, 3, 3},

	//Fourth action
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5, 15, 15, 40, 3},
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5, 15, 15, 41, 4},
	{GUI_ACTIONTEXT, 0x01, GWF_BUTTON, 30 + 10 + 33 + 10, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5, 100, 15,
	 103, 4},
	{GUI_KEYTEXT, 0x01, 0, 30 + 11 + 33 + 120, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5 + 3, 100, 15, 4, 4},

	//Fifth action
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5 + 15 + 5, 15, 15,
	 50, 3},
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5 + 15 + 5, 15, 15,
	 51, 4},
	{GUI_ACTIONTEXT, 0x01, GWF_BUTTON, 30 + 10 + 33 + 10, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5 + 15 + 5,
	 100, 15, 104, 5},
	{GUI_KEYTEXT, 0x01, 0, 30 + 11 + 33 + 120, 10 + 10 + 15 + 5 + 15 + 5 + 15 + 5 + 15 + 5 + 3, 100,
	 15, 5, 5},

	//OK
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 30 + 60, 10 + 106, 54, 16, 60, 9},
	//Previous-Next
	{GUI_NEXTTEXT, 0x01, GWF_BUTTON, 30 + 120, 10 + 106, 54, 16, 61, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};

const GuiWidget about_dialog[] = {
	{GUI_STAT, 0xFF, GWF_DEFAULT, 30, 10, 260, 134, 0, 0},
	{GUI_CUSTOMTEXT, 0x01, 0, 30 + 68, 10 + 20, 160, 15, 0, 9},	// Build
	{GUI_CUSTOMTEXT, 0x01, 0, 30 + 10, 10 + 35, 240, 15, 0, 10},	// ScummVM Url
	{GUI_CUSTOMTEXT, 0x01, 0, 30 + 75, 10 + 65, 150, 15, 0, 11},	// Lucasarts
	{GUI_CUSTOMTEXT, 0x01, 0, 30 + 110, 10 + 80, 40, 15, 0, 21},	// Except:
	{GUI_CUSTOMTEXT, 0x01, 0, 30 + 25, 10 + 95, 210, 15, 0, 22},	// Adventuresoft
	{GUI_SCROLLTEXT, 0x01, 0, 30 + 95, 10 + 10, 100, 15, 0},
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 30 + 100, 10 + 112, 54, 16, 40, 9},
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};

const GuiWidget options_dialog[] = {
	{GUI_STAT, 0xFF, GWF_DEFAULT, 50, 80, 210, 60, 0, 0},
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 50 + 10, 80 + 10, 40, 15, 1, 5, 'S'},	// Sound
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 50 + 10 + 40 + 30, 80 + 10, 40, 15, 2, 6, 'K'},	// Keys
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 50 + 10 + 40 + 30 + 40 + 30, 80 + 10, 40, 15, 3, 7, 'A'},	// About
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 50 + 10, 80 + 10 + 15 + 10, 40, 15, 4, 18, 'M'},	// Misc
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};

const GuiWidget misc_dialog[] = {
	{GUI_STAT, 0xFF, GWF_DEFAULT, 50, 80, 210, 65, 0, 0},
	{GUI_CHECKBOX, 0x01, GWF_DEFAULT, 50 + 10, 80 + 6, 14, 14, 1, 0, 'S'},	// checkbox for subtitles
	{GUI_CUSTOMTEXT, 0x01, 0, 50 + 10 + 20, 80 + 10, 140, 15, 0, 19},	// "Show speech subtitles"
	{GUI_CHECKBOX, 0x01, GWF_DEFAULT, 50 + 10, 80 + 6 + 16, 14, 14, 5, 0, 'A'},	// checkbox for amiga pallete
	{GUI_CUSTOMTEXT, 0x01, 0, 50 + 10 + 20, 80 + 10 + 15, 140, 15, 0, 20},	// "Amiga pallete conversion"
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 50 + 10 + 20, 80 + 10 + 15 + 20, 54, 16, 3, 9, 13},	// ok
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 50 + 10 + 20 + 80, 80 + 10 + 15 + 20, 54, 16, 4, 7},	// cancel
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};

const GuiWidget sound_dialog[] = {
	{GUI_STAT, 0xFF, GWF_DEFAULT, 30, 20, 260, 120, 0, 0},
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 20 + 11, 15, 15, 1, 3},	// Plus
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 20 + 11, 15, 15, 2, 4},	// Minus
	{GUI_VARTEXT, 0x01, GWF_DEFAULT, 30 + 73, 20 + 11, 128, 15, 3, 0},	// Master
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 20 + 25 + 11, 15, 15, 11, 3},	// Plus
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 20 + 25 + 11, 15, 15, 12, 4},	// Minus
	{GUI_VARTEXT, 0x01, GWF_BUTTON, 30 + 73, 20 + 25 + 11, 128, 15, 13, 1},	// Music
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 11, 20 + 25 + 25 + 11, 15, 15, 21, 3},	// Plus
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 30 + 33, 20 + 25 + 25 + 11, 15, 15, 22, 4},	// Minus
	{GUI_VARTEXT, 0x01, GWF_BUTTON, 30 + 73, 20 + 25 + 25 + 11, 128, 15, 23, 2},	// SFX
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 30 + (260 / 2) - 80, 20 + 25 + 25 + 11 + 25, 54, 16, 40, 9, 13},	/* OK */
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 30 + (260 / 2), 20 + 25 + 25 + 11 + 25, 54, 16, 50, 7},	/* Cancel */
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};

const GuiWidget save_load_dialog[] = {
	{GUI_STAT, 0xFF, GWF_DEFAULT | GWF_PARENT, 30, 20, 260, 124, 0, 0},
	{GUI_RESTEXT, 0x01, 0, 10, 7, 240, 16, 0, 1},	/* How may I serve you? */
	{GUI_RESTEXT, 0x02, 0, 10, 7, 240, 16, 0, 2},	/* Select a game to LOAD */
	{GUI_RESTEXT, 0x04, 0, 10, 7, 240, 16, 0, 3},	/* Name your SAVE game */

	{GUI_STAT, 0xFF, GWF_DEFAULT, 6, 20, 170, 96, 0, 0},
	{GUI_UPDOWNARROW, 0x01, GWF_BUTTON, 180, 24, 16, 40, 0, 0},	/* Up (dummy) */
	{GUI_UPDOWNARROW, 0x01, GWF_BUTTON, 180, 72, 16, 40, 0, 1},	/* Down (dummy) */
	{GUI_UPDOWNARROW, 0xFE, GWF_BUTTON, 180, 24, 16, 40, 1, 0},	/* Up */
	{GUI_UPDOWNARROW, 0xFE, GWF_BUTTON, 180, 72, 16, 40, 2, 1},	/* Down */

	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 24, 160, 10, 20, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 34, 160, 10, 21, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 44, 160, 10, 22, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 54, 160, 10, 23, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 64, 160, 10, 24, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 74, 160, 10, 25, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 84, 160, 10, 26, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 94, 160, 10, 27, 0},
	{GUI_RESTEXT, 0x06, GWF_CLEARBG, 10, 104, 160, 10, 28, 0},

	{GUI_RESTEXT, 0x01, GWF_BUTTON, 200, 20, 54, 16, 3, 4, 'S'},	/* Save */
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 200, 40, 54, 16, 4, 5, 'L'},	/* Load */
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 200, 60, 54, 16, 5, 6, 'P'},	/* Play */
	{GUI_CUSTOMTEXT, 0x01, GWF_BUTTON, 200, 80, 54, 16, 9, 17, 'O'},	/* Options */
	{GUI_RESTEXT, 0x01, GWF_BUTTON, 200, 100, 54, 16, 6, 8, 'Q'},	/* Quit */

	{GUI_RESTEXT, 0x02, GWF_BUTTON, 200, 60, 54, 16, 7, 7},	/* Cancel */

	{GUI_RESTEXT, 0x04, GWF_BUTTON, 200, 40, 54, 16, 8, 9},	/* Ok */
	{GUI_RESTEXT, 0x04, GWF_BUTTON, 200, 60, 54, 16, 7, 7},	/* Cancel */
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};

const GuiWidget pause_dialog[] = {
	{GUI_RESTEXT, 0x01, GWF_DEFAULT, 50, 80, 220, 16, 0, 10},
	{0, 0, 0, 0, 0, 0, 0, 0, 0}
};


void Gui::draw(int start, int end)
{
	int i;

	if (end == -1)
		end = start;

	for (i = 0; i < (int)(sizeof(_widgets) / sizeof(_widgets[0])); i++) {
		const GuiWidget *w = _widgets[i];
		if (w) {
			_parentX = 0;
			_parentY = 0;
			while (w->_type != GUI_NONE) {
				if (w->_id >= start && w->_id <= end && (w->_page & (1 << _cur_page))) {
					drawWidget(w);
				}
				if (w->_flags & GWF_PARENT) {
					_parentX += w->_x;
					_parentY += w->_y;
				}
				w++;
			}
		}
	}
}

const GuiWidget *Gui::widgetFromPos(int x, int y)
{
	int i;

	for (i = sizeof(_widgets) / sizeof(_widgets[0]) - 1; i >= 0; i--) {
		const GuiWidget *w = _widgets[i];
		if (w) {
			while (w->_type != GUI_NONE) {
				if ((w->_page & (1 << _cur_page)) && w->_id &&
						(uint16)(x - w->_x) < w->_w && (uint16)(y - w->_y) < w->_h)
					return w;
				if (w->_flags & GWF_PARENT) {
					x -= w->_x;
					y -= w->_y;
				}
				w++;
			}
		}
	}
	return NULL;
}

void Gui::drawChar(const char str, int xx, int yy)
{
	unsigned int buffer = 0, mask = 0, x, y;
	byte *tmp;
	int tempc = _color;
	_color = _textcolor;

	tmp = &guifont[0];
	tmp += 224 + (str + 1) * 8;

	byte *ptr = getBasePtr(xx, yy);
	if (ptr == NULL)
		return;

	for (y = 0; y < 8; y++) {
		for (x = 0; x < 8; x++) {
			unsigned char color;
			if ((mask >>= 1) == 0) {
				buffer = *tmp++;
				mask = 0x80;
			}
			color = ((buffer & mask) != 0);
			if (color)
				ptr[x] = _color;
		}
		ptr += 320;
	}
	_color = tempc;

}
void Gui::drawString(const char *str, int x, int y, int w, byte color, bool center)
{
	StringTab *st = &_s->string[5];
	st->charset = 1;
	st->center = center;
	st->color = color;
	st->xpos = x;
	st->ypos = y;
	st->right = x + w;

	if (_s->_gameId) {						/* If a game is active.. */
		_s->_messagePtr = (byte *)str;
		_s->drawString(5);
	} else {
		for (uint letter = 0; letter < strlen(str); letter++)
			drawChar(str[letter], st->xpos + (letter * 8), st->ypos);
	}
}

void Gui::drawWidget(const GuiWidget *w)
{
	const char *s;
	int x, y;

	x = w->_x;
	y = w->_y;

	if (w->_flags & GWF_CLEARBG)
		widgetClear(w);

	if (w->_flags & GWF_BORDER) {
		widgetBorder(w);
		x += 4;
		y += 4;
	}

	switch (w->_type) {
	case GUI_CUSTOMTEXT:
	case GUI_VARTEXT:
	case GUI_KEYTEXT:
	case GUI_ACTIONTEXT:
	case GUI_RESTEXT:
	case GUI_NEXTTEXT:
		{
			char text[500];
			text[0] = '\0';

			switch (w->_type) {
			case GUI_CUSTOMTEXT:
				strcpy(text, string_map_table_custom[w->_string_number]);
				break;
			case GUI_RESTEXT:
				s = queryString(w->_string_number, w->_id);
				if (s)
					strcpy(text, s);
				break;
			case GUI_VARTEXT:
				sprintf(text, "%s %d", string_map_table_custom[w->_string_number],
								_gui_variables[w->_string_number]);
				break;
			case GUI_SCROLLTEXT:
				sprintf(text, "%s", _gui_scroller);
				break;
#ifdef _WIN32_WCE
			case GUI_KEYTEXT:
				strcpy(text, getGAPIKeyName(getAction((_current_page * 5) + w->_string_number - 1)->action_key));
				break;
			case GUI_ACTIONTEXT:
				strcpy(text, getActionName(getAction((_current_page * 5) + w->_string_number - 1)->action_type));
				break;
			case GUI_NEXTTEXT:
				if (_current_page == 0)
					strcpy(text, "Next");
				else
					strcpy(text, "Prev");
				break;

#endif
			}

			if (*text) {
				drawString(text, x + _parentX, y + _parentY, w->_w,
									 (_clickWidget && _clickWidget == w->_id) ? _textcolorhi : _textcolor, false);
			}
			break;
		}
	case GUI_IMAGE:
		break;
	case GUI_UPDOWNARROW:
	case GUI_CHECKBOX:
		{
			uint32 *data;
			byte color = (_clickWidget && _clickWidget == w->_id) ? _textcolorhi : _textcolor;

			if (w->_type == GUI_UPDOWNARROW) {
				if (w->_string_number == 0)
					data = up_arrow;
				else
					data = down_arrow;
				// if not an updownarrow, it must be a checkbox
			} else {
				data = checked_img;
			}

			// Center the image
			x += w->_w / 2 - IMG_SIZE / 2;
			y += w->_h / 2 - IMG_SIZE / 2;
			if (w->_flags & GWF_BORDER) {
				x -= 4;
				y -= 4;
			}

			byte *ptr = getBasePtr(x, y);
			if (ptr == NULL)
				return;

			// If the checkbox is checked, or this is not a checkbox, draw the image
			if ((getCheckboxChecked(w->_id) == true) || (w->_type != GUI_CHECKBOX)) {
				for (int y2 = 0; y2 < IMG_SIZE; y2++) {
					uint32 mask = 0xF0000000;
					for (int x2 = 0; x2 < IMG_SIZE; x2++) {
						if (data[y2] & mask)
							ptr[x2] = color;
						mask >>= 4;
					}
					ptr += 320;
				}
			}
		}
		break;
	}
}

void Gui::widgetClear(const GuiWidget *wid)
{
	int x = wid->_x;
	int y = wid->_y;
	int w = wid->_w;
	int h = wid->_h;
	int i;

	byte *ptr = getBasePtr(x, y);
	if (ptr == NULL)
		return;

	_s->setVirtscreenDirty(_vs, x + _parentX, y + _parentY, x + _parentX + w, y + _parentY + h);

	if (wid->_flags & GWF_BORDER) {
		// Inset by 1 pixel in all directions
		ptr += 320 + 1;
		w -= 2;
		h -= 2;
	}

	while (h--) {
		for (i = 0; i < w; i++)
			ptr[i] = _bgcolor;
		ptr += 320;
	}
}

void Gui::widgetBorder(const GuiWidget *w)
{
	box(w->_x, w->_y, w->_w, w->_h);
}

void Gui::box(int x, int y, int width, int height)
{
	hline(x + 1, y, x + width - 2, _color);
	hline(x, y + 1, x + width - 1, _color);
	vline(x, y + 1, y + height - 2, _color);
	vline(x + 1, y, y + height - 1, _color);

	hline(x + 1, y + height - 2, x + width - 1, _shadowcolor);
	hline(x + 1, y + height - 1, x + width - 2, _shadowcolor);
	vline(x + width - 1, y + 1, y + height - 2, _shadowcolor);
	vline(x + width - 2, y + 1, y + height - 1, _shadowcolor);
}

byte *Gui::getBasePtr(int x, int y)
{
	x += _parentX;
	y += _parentY;
	_vs = _s->findVirtScreen(y);

	if (_vs == NULL)
		return NULL;

	return _vs->screenPtr + x + (y - _vs->topline) * 320 +
		_s->_screenStartStrip * 8 + (_s->camera._cur.y - 100) * 320;
}

void Gui::line(int x, int y, int x2, int y2, byte color)
{
	byte *ptr;

	if (x2 < x)
		x2 ^= x ^= x2 ^= x;					// Swap x2 and x

	if (y2 < y)
		y2 ^= y ^= y2 ^= y;					// Swap y2 and y

	ptr = getBasePtr(x, y);

	if (ptr == NULL)
		return;

	if (x == x2) {
		/* vertical line */
		while (y++ <= y2) {
			*ptr = color;
			ptr += 320;
		}
	} else if (y == y2) {
		/* horizontal line */
		while (x++ <= x2) {
			*ptr++ = color;
		}
	}
}

void Gui::leftMouseClick(int x, int y)
{
	const GuiWidget *w = widgetFromPos(x, y);
	int old;

	_clickTimer = 0;

	old = _clickWidget;
	_clickWidget = w ? w->_id : 0;

	if (old)
		draw(old);
	if (_clickWidget) {
		draw(_clickWidget);
		if (w->_flags & GWF_DELAY)
			_clickTimer = 5;
		else
			handleCommand(_clickWidget);
	}

	if (_dialog == PAUSE_DIALOG)
		close();
}

void Gui::handleSoundDialogCommand(int cmd)
{
	if (cmd == 50) {
		close();
	} else if (cmd == 40) {
		_s->_sound_volume_master = _gui_variables[0];	// Master
		_s->_sound_volume_music = _gui_variables[1];	// Music
		_s->_sound_volume_sfx = _gui_variables[2];	// SFX

		IMuse *imuse = _s->_imuse;
		imuse->set_music_volume(_s->_sound_volume_music);
		imuse->set_master_volume(_s->_sound_volume_master);
		_s->_mixer->set_volume(_s->_sound_volume_sfx);
		scummcfg->set("master_volume", _s->_sound_volume_master);
		scummcfg->set("music_volume", _s->_sound_volume_music);
		scummcfg->set("sfx_volume", _s->_sound_volume_sfx);
		scummcfg->flush();

		close();
	} else {
		if ((cmd % 10) == 1) {
			if (_gui_variables[cmd / 10] < 100)
				_gui_variables[cmd / 10] += 5;
		} else {
			if (_gui_variables[cmd / 10] > 0)
				_gui_variables[cmd / 10] -= 5;
		}

		draw((cmd / 10) * 10 + 3, (cmd / 10) * 10 + 3);
	}
}

void Gui::handleOptionsDialogCommand(int cmd)
{
	switch (cmd) {
	case 1:
		_widgets[0] = sound_dialog;
		_gui_variables[0] = _s->_sound_volume_master;
		_gui_variables[1] = _s->_sound_volume_music;
		_gui_variables[2] = _s->_sound_volume_sfx;
		_active = true;
		_cur_page = 0;
		_dialog = SOUND_DIALOG;
		draw(0, 100);
		return;
	case 2:
		_key_mapping_required = 0;
		get_key_mapping = true;
		_widgets[0] = keys_dialog;
		_active = true;
		_cur_page = 0;
		_dialog = KEYS_DIALOG;
		draw(0, 200);
		return;
	case 3:
		_widgets[0] = about_dialog;
		_active = true;
		_cur_page = 0;
		_return_to = 0;
		_dialog = ABOUT_DIALOG;
		draw(0, 100);
		return;
	case 4:
		_widgets[0] = misc_dialog;
		_active = true;
		_cur_page = 0;
		_return_to = 0;
		_dialog = MISC_DIALOG;
		clearCheckboxes();
		setCheckbox(!(_s->_noSubtitles), 1);
		if (_s->_features & GF_AMIGA)
			setCheckbox(true, 5);
		else
			setCheckbox(false, 5);
		draw(0, 100);
		return;
	}
}

void Gui::handleMiscDialogCommand(int cmd)
{
	switch (cmd) {
	case 1:
		if ((getCheckboxChecked(1)) == true)
			setCheckbox(false, 1);
		else
			setCheckbox(true, 1);
		draw(1, 1);
		return;
	case 5:
		if (getCheckboxChecked(5) == true)
			setCheckbox(false, 5);
		else
			setCheckbox(true, 5);
		draw(5, 5);
		return;
	case 3:
	case 4:
		// OK button - perform the actions of the checkboxes
		if (cmd == 3) {
			// The opposite of the checkbox(1) is set because the internal variable is 'no subtitles' but
			// a "Show subtitles" option makes more usability sense
			_s->_noSubtitles = (!getCheckboxChecked(1));

			// Amiga pallete conversion checkbox
			if (getCheckboxChecked(5))
				_s->_features = _s->_features | GF_AMIGA;
			else
				_s->_features = _s->_features & ~GF_AMIGA;
			_s->_fullRedraw = true;
		}
		close();
	}
}

void Gui::handleKeysDialogCommand(int cmd)
{
#ifdef _WIN32_WCE
	if (cmd < 100 && cmd != 60 && cmd != 61) {

		if ((cmd % 10) == 1)
			setNextType((_current_page * 5) + (cmd / 10) - 1);
		else
			setPreviousType((_current_page * 5) + (cmd / 10) - 1);

		draw(0, 200);

		return;
	}

	if (cmd >= 100)
		_key_mapping_required = cmd;

	if (cmd == 60) {
		get_key_mapping = false;
		save_key_mapping();
		close();
	}

	if (cmd == 61) {
		if (!_current_page)
			_current_page = 1;
		else
			_current_page = 0;
		draw(0, 200);
		return;
	}
#else
	close();
#endif
}

void Gui::handleLauncherDialogCommand(int cmd)
{
	debug(9, "handle launcher command\n");
	switch (cmd) {
	case 20:
		close();
		break;
	case 21:
		// Nothing yet
		break;
	case 22:
		_widgets[0] = about_dialog;
		_active = true;
		_cur_page = 0;
		_return_to = LAUNCHER_DIALOG;
		_dialog = ABOUT_DIALOG;
		draw(0, 100);
		debug(9, "about dialog\n");
		break;
	default:
		debug(9, "default\n");
		close();
	}
}

void Gui::handleCommand(int cmd)
{
	int lastEdit = _editString;
	showCaret(false);

	switch (_dialog) {
	case LAUNCHER_DIALOG:
		handleLauncherDialogCommand(cmd);
		return;
	case SOUND_DIALOG:
		handleSoundDialogCommand(cmd);
		return;
	case OPTIONS_DIALOG:
		handleOptionsDialogCommand(cmd);
		return;
	case MISC_DIALOG:
		handleMiscDialogCommand(cmd);
		return;
	case KEYS_DIALOG:
		handleKeysDialogCommand(cmd);
		return;
	case ABOUT_DIALOG:
		if (_return_to == LAUNCHER_DIALOG) {
			_widgets[0] = launcher_dialog;
			_active = true;
			_cur_page = 0;
			_dialog = LAUNCHER_DIALOG;
			draw(0, 100);
		} else
			close();
		return;
	}

	// If we get here, it's the SAVELOAD_DIALOG

	switch (cmd) {
	case 1:											/* up button */
		if (_slotIndex - 9 < 0)
			return;
		getSavegameNames(_slotIndex - 9);
		draw(20, 28);
		return;
	case 2:											/* down button */
		if (_slotIndex + 9 > 80)
			return;
		getSavegameNames(_slotIndex + 9);
		draw(20, 28);
		return;
	case 3:											/* save button */
		_cur_page = 2;
		getSavegameNames(1);				/* Start at 1, since slot 0 is reserved for autosave */
		draw(0, 100);
		return;
	case 4:											/* load button */
		_cur_page = 1;
		getSavegameNames(0);
		draw(0, 100);
		return;
	case 5:											/* play button */
		close();
		return;
	case 6:											/* quit button */
		exit(1);
		return;
	case 7:											/* cancel button */
		_cur_page = 0;
		draw(0, 100);
		return;
	case 8:											/* ok button (save game) */
		if (lastEdit == -1 || game_names[lastEdit][0] == 0)
			return;

		_s->_saveLoadSlot = lastEdit + _slotIndex;
		_s->_saveLoadCompatible = false;
		_s->_saveLoadFlag = 1;
		memcpy(_s->_saveLoadName, game_names[lastEdit], sizeof(_s->_saveLoadName));
		close();
		return;
	case 9:											/* options button */
		options();
		draw(0, 100);
		return;
	default:
		if (cmd >= 20 && cmd <= 28) {
			if (_cur_page == 1) {
				if (valid_games[cmd - 20]) {
					_s->_saveLoadSlot = cmd - 20 + _slotIndex;
					_s->_saveLoadCompatible = false;
					_s->_saveLoadFlag = 2;
					close();
				}
				return;
			} else if (_cur_page == 2) {
				_clickWidget = cmd;
				editString(cmd - 20);
			}
		}
	}

}

void Gui::getSavegameNames(int start)
{
	int i;
	_slotIndex = start;

	for (i = 0; i < 9; i++, start++) {
		valid_games[i] = _s->getSavegameName(start, game_names[i]);
	}
}

const char *Gui::queryString(int stringno, int id)
{
	static char namebuf[64];
	char *result;
	int string;

	if (id >= 20 && id <= 28) {
		// Save game names
		sprintf(namebuf, "%2d. %s", id - 20 + _slotIndex, game_names[id - 20]);
		return namebuf;
	}

	if (stringno == 0)
		return NULL;

	if (_s->_features & GF_AFTER_V7)
		string = _s->_vars[string_map_table_v7[stringno - 1].num];
	else if (_s->_features & GF_AFTER_V6)
		string = _s->_vars[string_map_table_v6[stringno - 1].num];
	else
		string = string_map_table_v5[stringno - 1].num;

	result = (char *)_s->getStringAddress(string);

	if (!result) {									// Gracelessly degrade to english :)
		if (_s->_features & GF_AFTER_V6)
			return string_map_table_v6[stringno - 1].string;
		else
			return string_map_table_v5[stringno - 1].string;
	}

	return result;
}

void Gui::showCaret(bool show)
{
	int i;
	char *s;

	if (_editString == -1)
		return;

	i = _editLen;
	s = game_names[_editString];

	if (show) {
		if (i < SAVEGAME_NAME_LEN - 1) {
			s[i] = '_';
			s[i + 1] = 0;
		}
	} else {
		s[i] = 0;
	}

	draw(_editString + 20);

	if (!show)
		_editString = -1;
}

void Gui::editString(int i)
{
	char *s = game_names[i];
	if (!valid_games[i]) {
		valid_games[i] = true;
		*s = 0;
	}
	_editString = i;
	_editLen = strlen(s);
	showCaret(true);
}

void Gui::addLetter(byte letter)
{
	switch (_dialog) {
	case SAVELOAD_DIALOG:
		if (_editString == -1)
			return;

/*
		FIXME - this code here has no effect at all, since Scumm::convertKeysToClicks()
		swallows all return key events.
		// Return pressed?
		if (letter == '\n' || letter == '\r') { 
			handleCommand(8);
			return;
		}
*/

		if (letter >= 32 && letter < 128 && _editLen < SAVEGAME_NAME_LEN - 1) {
			game_names[_editString][_editLen++] = letter;
		} else if (letter == 8 && _editLen > 0) {
			_editLen--;
		}
		showCaret(true);
		break;
	case PAUSE_DIALOG:
		if (letter == 32)
			close();
		break;

#ifdef _WIN32_WCE
	case KEYS_DIALOG:
		clearActionKey(letter);
		if (_key_mapping_required)
			getAction((_current_page * 5) + _key_mapping_required - 100)->action_key = letter;
		_key_mapping_required = 0;
		draw(0, 200);
		break;
#endif
	}
}

bool Gui::getCheckboxChecked(int id)
{
	return _cbox_checked[id];
}

void Gui::setCheckbox(bool state, int id)
{
	_cbox_checked[id] = state;
}

void Gui::clearCheckboxes()
{
	for (int id = 0; id <= 100; id++) {
		_cbox_checked[id] = false;
	}
}

void Gui::init(Scumm *s)
{
	/* Default GUI colors */
	_bgcolor = 0;
	_color = 0;
	_textcolor = 8;								// 15 is nice
	_textcolorhi = 15;
	_shadowcolor = 0;
	_s = s;

	strcpy(_gui_scroller, "Brought to you by:");
}

void Gui::loop()
{
	if (_active && !_inited) {
		_inited = true;
		draw(0, 200);								// was 100    
		_old_soundsPaused = _s->_soundsPaused;
		_s->pauseSounds(true);

		// Backup old cursor
		memcpy(_old_grabbedCursor, _s->_grabbedCursor, sizeof(_old_grabbedCursor));
		_old_cursorWidth = _s->_cursorWidth;
		_old_cursorHeight = _s->_cursorHeight;
		_old_cursorHotspotX = _s->_cursorHotspotX;
		_old_cursorHotspotY = _s->_cursorHotspotY;
		_old_cursor_mode = _s->_system->show_mouse(true);

		_s->_cursorAnimate++;
		_s->gdi._cursorActive = 1;
	}
	_s->animateCursor();
	_s->getKeyInput(0);
	if (_s->_mouseButStat & MBS_LEFT_CLICK) {
		leftMouseClick(_s->mouse.x, _s->mouse.y);
	} else if (_s->_lastKeyHit) {
		if (_dialog != KEYS_DIALOG) {
			if (_s->_lastKeyHit == 27)
				close();
			else {
				addLetter((unsigned char)_s->_lastKeyHit);
				checkHotKey(_s->_lastKeyHit);
			}
#ifdef _WIN32_WCE
		} else if (_s->_lastKeyHit > 1000) {	// GAPI
			addLetter(_s->_lastKeyHit - 1000);
#endif
		}
	}

	if (_clickTimer && !--_clickTimer) {
		int old = _clickWidget;
		_clickWidget = 0;
		draw(old);
		handleCommand(old);
	}

	_s->drawDirtyScreenParts();
	_s->_mouseButStat = 0;
}

void Gui::close()
{
	_s->_fullRedraw = true;
	_s->_completeScreenRedraw = true;
	_s->_cursorAnimate--;

	// Restore old cursor
	memcpy(_s->_grabbedCursor, _old_grabbedCursor, sizeof(_old_grabbedCursor));
	_s->_cursorWidth = _old_cursorWidth;
	_s->_cursorHeight = _old_cursorHeight;
	_s->_cursorHotspotX = _old_cursorHotspotX;
	_s->_cursorHotspotY = _old_cursorHotspotY;
	_s->updateCursor();

	_s->_system->show_mouse(_old_cursor_mode);

	_s->pauseSounds(_old_soundsPaused);
	_active = false;
	_inited = false;

#ifdef _WIN32_WCE

	// Option dialog can be accessed from the file dialog now, always check
	if (draw_keyboard) {
		draw_keyboard = false;
		toolbar_drawn = false;
	}
#endif
}

void Gui::checkHotKey(int keycode)
{
	byte page;
	for (int i = 0; i < (int)(sizeof(_widgets) / sizeof(_widgets[0])); i++) {
		const GuiWidget *w = _widgets[i];
		if (w) {
			while (w->_type != GUI_NONE) {

				// This rubbish is needed because the current page is 0 when really it should be 1
				if (_cur_page == 0)
					page = 1;
				else
					page = _cur_page;

				// Only check for widgets that are on the current GUI page (otherwise save dialog problems occur)
				if (w->_page == page) {
					// Check the actual key pressed, and the uppercase version. For people who have caps lock on
					if (keycode == w->_hotkey || toupper(keycode) == w->_hotkey)
						handleCommand(w->_id);
				}
				w++;
			}
		}
	}
}

void Gui::saveLoadDialog()
{
	_widgets[0] = save_load_dialog;
	_editString = -1;
	_active = true;
	_cur_page = 0;
	_dialog = SAVELOAD_DIALOG;
}

void Gui::pause()
{
	_widgets[0] = pause_dialog;
	_active = true;
	_cur_page = 0;
	_dialog = PAUSE_DIALOG;
}

void Gui::options()
{
#ifdef _WIN32_WCE
	_current_page = 0;
#endif
	_widgets[0] = options_dialog;
	_active = true;
	_cur_page = 0;
	_dialog = OPTIONS_DIALOG;
}

void Gui::launcher()
{
	_widgets[0] = launcher_dialog;
	_active = true;
	_cur_page = 0;
	_dialog = LAUNCHER_DIALOG;
}


















/*
void Gui::loop()
{
	if (_active && !_inited) {
		_inited = true;
		_old_soundsPaused = _s->_soundsPaused;
		_s->pauseSounds(true);

		// Backup old cursor
		memcpy(_old_grabbedCursor, _s->_grabbedCursor, sizeof(_old_grabbedCursor));
		_old_cursorWidth = _s->_cursorWidth;
		_old_cursorHeight = _s->_cursorHeight;
		_old_cursorHotspotX = _s->_cursorHotspotX;
		_old_cursorHotspotY = _s->_cursorHotspotY;
		_old_cursor_mode = _s->_system->show_mouse(true);

		_s->_cursorAnimate++;
		_s->gdi._cursorActive = 1;
	}
	_s->animateCursor();
	_s->getKeyInput(0);
	
	_s->drawDirtyScreenParts();
	_s->_mouseButStat = 0;
}
*/
