/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */


#include "common/error.h"
#include "common/events.h"
#include "common/system.h"
#include "queen/journal.h"

#include "queen/bankman.h"
#include "queen/display.h"
#include "queen/graphics.h"
#include "queen/grid.h"
#include "queen/logic.h"
#include "queen/music.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "queen/sound.h"

namespace Queen {

Journal::Journal(QueenEngine *vm)
	: _vm(vm) {
	_currentSavePage = 0;
	_currentSaveSlot = 0;
}

void Journal::use() {
	BobSlot *joe = _vm->graphics()->bob(0);
	_prevJoeX = joe->x;
	_prevJoeY = joe->y;

	_panelMode = PM_NORMAL;
	_system = g_system;

	_panelTextCount = 0;
	memset(_panelTextY, 0, sizeof(_panelTextY));
	memset(&_textField, 0, sizeof(_textField));

	memset(_saveDescriptions, 0, sizeof(_saveDescriptions));
	_vm->findGameStateDescriptions(_saveDescriptions);

	setup();
	redraw();
	update();
	_vm->display()->palFadeIn(ROOM_JOURNAL);

	_quitMode = QM_LOOP;
	while (_quitMode == QM_LOOP) {
		Common::Event event;
		Common::EventManager *eventMan = _system->getEventManager();
		while (eventMan->pollEvent(event)) {
			switch (event.type) {
			case Common::EVENT_KEYDOWN:
				handleKeyDown(event.kbd.ascii, event.kbd.keycode);
				break;
			case Common::EVENT_LBUTTONDOWN:
				handleMouseDown(event.mouse.x, event.mouse.y);
				break;
			case Common::EVENT_WHEELUP:
				handleMouseWheel(-1);
				break;
			case Common::EVENT_WHEELDOWN:
				handleMouseWheel(1);
				break;
			case Common::EVENT_RTL:
			case Common::EVENT_QUIT:
				return;
			default:
				break;
			}
		}
		_system->delayMillis(20);
		_system->updateScreen();
	}

	_vm->writeOptionSettings();

	_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
	_vm->graphics()->putCameraOnBob(0);
	if (_quitMode == QM_CONTINUE) {
		continueGame();
	}
}

void Journal::continueGame() {
	_vm->display()->fullscreen(false);
	_vm->display()->forceFullRefresh();

	_vm->logic()->joePos(_prevJoeX, _prevJoeY);
	_vm->logic()->joeCutFacing(_vm->logic()->joeFacing());

	_vm->logic()->oldRoom(_vm->logic()->currentRoom());
	_vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE, 0, 0, false);
}

void Journal::setup() {
	_vm->display()->palFadeOut(_vm->logic()->currentRoom());
	_vm->display()->horizontalScroll(0);
	_vm->display()->fullscreen(true);
	_vm->graphics()->clearBobs();
	_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
	_vm->bankMan()->eraseFrames(false);
	_vm->display()->textCurrentColor(_vm->display()->getInkColor(INK_JOURNAL));

	_vm->grid()->clear(GS_ROOM);
	for (int i = 0; i < MAX_ZONES; ++i) {
		const Zone *zn = &_zones[i];
		_vm->grid()->setZone(GS_ROOM, zn->num, zn->x1, zn->y1, zn->x2, zn->y2);
	}

	_vm->display()->setupNewRoom("journal", ROOM_JOURNAL);
	_vm->bankMan()->load("journal.BBK", JOURNAL_BANK);
	for (int f = 1; f <= 20; ++f) {
		int frameNum = JOURNAL_FRAMES + f;
		_vm->bankMan()->unpack(f, frameNum, JOURNAL_BANK);
		BobFrame *bf = _vm->bankMan()->fetchFrame(frameNum);
		bf->xhotspot = 0;
		bf->yhotspot = 0;
		if (f == FRAME_INFO_BOX) { // adjust info box hot spot to put it always on top
			bf->yhotspot = 200;
		}
	}
	_vm->bankMan()->close(JOURNAL_BANK);

	_textField.x = 136;
	_textField.y = 9;
	_textField.w = 146;
	_textField.h = 13;
}

void Journal::redraw() {
	drawNormalPanel();
	drawConfigPanel();
	drawSaveDescriptions();
	drawSaveSlot();
}

void Journal::update() {
	_vm->graphics()->sortBobs();
	_vm->display()->prepareUpdate();
	_vm->graphics()->drawBobs();
	if (_textField.enabled) {
		int16 x = _textField.x + _textField.posCursor;
		int16 y = _textField.y + _currentSaveSlot * _textField.h + 8;
		_vm->display()->drawBox(x, y, x + 6, y, _vm->display()->getInkColor(INK_JOURNAL));
	}
	_vm->display()->forceFullRefresh();
	_vm->display()->update();
	_system->updateScreen();
}

void Journal::showBob(int bobNum, int16 x, int16 y, int frameNum) {
	BobSlot *bob = _vm->graphics()->bob(bobNum);
	bob->curPos(x, y);
	bob->frameNum = JOURNAL_FRAMES + frameNum;
}

void Journal::hideBob(int bobNum) {
	_vm->graphics()->bob(bobNum)->active = false;
}

void Journal::drawSaveDescriptions() {
	for (int i = 0; i < NUM_SAVES_PER_PAGE; ++i) {
		int n = _currentSavePage * 10 + i;
		char nb[4];
		sprintf(nb, "%d", n + 1);
		int y = _textField.y + i * _textField.h;
		_vm->display()->setText(_textField.x, y, _saveDescriptions[n], false);
		_vm->display()->setText(_textField.x - 27, y + 1, nb, false);
	}
	// highlight current page
	showBob(BOB_SAVE_PAGE, 300, 3 + _currentSavePage * 15, 6 + _currentSavePage);
}

void Journal::drawSaveSlot() {
	showBob(BOB_SAVE_DESC, 130, 6 + _currentSaveSlot * 13, 17);
}

void Journal::enterYesNoPanelMode(int16 prevZoneNum, int titleNum) {
	_panelMode = PM_YES_NO;
	_prevZoneNum = prevZoneNum;
	drawYesNoPanel(titleNum);
}

void Journal::exitYesNoPanelMode() {
	_panelMode = PM_NORMAL;
	if (_prevZoneNum == ZN_MAKE_ENTRY) {
		closeTextField();
	}
	redraw();
}

void Journal::enterInfoPanelMode() {
	_panelMode = PM_INFO_BOX;
	_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
	drawInfoPanel();
}

void Journal::exitInfoPanelMode() {
	_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
	hideBob(BOB_INFO_BOX);
	redraw();
	_panelMode = PM_NORMAL;
}

void Journal::handleKeyDown(uint16 ascii, int keycode) {
	switch (_panelMode) {
	case PM_INFO_BOX:
		break;
	case PM_YES_NO:
		if (keycode == Common::KEYCODE_ESCAPE) {
			exitYesNoPanelMode();
		} else if (_textField.enabled) {
			updateTextField(ascii, keycode);
		}
		break;
	case PM_NORMAL:
		if (keycode == Common::KEYCODE_ESCAPE) {
			_quitMode = QM_CONTINUE;
		}
		break;
	}
}

void Journal::handleMouseWheel(int inc) {
	if (_panelMode == PM_NORMAL) {
		int curSave = _currentSavePage * NUM_SAVES_PER_PAGE + _currentSaveSlot + inc;
		if (curSave >= 0 && curSave < NUM_SAVES_PER_PAGE * 10) {
			_currentSavePage = curSave / NUM_SAVES_PER_PAGE;
			_currentSaveSlot = curSave % NUM_SAVES_PER_PAGE;
			drawSaveDescriptions();
			drawSaveSlot();
			update();
		}
	}
}

void Journal::handleMouseDown(int x, int y) {
	int val;
	int16 zoneNum = _vm->grid()->findZoneForPos(GS_ROOM, x, y);
	switch (_panelMode) {
	case PM_INFO_BOX:
		exitInfoPanelMode();
		break;
	case PM_YES_NO:
		if (zoneNum == ZN_YES) {
			_panelMode = PM_NORMAL;
			int currentSlot = _currentSavePage * 10 + _currentSaveSlot;
			switch (_prevZoneNum) {
			case ZN_REVIEW_ENTRY:
				if (_saveDescriptions[currentSlot][0]) {
					_vm->graphics()->clearBobs();
					_vm->display()->palFadeOut(ROOM_JOURNAL);
					_vm->sound()->stopSong();
					_vm->loadGameState(currentSlot);
					_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
					_quitMode = QM_RESTORE;
				} else {
					exitYesNoPanelMode();
				}
				break;
			case ZN_MAKE_ENTRY:
				if (_textField.text[0]) {
					closeTextField();
					_vm->saveGameState(currentSlot, _textField.text);
					_quitMode = QM_CONTINUE;
				} else {
					exitYesNoPanelMode();
				}
				break;
			case ZN_GIVEUP:
				_quitMode = QM_CONTINUE;
				_vm->quitGame();
				break;
			}
		} else if (zoneNum == ZN_NO) {
			exitYesNoPanelMode();
		}
		break;
	case PM_NORMAL:
		switch (zoneNum) {
		case ZN_REVIEW_ENTRY:
			enterYesNoPanelMode(zoneNum, TXT_REVIEW_ENTRY);
			break;
		case ZN_MAKE_ENTRY:
			initTextField(_saveDescriptions[_currentSavePage * 10 + _currentSaveSlot]);
			enterYesNoPanelMode(zoneNum, TXT_MAKE_ENTRY);
			break;
		case ZN_CLOSE:
			_quitMode = QM_CONTINUE;
			break;
		case ZN_GIVEUP:
			enterYesNoPanelMode(zoneNum, TXT_GIVE_UP);
			break;
		case ZN_TEXT_SPEED:
			val = (x - 136) * QueenEngine::MAX_TEXT_SPEED / (266 - 136);
			_vm->talkSpeed(val);
			drawConfigPanel();
			break;
		case ZN_SFX_TOGGLE:
			_vm->sound()->toggleSfx();
			drawConfigPanel();
			break;
		case ZN_MUSIC_VOLUME:
			val = (x - 136) * Audio::Mixer::kMaxMixerVolume / (266 - 136);
			_vm->sound()->setVolume(val);
			drawConfigPanel();
			break;
		case ZN_DESC_1:
		case ZN_DESC_2:
		case ZN_DESC_3:
		case ZN_DESC_4:
		case ZN_DESC_5:
		case ZN_DESC_6:
		case ZN_DESC_7:
		case ZN_DESC_8:
		case ZN_DESC_9:
		case ZN_DESC_10:
			_currentSaveSlot = zoneNum - ZN_DESC_1;
			drawSaveSlot();
			break;
		case ZN_PAGE_A:
		case ZN_PAGE_B:
		case ZN_PAGE_C:
		case ZN_PAGE_D:
		case ZN_PAGE_E:
		case ZN_PAGE_F:
		case ZN_PAGE_G:
		case ZN_PAGE_H:
		case ZN_PAGE_I:
		case ZN_PAGE_J:
			_currentSavePage = zoneNum - ZN_PAGE_A;
			drawSaveDescriptions();
			break;
		case ZN_INFO_BOX:
			enterInfoPanelMode();
			break;
		case ZN_MUSIC_TOGGLE:
			_vm->sound()->toggleMusic();
			if (_vm->sound()->musicOn()) {
				_vm->sound()->playLastSong();
			} else {
				_vm->sound()->stopSong();
			}
			drawConfigPanel();
			break;
		case ZN_VOICE_TOGGLE:
			_vm->sound()->toggleSpeech();
			drawConfigPanel();
			break;
		case ZN_TEXT_TOGGLE:
			_vm->subtitles(!_vm->subtitles());
			drawConfigPanel();
			break;
		}
		break;
	}
	update();
}

static void removeLeadingAndTrailingSpaces(char *dst, size_t dstSize, const char* src) {
	assert(dstSize > 0);
	size_t srcLen = strlen(src);
	if (0 == srcLen) {
		dst[0] = '\0';
		return;
	}

	size_t firstNonSpaceIndex;
	for (firstNonSpaceIndex = 0; firstNonSpaceIndex < srcLen; ++firstNonSpaceIndex) {
		if (src[firstNonSpaceIndex] != ' ')
			break;
	}
	if (firstNonSpaceIndex == srcLen) {
		dst[0] = '\0';
		return;
	}

	size_t lastNonSpaceIndex = srcLen - 1;
	while (src[lastNonSpaceIndex] == ' ')
		--lastNonSpaceIndex;

	uint newLen = lastNonSpaceIndex - firstNonSpaceIndex + 1;
	assert(newLen < dstSize);
	for (size_t i = 0; i < newLen; ++i) {
		dst[i] = src[firstNonSpaceIndex + i];
	}
	dst[newLen] = '\0';
}

void Journal::drawPanelText(int y, const char *text) {
	debug(7, "Journal::drawPanelText(%d, '%s')", y, text);

	char s[128];
	removeLeadingAndTrailingSpaces(s, 128, text); // necessary for spanish version

	// draw the substrings
	char *p = strchr(s, ' ');
	if (!p) {
		int x = (128 - _vm->display()->textWidth(s)) / 2;
		_vm->display()->setText(x, y, s, false);
		assert(_panelTextCount < MAX_PANEL_TEXTS);
		_panelTextY[_panelTextCount++] = y;
	} else {
		*p++ = '\0';
		if (_vm->resource()->getLanguage() == Common::HE_ISR) {
			drawPanelText(y - 5, p);
			drawPanelText(y + 5, s);
		} else {
			drawPanelText(y - 5, s);
			drawPanelText(y + 5, p);
		}
	}
}

void Journal::drawCheckBox(bool active, int bobNum, int16 x, int16 y, int frameNum) {
	if (active) {
		showBob(bobNum, x, y, frameNum);
	} else {
		hideBob(bobNum);
	}
}

void Journal::drawSlideBar(int value, int maxValue, int bobNum, int16 y, int frameNum) {
	showBob(bobNum, 136 + value * (266 - 136) / maxValue, y, frameNum);
}

void Journal::drawPanel(const int *frames, const int *titles, int n) {
	for (int i = 0; i < _panelTextCount; ++i) {
		_vm->display()->clearTexts(_panelTextY[i], _panelTextY[i]);
	}
	_panelTextCount = 0;
	int bobNum = 1;
	int y = 8;
	while (n--) {
		showBob(bobNum++, 32, y, *frames++);
		drawPanelText(y + 12, _vm->logic()->joeResponse(*titles++));
		y += 48;
	}
}

void Journal::drawNormalPanel() {
	static const int frames[] = { FRAME_BLUE_1, FRAME_BLUE_2, FRAME_BLUE_1, FRAME_ORANGE };
	static const int titles[] = { TXT_REVIEW_ENTRY, TXT_MAKE_ENTRY, TXT_CLOSE, TXT_GIVE_UP };
	drawPanel(frames, titles, 4);
}

void Journal::drawYesNoPanel(int titleNum) {
	static const int frames[] = { FRAME_GREY, FRAME_BLUE_1, FRAME_BLUE_2 };
	const int titles[] = { titleNum, TXT_YES, TXT_NO };
	drawPanel(frames, titles, 3);

	hideBob(BOB_LEFT_RECT_4);
	hideBob(BOB_TALK_SPEED);
	hideBob(BOB_SFX_TOGGLE);
	hideBob(BOB_MUSIC_VOLUME);
	hideBob(BOB_SPEECH_TOGGLE);
	hideBob(BOB_TEXT_TOGGLE);
	hideBob(BOB_MUSIC_TOGGLE);
}

void Journal::drawConfigPanel() {
	_vm->checkOptionSettings();

	drawSlideBar(_vm->talkSpeed(), QueenEngine::MAX_TEXT_SPEED, BOB_TALK_SPEED, 164, FRAME_BLUE_PIN);
	drawSlideBar(_vm->sound()->getVolume(), Audio::Mixer::kMaxMixerVolume, BOB_MUSIC_VOLUME, 177, FRAME_GREEN_PIN);

	drawCheckBox(_vm->sound()->sfxOn(), BOB_SFX_TOGGLE, 221, 155, FRAME_CHECK_BOX);
	drawCheckBox(_vm->sound()->speechOn(), BOB_SPEECH_TOGGLE, 158, 155, FRAME_CHECK_BOX);
	drawCheckBox(_vm->subtitles(), BOB_TEXT_TOGGLE, 125, 167, FRAME_CHECK_BOX);
	drawCheckBox(_vm->sound()->musicOn(), BOB_MUSIC_TOGGLE, 125, 181, FRAME_CHECK_BOX);
}

void Journal::drawInfoPanel() {
	showBob(BOB_INFO_BOX, 72, 221, FRAME_INFO_BOX);
	const char *ver = _vm->resource()->getJASVersion();
	switch (ver[0]) {
	case 'P':
		_vm->display()->setTextCentered(132, "PC Hard Drive", false);
		break;
	case 'C':
		_vm->display()->setTextCentered(132, "PC CD-ROM", false);
		break;
	case 'a':
		_vm->display()->setTextCentered(132, "Amiga A500/600", false);
		break;
	}
	switch (ver[1]) {
	case 'E':
		_vm->display()->setTextCentered(144, "English", false);
		break;
	case 'F' :
		_vm->display()->setTextCentered(144, "Fran\x87""ais", false);
		break;
	case 'G':
		_vm->display()->setTextCentered(144, "Deutsch", false);
		break;
	case 'H':
		_vm->display()->setTextCentered(144, "Hebrew", false);
		break;
	case 'I':
		_vm->display()->setTextCentered(144, "Italiano", false);
		break;
	case 'S':
		_vm->display()->setTextCentered(144, "Espa\xA4""ol", false);
		break;
	}
	char versionId[13];
	sprintf(versionId, "Version %c.%c%c", ver[2], ver[3], ver[4]);
	_vm->display()->setTextCentered(156, versionId, false);
}

void Journal::initTextField(const char *desc) {
	_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
	_textField.enabled = true;
	_textField.posCursor = _vm->display()->textWidth(desc);
	_textField.textCharsCount = strlen(desc);
	memset(_textField.text, 0, sizeof(_textField.text));
	strcpy(_textField.text, desc);
}

void Journal::updateTextField(uint16 ascii, int keycode) {
	bool dirty = false;
	switch (keycode) {
	case Common::KEYCODE_BACKSPACE:
		if (_textField.textCharsCount > 0) {
			--_textField.textCharsCount;
			_textField.text[_textField.textCharsCount] = '\0';
			dirty = true;
		}
		break;
	case Common::KEYCODE_RETURN:
	case Common::KEYCODE_KP_ENTER:
		if (_textField.text[0]) {
			closeTextField();
			int currentSlot = _currentSavePage * 10 + _currentSaveSlot;
			_vm->saveGameState(currentSlot, _textField.text);
			_quitMode = QM_CONTINUE;
		}
		break;
	default:
		if (Common::isPrint((char)ascii) &&
			_textField.textCharsCount < (sizeof(_textField.text) - 1) &&
			_vm->display()->textWidth(_textField.text) < _textField.w) {
			_textField.text[_textField.textCharsCount] = (char)ascii;
			++_textField.textCharsCount;
			dirty = true;
		}
		break;
	}
	if (dirty) {
		_vm->display()->setText(_textField.x, _textField.y + _currentSaveSlot * _textField.h, _textField.text, false);
		_textField.posCursor = _vm->display()->textWidth(_textField.text);
		update();
	}
}

void Journal::closeTextField() {
	_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
	_textField.enabled = false;
}

const Journal::Zone Journal::_zones[] = {
	{ ZN_REVIEW_ENTRY,  32,   8,  96,  40 },
	{ ZN_MAKE_ENTRY,    32,  56,  96,  88 }, // == ZN_YES
	{ ZN_CLOSE,         32, 104,  96, 136 }, // == ZN_NO
	{ ZN_GIVEUP,        32, 152,  96, 184 },
	{ ZN_TEXT_SPEED,   136, 169, 265, 176 },
	{ ZN_SFX_TOGGLE,   197, 155, 231, 164 },
	{ ZN_MUSIC_VOLUME, 136, 182, 265, 189 },
	{ ZN_DESC_1,       131,   7, 290,  18 },
	{ ZN_DESC_2,       131,  20, 290,  31 },
	{ ZN_DESC_3,       131,  33, 290,  44 },
	{ ZN_DESC_4,       131,  46, 290,  57 },
	{ ZN_DESC_5,       131,  59, 290,  70 },
	{ ZN_DESC_6,       131,  72, 290,  83 },
	{ ZN_DESC_7,       131,  85, 290,  96 },
	{ ZN_DESC_8,       131,  98, 290, 109 },
	{ ZN_DESC_9,       131, 111, 290, 122 },
	{ ZN_DESC_10,      131, 124, 290, 135 },
	{ ZN_PAGE_A,       300,   4, 319,  17 },
	{ ZN_PAGE_B,       300,  19, 319,  32 },
	{ ZN_PAGE_C,       300,  34, 319,  47 },
	{ ZN_PAGE_D,       300,  49, 319,  62 },
	{ ZN_PAGE_E,       300,  64, 319,  77 },
	{ ZN_PAGE_F,       300,  79, 319,  92 },
	{ ZN_PAGE_G,       300,  94, 319, 107 },
	{ ZN_PAGE_H,       300, 109, 319, 122 },
	{ ZN_PAGE_I,       300, 124, 319, 137 },
	{ ZN_PAGE_J,       300, 139, 319, 152 },
	{ ZN_INFO_BOX,     273, 146, 295, 189 },
	{ ZN_MUSIC_TOGGLE, 109, 181, 135, 190 },
	{ ZN_VOICE_TOGGLE, 134, 155, 168, 164 },
	{ ZN_TEXT_TOGGLE,  109, 168, 135, 177 }
};

} // End of namespace Queen