/* 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 "neverhood/gamemodule.h"

#include "neverhood/graphics.h"
#include "neverhood/menumodule.h"
#include "neverhood/modules/module1000.h"
#include "neverhood/modules/module1100.h"
#include "neverhood/modules/module1200.h"
#include "neverhood/modules/module1300.h"
#include "neverhood/modules/module1400.h"
#include "neverhood/modules/module1500.h"
#include "neverhood/modules/module1600.h"
#include "neverhood/modules/module1700.h"
#include "neverhood/modules/module1800.h"
#include "neverhood/modules/module1900.h"
#include "neverhood/modules/module2000.h"
#include "neverhood/modules/module2100.h"
#include "neverhood/modules/module2200.h"
#include "neverhood/modules/module2300.h"
#include "neverhood/modules/module2400.h"
#include "neverhood/modules/module2500.h"
#include "neverhood/modules/module2600.h"
#include "neverhood/modules/module2700.h"
#include "neverhood/modules/module2800.h"
#include "neverhood/modules/module2900.h"
#include "neverhood/modules/module3000.h"

namespace Neverhood {

static const uint32 kRadioMusicFileHashes[] = {
	0x82B22000,
	0x02B22004,
	0x42B22000,
	0x03322008,
	0x02B22001,
	0x02B22008,
	0x02B22020,
	0x03322001,
	0x03322002,
	0x03322004,
	0x03322040,
	0x02B22002,
	0x02B22010,
	0x03322010,
	0x02B22040,
	0x43322000,
	0x83322000,
	0x03322020
};

enum {
	MENU_MODULE			= 9999
};

GameModule::GameModule(NeverhoodEngine *vm)
	: Module(vm, NULL), _moduleNum(-1), _prevChildObject(NULL), _prevModuleNum(-1),
	_restoreGameRequested(false), _restartGameRequested(false), _canRequestMainMenu(true),
	_mainMenuRequested(false) {

	// Other initializations moved to actual engine class
	_vm->_soundMan->playSoundThree(0x002D0031, 0x08861079);
	SetMessageHandler(&GameModule::handleMessage);
}

GameModule::~GameModule() {
	_vm->_soundMan->deleteSoundGroup(0x002D0031);
	delete _childObject;
	_childObject = NULL;
}

void GameModule::handleMouseMove(int16 x, int16 y) {
	if (_childObject) {
		NPoint mousePos;
		mousePos.x = x;
		mousePos.y = y;
		debug(2, "GameModule::handleMouseMove(%d, %d)", x, y);
		sendPointMessage(_childObject, NM_MOUSE_MOVE, mousePos);
	}
}

void GameModule::handleMouseDown(int16 x, int16 y) {
	if (_childObject) {
		NPoint mousePos;
		mousePos.x = x;
		mousePos.y = y;
		debug(2, "GameModule::handleMouseDown(%d, %d)", x, y);
		sendPointMessage(_childObject, NM_MOUSE_CLICK, mousePos);
	}
}

void GameModule::handleMouseUp(int16 x, int16 y) {
	if (_childObject) {
		NPoint mousePos;
		mousePos.x = x;
		mousePos.y = y;
		debug(2, "GameModule::handleMouseUp(%d, %d)", x, y);
		sendPointMessage(_childObject, NM_MOUSE_RELEASE, mousePos);
	}
}

void GameModule::handleWheelUp() {
	if (_childObject) {
		sendMessage(_childObject, NM_MOUSE_WHEELUP, 0);
	}
}

void GameModule::handleWheelDown() {
	if (_childObject) {
		sendMessage(_childObject, NM_MOUSE_WHEELDOWN, 0);
	}
}

void GameModule::handleSpaceKey() {
	if (_childObject) {
		debug(2, "GameModule::handleSpaceKey()");
		sendMessage(_childObject, NM_KEYPRESS_SPACE, 0);
	}
}

void GameModule::handleAsciiKey(char key) {
	if (_childObject) {
		debug(2, "GameModule::handleAsciiKey()");
		sendMessage(_childObject, 0x000A, (uint32)key);
	}
}

void GameModule::handleKeyDown(Common::KeyCode keyCode) {
	if (_childObject) {
		if (keyCode == Common::KEYCODE_ESCAPE)
			handleEscapeKey();
		else if (keyCode == Common::KEYCODE_SPACE)
			handleSpaceKey();
		debug(2, "GameModule::handleKeyDown()");
		sendMessage(_childObject, 0x000B, keyCode);
	}
}

void GameModule::handleEscapeKey() {
	if (_vm->isDemo())
		_vm->quitGame();
	else if (!_prevChildObject && _canRequestMainMenu)
		_mainMenuRequested = true;
	else if (_childObject)
		sendMessage(_childObject, NM_KEYPRESS_ESC, 0);
}

void GameModule::initKeySlotsPuzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x25400B10)) {
		NonRepeatingRandomNumbers keySlots(_vm->_rnd, 16);
		for (uint i = 0; i < 3; i++) {
			setSubVar(VA_GOOD_KEY_SLOT_NUMBERS, i, keySlots.getNumber());
			setSubVar(VA_CURR_KEY_SLOT_NUMBERS, i, keySlots.getNumber());
		}
		setSubVar(VA_IS_PUZZLE_INIT, 0x25400B10, 1);
	}
}

void GameModule::initMemoryPuzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0xC8606803)) {
		NonRepeatingRandomNumbers diceIndices(_vm->_rnd, 3);
		NonRepeatingRandomNumbers availableTiles(_vm->_rnd, 48);
		NonRepeatingRandomNumbers tileSymbols(_vm->_rnd, 10);
		for (uint32 i = 0; i < 3; i++)
			setSubVar(VA_CURR_DICE_NUMBERS, i, 1);
		// Set special symbols
		// Symbol 5 is always one of the three special symbols
		setSubVar(VA_DICE_MEMORY_SYMBOLS, diceIndices.getNumber(), 5);
		tileSymbols.removeNumber(5);
		for (int i = 0; i < 2; i++)
			setSubVar(VA_DICE_MEMORY_SYMBOLS, diceIndices.getNumber(), tileSymbols.getNumber());
		// Insert special symbols tiles
		for (uint32 i = 0; i < 3; ++i) {
			int tileSymbolOccurence = _vm->_rnd->getRandomNumber(4 - 1) * 2 + 2;
			setSubVar(VA_GOOD_DICE_NUMBERS, i, tileSymbolOccurence);
			while (tileSymbolOccurence--)
				setSubVar(VA_TILE_SYMBOLS, availableTiles.getNumber(), getSubVar(VA_DICE_MEMORY_SYMBOLS, i));
		}
		// Fill the remaining tiles
		uint32 tileSymbolIndex = 0;
		while (!availableTiles.empty()) {
			setSubVar(VA_TILE_SYMBOLS, availableTiles.getNumber(), tileSymbols[tileSymbolIndex]);
			setSubVar(VA_TILE_SYMBOLS, availableTiles.getNumber(), tileSymbols[tileSymbolIndex]);
			tileSymbolIndex++;
			if (tileSymbolIndex >= tileSymbols.size())
				tileSymbolIndex = 0;
		}
		setSubVar(VA_IS_PUZZLE_INIT, 0xC8606803, 1);
	}
}

void GameModule::initWaterPipesPuzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x40520234)) {
		setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 0, 3);
		setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 1, 1);
		setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 2, 2);
		setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 3, 0);
		setSubVar(VA_GOOD_WATER_PIPES_LEVEL, 4, 4);
		setSubVar(VA_IS_PUZZLE_INIT, 0x40520234, 1);
	}
}

void GameModule::initRadioPuzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x08C80800)) {
		int currMusicIndex = _vm->_rnd->getRandomNumber(5 - 1) + 3;
		setGlobalVar(V_GOOD_RADIO_MUSIC_INDEX, 5 * currMusicIndex);
		setGlobalVar(V_GOOD_RADIO_MUSIC_NAME, kRadioMusicFileHashes[currMusicIndex]);
		setGlobalVar(V_RADIO_ROOM_LEFT_DOOR, 1);
		setGlobalVar(V_RADIO_ROOM_RIGHT_DOOR, 0);
		setSubVar(VA_IS_PUZZLE_INIT, 0x08C80800, 1);
	}
}

void GameModule::initTestTubes1Puzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x20479010)) {
		for (uint i = 0; i < 3; i++)
			setSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, i, _vm->_rnd->getRandomNumber(3 - 1) + 1);
		setSubVar(VA_IS_PUZZLE_INIT, 0x20479010, 1);
	}
}

void GameModule::initTestTubes2Puzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x66059818)) {
		for (uint i = 0; i < 3; i++)
			setSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, i, _vm->_rnd->getRandomNumber(6 - 1) + 1);
		setSubVar(VA_IS_PUZZLE_INIT, 0x66059818, 1);
	}
}

void GameModule::initCannonSymbolsPuzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x8C9819C2)) {
		for (int i = 0; i < 3; i++) {
			setSubVar(VA_GOOD_CANNON_SYMBOLS_1, i, _vm->_rnd->getRandomNumber(12 - 1));
			setSubVar(VA_GOOD_CANNON_SYMBOLS_2, i, _vm->_rnd->getRandomNumber(12 - 1));
		}
		setSubVar(VA_IS_PUZZLE_INIT, 0x8C9819C2, 1);
	}
}

void GameModule::initCodeSymbolsPuzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x0CD09B50)) {
		for (int i = 0; i < 12; ++i)
			setSubVar(VA_CODE_SYMBOLS, i, i);
		for (int i = 0; i < 12; ++i) {
			uint32 index1 = _vm->_rnd->getRandomNumber(12 - 1);
			uint32 index2 = _vm->_rnd->getRandomNumber(12 - 1);
			uint32 temp = getSubVar(VA_CODE_SYMBOLS, index1);
			setSubVar(VA_CODE_SYMBOLS, index1, getSubVar(VA_CODE_SYMBOLS, index2));
			setSubVar(VA_CODE_SYMBOLS, index2, temp);
		}
		setGlobalVar(V_NOISY_SYMBOL_INDEX, _vm->_rnd->getRandomNumber(11 - 1) + 1);
		setSubVar(VA_IS_PUZZLE_INIT, 0x0CD09B50, 1);
	}
}

void GameModule::initCubeSymbolsPuzzle() {
	if (!getSubVar(VA_IS_PUZZLE_INIT, 0x60400854)) {
		NonRepeatingRandomNumbers cubeSymbols(_vm->_rnd, 9);
		for (uint32 cubePosition = 0; cubePosition < 9; ++cubePosition)
			setSubVar(VA_CUBE_POSITIONS, cubePosition, (uint32)(cubeSymbols.getNumber() - 1));
		setSubVar(VA_IS_PUZZLE_INIT, 0x60400854, 1);
	}
}

void GameModule::initCrystalColorsPuzzle() {
	if (!getGlobalVar(V_CRYSTAL_COLORS_INIT)) {
		TextResource textResource(_vm);
		const char *textStart, *textEnd;
		textResource.load(0x46691611);
		textStart = textResource.getString(0, textEnd);
		for (uint index = 0; index < 5; index++) {
			char colorLetter = (byte)textStart[index];
			byte correctColorNum = 0, misalignedColorNum;
			switch (colorLetter) {
			case 'B':
				correctColorNum = 4;
				break;
			case 'G':
				correctColorNum = 3;
				break;
			case 'O':
				correctColorNum = 1;
				break;
			case 'R':
				correctColorNum = 0;
				break;
			case 'V':
				correctColorNum = 5;
				break;
			case 'Y':
				correctColorNum = 2;
				break;
			}
			do {
				misalignedColorNum = _vm->_rnd->getRandomNumber(6 - 1);
			} while (misalignedColorNum == correctColorNum);
			setSubVar(VA_GOOD_CRYSTAL_COLORS, index, correctColorNum);
			setSubVar(VA_CURR_CRYSTAL_COLORS, index, misalignedColorNum);
		}
		setGlobalVar(V_CRYSTAL_COLORS_INIT, 1);
	}
}

uint32 GameModule::getCurrRadioMusicFileHash() {
	uint musicIndex = getGlobalVar(V_CURR_RADIO_MUSIC_INDEX);
	return (musicIndex % 5 != 0) ? 0 : kRadioMusicFileHashes[CLIP<uint>(musicIndex / 5, 0, 17)];
}


uint32 GameModule::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
	uint32 messageResult = Module::handleMessage(messageNum, param, sender);
	switch (messageNum) {
	case 0x0800:
		_canRequestMainMenu = true;
		break;
	case 0x1009:
		_moduleResult = param.asInteger();
		_done = true;
		break;
	}
	return messageResult;
}

void GameModule::startup() {
#if 1
	// Logos and intro video // Real game start
	createModule(1500, 0);
#else
	// DEBUG>>>
	/*
	setGlobalVar(V_SEEN_MUSIC_BOX, 1);
	setGlobalVar(V_CREATURE_EXPLODED, 0);
	setGlobalVar(V_MATCH_STATUS, 0);
	setGlobalVar(V_PROJECTOR_LOCATION, 2);
	*/
	//setGlobalVar(V_ENTRANCE_OPEN, 0);
	//setGlobalVar(V_DOOR_SPIKES_OPEN, 1);
	//setGlobalVar(V_CREATURE_ANGRY, 1);
	setGlobalVar(V_RADIO_ENABLED, 1);
	//setGlobalVar(V_TNT_DUMMY_BUILT, 1);
	setGlobalVar(V_FLYTRAP_RING_DOOR, 1);
	setGlobalVar(V_TV_JOKE_TOLD, 1);
	/*
	// Give all disks
	for (int i = 0; i < 20; i++)
		setSubVar(VA_IS_TAPE_INSERTED, i, 1);
	*/
	setSubVar(VA_IS_KEY_INSERTED, 0, 1);
	setSubVar(VA_IS_KEY_INSERTED, 1, 1);
	setSubVar(VA_IS_KEY_INSERTED, 2, 1);
	for (uint32 index = 0; index < 9; index++)
		setSubVar(VA_CUBE_POSITIONS, index, 7 - index);
	setGlobalVar(V_WALL_BROKEN, 0);
	setGlobalVar(V_WORLDS_JOINED, 1);
	setGlobalVar(V_RADIO_MOVE_DISH_VIDEO, 0);
	// Enable all locations
	for (int i = 0; i < 6; i++)
		setSubVar(V_TELEPORTER_DEST_AVAILABLE, i, 1);
	//setGlobalVar(V_PROJECTOR_LOCATION, 4);
	setGlobalVar(V_KEYDOOR_UNLOCKED, 1);
	setGlobalVar(V_LIGHTS_ON, 1);
	setGlobalVar(V_WATER_RUNNING, 1);
	setGlobalVar(V_HAS_TEST_TUBE, 1);
	setSubVar(VA_CURR_WATER_PIPES_LEVEL, 0, 3);
	setSubVar(VA_CURR_WATER_PIPES_LEVEL, 1, 1);
	setSubVar(VA_CURR_WATER_PIPES_LEVEL, 2, 2);
	setSubVar(VA_CURR_WATER_PIPES_LEVEL, 3, 0);
	setSubVar(VA_CURR_WATER_PIPES_LEVEL, 4, 4);
	setGlobalVar(V_KLAYMEN_SMALL, 1);
	setGlobalVar(V_SHRINK_LIGHTS_ON, 0);
	// <<<DEBUG

#if 1
	_vm->gameState().which = 0;
	_vm->gameState().sceneNum = 0;
	createModule(2400, 0);
#endif

#endif
}

void GameModule::requestRestoreGame() {
	_restoreGameRequested = true;
}

void GameModule::requestRestartGame(bool requestMainMenu) {
	_restartGameRequested = true;
	_mainMenuRequested = requestMainMenu;
}

void GameModule::redrawPrevChildObject() {
	if (_prevChildObject) {
		_prevChildObject->draw();
		_vm->_screen->update();
	}
}

void GameModule::checkRequests() {
	if (_restartGameRequested) {
		_restartGameRequested = false;
		_vm->_gameVars->clear();
		requestRestoreGame();
	}
	if (_restoreGameRequested) {
		_restoreGameRequested = false;
		_vm->_audioResourceMan->stopAllMusic();
		_vm->_audioResourceMan->stopAllSounds();
		_vm->_soundMan->stopAllMusic();
		_vm->_soundMan->stopAllSounds();
		// Reinsert turning sound because SoundMan::stopAllSounds() removes it
		_vm->_soundMan->playSoundThree(0x002D0031, 0x08861079);
		delete _childObject;
		delete _prevChildObject;
		_childObject = NULL;
		_prevChildObject = NULL;
		_prevModuleNum = 0;
		createModuleByHash(getGlobalVar(V_MODULE_NAME));
	}
	if (_mainMenuRequested)
		openMainMenu();
}

void GameModule::createModule(int moduleNum, int which) {
	debug(1, "GameModule::createModule(%d, %d)", moduleNum, which);
	_moduleNum = moduleNum;

	delete _childObject;

	switch (_moduleNum) {
	case 1000:
		setGlobalVar(V_MODULE_NAME, 0x03294419);
		_childObject = new Module1000(_vm, this, which);
		break;
	case 1100:
		setGlobalVar(V_MODULE_NAME, 0x0002C818);
		_childObject = new Module1100(_vm, this, which);
		break;
	case 1200:
		setGlobalVar(V_MODULE_NAME, 0x00478311);
		_childObject = new Module1200(_vm, this, which);
		break;
	case 1300:
		setGlobalVar(V_MODULE_NAME, 0x0061C090);
		_childObject = new Module1300(_vm, this, which);
		break;
	case 1400:
		setGlobalVar(V_MODULE_NAME, 0x00AD0012);
		_childObject = new Module1400(_vm, this, which);
		break;
	case 1500:
		_canRequestMainMenu = false;
		setGlobalVar(V_MODULE_NAME, 0x00F10114);
		_childObject = new Module1500(_vm, this, which);
		break;
	case 1600:
		setGlobalVar(V_MODULE_NAME, 0x01A008D8);
		_childObject = new Module1600(_vm, this, which);
		break;
	case 1700:
		setGlobalVar(V_MODULE_NAME, 0x04212331);
		_childObject = new Module1700(_vm, this, which);
		break;
	case 1800:
		setGlobalVar(V_MODULE_NAME, 0x04A14718);
		_childObject = new Module1800(_vm, this, which);
		break;
	case 1900:
		setGlobalVar(V_MODULE_NAME, 0x04E1C09C);
		_childObject = new Module1900(_vm, this, which);
		break;
	case 2000:
		setGlobalVar(V_MODULE_NAME, 0x08250000);
		_childObject = new Module2000(_vm, this, which);
		break;
	case 2100:
		setGlobalVar(V_MODULE_NAME, 0x10A10C14);
		_childObject = new Module2100(_vm, this, which);
		break;
	case 2200:
		setGlobalVar(V_MODULE_NAME, 0x11391412);
		_childObject = new Module2200(_vm, this, which);
		break;
	case 2300:
		setGlobalVar(V_MODULE_NAME, 0x1A214010);
		_childObject = new Module2300(_vm, this, which);
		break;
	case 2400:
		setGlobalVar(V_MODULE_NAME, 0x202D1010);
		_childObject = new Module2400(_vm, this, which);
		break;
	case 2500:
		setGlobalVar(V_MODULE_NAME, 0x29220120);
		_childObject = new Module2500(_vm, this, which);
		break;
	case 2600:
		setGlobalVar(V_MODULE_NAME, 0x40271018);
		_childObject = new Module2600(_vm, this, which);
		break;
	case 2700:
		setGlobalVar(V_MODULE_NAME, 0x42212411);
		_childObject = new Module2700(_vm, this, which);
		break;
	case 2800:
		setGlobalVar(V_MODULE_NAME, 0x64210814);
		_childObject = new Module2800(_vm, this, which);
		break;
	case 2900:
		setGlobalVar(V_MODULE_NAME, 0x81100020);
		if (which >= 0)
			setGlobalVar(V_TELEPORTER_CURR_LOCATION, which);
		_childObject = new Module2900(_vm, this, which);
		break;
	case 3000:
		setGlobalVar(V_MODULE_NAME, 0x81293110);
		_childObject = new Module3000(_vm, this, which);
		break;
	case 9999:
		createDemoScene();
		break;
	default:
		error("GameModule::createModule() Could not create module %d", moduleNum);
	}
	SetUpdateHandler(&GameModule::updateModule);
	_childObject->handleUpdate();
}

void GameModule::createModuleByHash(uint32 nameHash) {
	debug(1, "GameModule::createModuleByHash(%08X)", nameHash);
	switch (nameHash) {
	case 0x03294419:
		createModule(1000, -1);
		break;
	case 0x0002C818:
		createModule(1100, -1);
		break;
	case 0x00478311:
		createModule(1200, -1);
		break;
	case 0x0061C090:
		createModule(1300, -1);
		break;
	case 0x00AD0012:
		createModule(1400, -1);
		break;
	case 0x00F10114:
		createModule(1500, -1);
		break;
	case 0x01A008D8:
		createModule(1600, -1);
		break;
	case 0x04212331:
		createModule(1700, -1);
		break;
	case 0x04A14718:
		createModule(1800, -1);
		break;
	case 0x04E1C09C:
		createModule(1900, -1);
		break;
	case 0x08250000:
		createModule(2000, -1);
		break;
	case 0x10A10C14:
		createModule(2100, -1);
		break;
	case 0x11391412:
		createModule(2200, -1);
		break;
	case 0x1A214010:
		createModule(2300, -1);
		break;
	case 0x202D1010:
		createModule(2400, -1);
		break;
	case 0x29220120:
		createModule(2500, -1);
		break;
	case 0x40271018:
		createModule(2600, -1);
		break;
	case 0x42212411:
		createModule(2700, -1);
		break;
	case 0x64210814:
		createModule(2800, -1);
		break;
	case 0x81100020:
		createModule(2900, -1);
		break;
	case 0x81293110:
		createModule(3000, -1);
		break;
	default:
		createModule(1000, 0);
		break;
	}
}

void GameModule::updateModule() {
	if (!updateChild()) {
		switch (_moduleNum) {
		case 1000:
			createModule(2300, 0);
			break;
		case 1200:
			if (_moduleResult == 1)
				createModule(2600, 0);
			else
				createModule(2300, 2);
			break;
		case 1100:
			if (_moduleResult == 0)
				createModule(2900, 2);
			else {
				setGlobalVar(V_ENTRANCE_OPEN, 1);
				createModule(1300, 0);
			}
			break;
		case 1300:
			if (_moduleResult == 1) {
				// The game was successfully finished
				requestRestartGame(true);
			} else
				createModule(2900, 0);
			break;
		case 1400:
			createModule(1600, 1);
			break;
		case 1500:
			createModule(1000, 0);
			break;
		case 1600:
			if (_moduleResult == 1)
				createModule(1400, 0);
			else if (_moduleResult == 2)
				createModule(1700, 0);
			else
				createModule(2100, 0);
			break;
		case 1700:
			if (_moduleResult == 1)
				createModule(2900, 3);
			else
				createModule(1600, 2);
			break;
		case 1800:
			if (_moduleResult == 1) {
				// Game over, Klaymen jumped into the hole
				requestRestartGame(true);
			} else if (_moduleResult == 2)
				createModule(2700, 0);
			else if (_moduleResult == 3)
				createModule(3000, 3);
			else
				createModule(2800, 0);
			break;
		case 1900:
			createModule(3000, 1);
			break;
		case 2000:
			createModule(2900, 4);
			break;
		case 2100:
			if (_moduleResult == 1)
				createModule(2900, 1);
			else
				createModule(1600, 0);
			break;
		case 2200:
			createModule(2300, 1);
			break;
		case 2300:
		debug(1, "module 23000 _moduleResult : %d", _moduleResult);
			if (_moduleResult == 2)
				createModule(1200, 0);
			else if (_moduleResult == 0)
				createModule(1000, 1);
			else if (_vm->isDemo())
				createModule(9999, -1);
			else if (_moduleResult == 1)
				createModule(2200, 0);
			else if (_moduleResult == 3)
				createModule(2400, 0);
			else if (_moduleResult == 4)
				createModule(3000, 0);
			break;
		case 2400:
			createModule(2300, 3);
			break;
		case 2500:
			createModule(2600, 1);
			break;
		case 2600:
			if (_moduleResult == 1)
				createModule(2500, 0);
			else
				createModule(1200, 1);
			break;
		case 2700:
			createModule(1800, 2);
			break;
		case 2800:
			if (_moduleResult == 1)
				createModule(2900, 5);
			else
				createModule(1800, 0);
			break;
		case 2900:
			if (_moduleResult != (uint32)-1) {
				switch (_moduleResult) {
				case 0:
					createModule(1300, 5);
					break;
				case 1:
					createModule(2100, 1);
					break;
				case 2:
					createModule(1100, 1);
					break;
				case 3:
					setSubVar(V_TELEPORTER_DEST_AVAILABLE, 2, 1);
					createModule(1700, 1);
					break;
				case 4:
					createModule(2000, 0);
					break;
				case 5:
				default:
					createModule(2800, 1);
					break;
				}
			} else {
				switch (getGlobalVar(V_TELEPORTER_CURR_LOCATION)) {
				case 0:
					createModule(1300, 6);
					break;
				case 1:
					createModule(2100, 2);
					break;
				case 2:
					createModule(1100, 2);
					break;
				case 3:
					createModule(1700, 2);
					break;
				case 4:
					createModule(2000, 1);
					break;
				case 5:
				default:
					createModule(2800, 2);
					break;
				}
			}
			setGlobalVar(V_TELEPORTER_CURR_LOCATION, 0);
			break;
		case 3000:
			// NOTE _moduleResult 2 never used
			// NOTE Check if _moduleResult 4 is used
			if (_moduleResult == 1)
				createModule(1900, 0);
			else if (_moduleResult == 3)
				createModule(1800, 3);
			else if (_moduleResult == 4)
				createModule(3000, 0);
			else
				createModule(2300, 4);
			break;
		case 9999:
			createModuleByHash(getGlobalVar(V_MODULE_NAME));
			break;
		}
	}
}

void GameModule::openMainMenu() {
	if (_childObject) {
		sendMessage(_childObject, NM_MOUSE_HIDE, 0);
		_childObject->draw();
	} else {
		// If there's no module, create one so there's something to return to
		createModule(1000, 0);
	}
	_vm->_screen->saveParams();
	_vm->_screen->update();
	_mainMenuRequested = false;
	createMenuModule();
}

void GameModule::createMenuModule() {
	if (!_prevChildObject) {
		_prevChildObject = _childObject;
		_prevModuleNum = _moduleNum;
		_childObject = new MenuModule(_vm, this, 0);
		_childObject->handleUpdate();
		SetUpdateHandler(&GameModule::updateMenuModule);
	}
}

void GameModule::updateMenuModule() {
	if (!updateChild()) {
		_vm->_screen->restoreParams();
		_childObject = _prevChildObject;
		sendMessage(_childObject, NM_MOUSE_SHOW, 0);
		_prevChildObject = NULL;
		_moduleNum = _prevModuleNum;
		SetUpdateHandler(&GameModule::updateModule);
	}
}

NonRepeatingRandomNumbers::NonRepeatingRandomNumbers(Common::RandomSource *rnd, int count)
	: _rnd(rnd) {
	for (int i = 0; i < count; i++)
		push_back(i);
}

int NonRepeatingRandomNumbers::getNumber() {
	int number;
	if (!empty()) {
		uint index = _rnd->getRandomNumber(size() - 1);
		number = (*this)[index];
		remove_at(index);
	} else
		number = 0;
	return number;
}

void NonRepeatingRandomNumbers::removeNumber(int number) {
	for (uint i = 0; i < size(); ++i)
		if ((*this)[i] == number) {
			remove_at(i);
			break;
		}
}

} // End of namespace Neverhood