/* 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/scummsys.h"
#include "common/config-manager.h"
#include "mads/mads.h"
#include "mads/compression.h"
#include "mads/resources.h"
#include "mads/scene.h"
#include "mads/nebular/game_nebular.h"
#include "mads/nebular/nebular_scenes.h"
#include "mads/nebular/nebular_scenes1.h"
#include "mads/nebular/nebular_scenes2.h"
#include "mads/nebular/nebular_scenes3.h"
#include "mads/nebular/nebular_scenes4.h"
#include "mads/nebular/nebular_scenes5.h"
#include "mads/nebular/nebular_scenes6.h"
#include "mads/nebular/nebular_scenes7.h"
#include "mads/nebular/nebular_scenes8.h"

namespace MADS {

namespace Nebular {

SceneLogic *SceneFactory::createScene(MADSEngine *vm) {
	Scene &scene = vm->_game->_scene;

	scene.addActiveVocab(NOUN_DROP);
	scene.addActiveVocab(NOUN_DOLLOP);
	scene.addActiveVocab(NOUN_DASH);
	scene.addActiveVocab(NOUN_SPLASH);
	scene.addActiveVocab(NOUN_ALCOHOL);

	switch (scene._nextSceneId) {
	// Scene group #1 (ship, ocean, cave)
	case 101:	// Ship, cockpit
		return new Scene101(vm);
	case 102:	// Ship, dining room
		return new Scene102(vm);
	case 103:	// Ship, engine room
		return new Scene103(vm);
	case 104:	// Ocean, northwest cliff
		return new Scene104(vm);
	case 105:	// Ocean, northeast cliff with mine
		return new Scene105(vm);
	case 106:	// Ocean, outside ship
		return new Scene106(vm);
	case 107:	// Ocean, bushes
		return new Scene107(vm);
	case 108:	// Ocean, southwest cliff
		return new Scene108(vm);
	case 109:	// Ocean, tunnel
		return new Scene109(vm);
	case 110:	// Ocean, cave with tunnel
		return new Scene110(vm);
	case 111:	// Cave with pool and opening
		return new Scene111(vm);
	case 112:	// cutscene, looking at view screen
		return new Scene112(vm);

	// Scene group #2 (island)
	case 201:	// outside teleporter
		return new Scene201(vm);
	case 202:	// village
		return new Scene202(vm);
	case 203:	// tree with Rhotunda (fat woman)
		return new Scene203(vm);
	case 205:	// village
		return new Scene205(vm);
	case 207:	// outside witch doctor's hut
		return new Scene207(vm);
	case 208:	// pit with leaves (trap)
		return new Scene208(vm);
	case 209:	// palm tree and bamboo plant
		return new Scene209(vm);
	case 210:	// outside native woman's hut
		return new Scene210(vm);
	case 211:	// palm tree with monkey
		return new Scene211(vm);
	case 212:	// outside cave
		return new Scene212(vm);
	case 213:	// inside teleporter
		return new Scene213(vm);
	case 214:	// inside witch doctor's hut
		return new Scene214(vm);
	case 215:	// inside native woman's hut
		return new Scene215(vm);
	case 216:	// cutscene, monitor showing Rex and native woman
		return new Scene216(vm);

	// Scene group #3 (women's base, cell block)
	case 301:	// outside teleporter (before chaos)
		return new Scene301(vm);
	case 302:	// room with statue (before chaos)
		return new Scene302(vm);
	case 303:	// western corridor (before chaos)
		return new Scene303(vm);
	case 304:	// crossing with traffic light (before chaos)
		return new Scene304(vm);
	case 307:	// Rex's cell (before chaos)
		return new Scene307(vm);
	case 308:	// sauropod's cell (before chaos)
		return new Scene308(vm);
	case 309:	// multihand monster's cell (before chaos)
		return new Scene309(vm);
	case 310:	// empty cell (before chaos)
		return new Scene310(vm);
	case 311:	// warden's desk (before chaos)
		return new Scene311(vm);
	case 313:	// air shaft overview
		return new Scene313(vm);
	case 316:	// Gender Bender
		return new Scene316(vm);
	case 318:	// doctor's gurney
		return new Scene318(vm);
	case 319:	// doctor Slache closeup (lying on the gurney)
		return new Scene319(vm);
	case 320:	// warden's desk closeup / monitors
		return new Scene320(vm);
	case 321:	// gender bender sex change sequence
		return new Scene321(vm);
	case 322:	// inside teleporter
		return new Scene322(vm);
	case 351:	// outside teleporter (after chaos)
		return new Scene351(vm);
	case 352:	// room with statue (after chaos)
		return new Scene352(vm);
	case 353:	// western corridor (after chaos)
		return new Scene353(vm);
	case 354:	// crossing with traffic light (after chaos)
		return new Scene354(vm);
	case 357:	// Rex's cell (after chaos)
		return new Scene357(vm);
	case 358:	// sauropod's cell (after chaos)
		return new Scene358(vm);
	case 359:	// multihand monster's cell (after chaos)
		return new Scene359(vm);
	case 360:	// empty cell (after chaos)
		return new Scene360(vm);
	case 361:	// warden's desk (after chaos)
		return new Scene361(vm);
	case 366:	// air shaft ending at Gender Bender
		return new Scene366(vm);
	case 387:	// air shaft ending at cell
		return new Scene387(vm);
	case 388:	// air shaft ending at sauropod's cell
		return new Scene388(vm);
	case 389:	// air shaft ending at multihand monster's cell (before chaos)
		return new Scene389(vm);
	case 390:	// air shaft ending at cell
		return new Scene390(vm);
	case 391:	// air shaft ending at warden's desk
		return new Scene391(vm);
	case 399:	// air shaft ending at multihand monster's cell (after chaos)
		return new Scene399(vm);

	// Scene group #4 (women's base)
	case 401:	// outside bar
		return new Scene401(vm);
	case 402:	// inside bar
		return new Scene402(vm);
	case 405:	// outside armory
		return new Scene405(vm);
	case 406:	// outside storage room
		return new Scene406(vm);
	case 407:	// eastern corridor
		return new Scene407(vm);
	case 408:	// inside armory
		return new Scene408(vm);
	case 409:	// inside female only teleporter
		return new Scene409(vm);
	case 410:	// inside storage room
		return new Scene410(vm);
	case 411:	// lab
		return new Scene411(vm);
	case 413:	// outside female only teleporter
		return new Scene413(vm);

	// Scene group #5 (men's city, lower floor)
	case 501:	// outside car
		return new Scene501(vm);
	case 502:	// inside male only teleporter
		return new Scene502(vm);
	case 503:	// guard tower
		return new Scene503(vm);
	case 504:	// inside car
		return new Scene504(vm);
	case 505:	// car view screen
		return new Scene505(vm);
	case 506:	// shopping street
		return new Scene506(vm);
	case 507:	// inside software house
		return new Scene507(vm);
	case 508:	// laser cannon
		return new Scene508(vm);
	case 511:	// outside pleasure dome
		return new Scene511(vm);
	case 512:	// inside pleasure dome
		return new Scene512(vm);
	case 513:	// outside mall
		return new Scene513(vm);
	case 515:	// overview
		return new Scene515(vm);
	case 551:	// outside teleporter (with skeleton)
		return new Scene551(vm);

	// Scene group #6 (men's city, upper floor)
	case 601:	// outside Bruce's house
		return new Scene601(vm);
	case 602:	// Bruce's house, living room
		return new Scene602(vm);
	case 603:	// Bruce's house, bedroom
		return new Scene603(vm);
	case 604:	// viewport
		return new Scene604(vm);
	case 605:	// viewport closeup
		return new Scene605(vm);
	case 607:	// outside Abdul's garage
		return new Scene607(vm);
	case 608:	// inside Abdul's garage
		return new Scene608(vm);
	case 609:	// outside Buckluster video store
		return new Scene609(vm);
	case 610:	// inside Buckluster video store
		return new Scene610(vm);
	case 611:	// back alley
		return new Scene611(vm);
	case 612:	// expressway / maintenance building
		return new Scene612(vm);
	case 620:	// cutscene, viewport glass breaking
		return new Scene620(vm);

	// Scene group #7 (submerged men's city / upper floor)
	case 701:	// outside elevator (after city is submerged)
		return new Scene701(vm);
	case 702:	// outside teleporter (after city is submerged)
		return new Scene702(vm);
	case 703:	// water
		return new Scene703(vm);
	case 704:	// water, building in the distance
		return new Scene704(vm);
	case 705:	// water, outside building
		return new Scene705(vm);
	case 706:	// inside building, pedestral room, outside teleporter
		return new Scene706(vm);
	case 707:	// teleporter
		return new Scene707(vm);
	case 710:	// looking at pedestral room through binoculars
		return new Scene710(vm);
	case 711:	// inside teleporter
		return new Scene711(vm);
	case 751:	// outside elevator (before city is submerged)
		return new Scene751(vm);
	case 752:	// outside teleporter (before city is submerged)
		return new Scene752(vm);

	// Scene group #8
	case 801:	// control room, outside teleporter
		return new Scene801(vm);
	case 802:	// launch pad with destroyed ship
		return new Scene802(vm);
	case 803:	// empty launch pad
		return new Scene803(vm);
	case 804:	// inside Rex's ship - cockpit
		return new Scene804(vm);
	case 805:	// service panel
		return new Scene805(vm);
	case 807:	// teleporter
		return new Scene807(vm);
	case 808:	// antigrav control
		return new Scene808(vm);
	case 810:	// cutscene: Rex's ship leaving the planet
		return new Scene810(vm);

	default:
		error("Invalid scene %d called", scene._nextSceneId);
	}
}

/*------------------------------------------------------------------------*/

NebularScene::NebularScene(MADSEngine *vm) : SceneLogic(vm),
		_globals(static_cast<GameNebular *>(vm->_game)->_globals),
		_game(*static_cast<GameNebular *>(vm->_game)),
		_action(vm->_game->_scene._action) {
}

Common::String NebularScene::formAnimName(char sepChar, int suffixNum) {
	return Resources::formatName(_scene->_currentSceneId, sepChar, suffixNum,
		EXT_NONE, "");
}

/*------------------------------------------------------------------------*/

void SceneInfoNebular::loadCodes(MSurface &depthSurface, int variant) {
	File f(Resources::formatName(RESPREFIX_RM, _sceneId, ".DAT"));
	MadsPack codesPack(&f);
	Common::SeekableReadStream *stream = codesPack.getItemStream(variant + 1);

	loadCodes(depthSurface, stream);

	delete stream;
	f.close();
}

void SceneInfoNebular::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) {
	byte *destP = depthSurface.getData();
	byte *endP = depthSurface.getBasePtr(0, depthSurface.h);

	byte runLength = stream->readByte();
	while (destP < endP && runLength > 0) {
		byte runValue = stream->readByte();

		// Write out the run length
		Common::fill(destP, MIN(endP, destP + runLength), runValue);
		destP += runLength;

		// Get the next run length
		runLength = stream->readByte();
	}

	if (destP < endP)
		Common::fill(destP, endP, 0);
}

/*------------------------------------------------------------------------*/

SceneTeleporter::SceneTeleporter(MADSEngine *vm) : NebularScene(vm) {
	_buttonTyped = -1;
	_curCode = -1;
	_digitCount = -1;
	_curMessageId = -1;
	_handSpriteId = -1;
	_handSequenceId = -1;
	_finishedCodeCounter = -1;
	_meteorologistNextPlace = -1;
	_meteorologistCurPlace = -1;
	_teleporterSceneId = -1;
}

int SceneTeleporter::teleporterAddress(int code, bool working) {
	int limit = working ? 6 : 10;

	for (int i = 0; i < limit; i++) {
		if (code == _globals[kTeleporterCode + i])
			return _globals[kTeleporterRoom + i];
	}

	return -1;
}

Common::Point SceneTeleporter::teleporterComputeLocation() {
	Common::Point result;

	switch (_buttonTyped) {
	case 0:
		result = Common::Point(179, 200);
		break;

	case 1:
		result = Common::Point(166, 170);
		break;

	case 2:
		result = Common::Point(179, 170);
		break;

	case 3:
		result = Common::Point(192, 170);
		break;

	case 4:
		result = Common::Point(166, 180);
		break;

	case 5:
		result = Common::Point(179, 180);
		break;

	case 6:
		result = Common::Point(192, 180);
		break;

	case 7:
		result = Common::Point(166, 190);
		break;

	case 8:
		result = Common::Point(179, 190);
		break;

	case 9:
		result = Common::Point(192, 190);
		break;

	case 10:
		result = Common::Point(194, 200);
		break;

	case 11:
		result = Common::Point(164, 200);
		break;

	default:
		error("teleporterComputeLocation() - Unexpected button pressed");
	}

	return result;
}

void SceneTeleporter::teleporterHandleKey() {
	switch (_game._trigger) {
	case 0: {
		_game._player._stepEnabled = false;
		Common::Point msgPos = teleporterComputeLocation();
		_handSequenceId = _scene->_sequences.startPingPongCycle(_handSpriteId, false, 4, 2, 0, 0);
		_scene->_sequences.setPosition(_handSequenceId, msgPos);
		_scene->_sequences.setDepth(_handSequenceId, 2);
		_scene->_sequences.addSubEntry(_handSequenceId, SEQUENCE_TRIGGER_LOOP, 0, 1);
		_scene->_sequences.addSubEntry(_handSequenceId, SEQUENCE_TRIGGER_EXPIRE, 0, 2);

		if (_globals[kMeteorologistWatch] == METEOROLOGIST_NORMAL)
			_vm->_events->hideCursor();

		}
		break;

	case 1:
		_scene->_sequences.addSubEntry(_handSequenceId, SEQUENCE_TRIGGER_SPRITE, 3, 3);
		if (_buttonTyped <= 9) {
			if (_digitCount < 4) {
				_curCode *= 10;
				_curCode += _buttonTyped;
				_digitCount++;

				Common::String format = "%01d";
				format.setChar('0' + _digitCount, 2);
				_msgText = Common::String::format(format.c_str(), _curCode);
				if (_digitCount < 4)
					_msgText += "_";

				if (_scene->_currentSceneId != 711)
					_vm->_sound->command(32);
			}
		} else if (_buttonTyped == 11) {
			_digitCount = 0;
			_curCode = 0;
			_msgText = "_";
			if (_scene->_currentSceneId != 711)
				_vm->_sound->command(33);
		} else if (_digitCount == 4) {
			if (_scene->_currentSceneId != 711)
				_finishedCodeCounter = 1;

			if (teleporterAddress(_curCode, true) > 0) {
				_vm->_palette->setEntry(252, 0, 63, 0);
				if (_scene->_currentSceneId != 711)
					_vm->_sound->command(34);
			} else {
				_vm->_palette->setEntry(252, 63, 0, 0);
				if (_scene->_currentSceneId != 711)
					_vm->_sound->command(35);
			}
		}

		if (_scene->_currentSceneId != 711) {
			if (_curMessageId >= 0)
				_scene->_kernelMessages.remove(_curMessageId);
			_curMessageId = _scene->_kernelMessages.add(Common::Point(143, 61), 0xFDFC, 16, 0, INDEFINITE_TIMEOUT, _msgText);
		}
		break;

	case 2:
		if (_finishedCodeCounter == 1) {
			_finishedCodeCounter++;

			if (_globals[kMeteorologistWatch] != METEOROLOGIST_NORMAL)
				_scene->_nextSceneId = 202;
			else {
				_vm->_events->showCursor();
				int destination = teleporterAddress(_curCode, true);

				if (destination > 0) {
					_globals[kTeleporterCommand] = 2;
					_scene->_nextSceneId = _teleporterSceneId;
					_globals[kTeleporterDestination] = destination;
				} else {
					_globals[kTeleporterCommand] = 4;
					_scene->_nextSceneId = _teleporterSceneId;
				}
			}
		} else if (_globals[kMeteorologistWatch] != METEOROLOGIST_NORMAL)
			_scene->_sequences.addTimer(30, 230 + _meteorologistCurPlace);

		break;

	case 3:
		if (!_finishedCodeCounter) {
			if (_globals[kMeteorologistWatch] == METEOROLOGIST_NORMAL) {
				_game._player._stepEnabled = true;
				_vm->_events->showCursor();
			}
		}
		break;

	default:
		break;
	}
}

void SceneTeleporter::teleporterEnter() {
	_game._player._visible   = false;
	_game._player._stepEnabled = (_globals[kMeteorologistWatch] == METEOROLOGIST_NORMAL);
	_scene->_kernelMessages._talkFont = _vm->_font->getFont(FONT_TELE);
	_scene->_textSpacing = 0;
	_curCode   = 0;
	_digitCount = 0;
	_finishedCodeCounter       = 0;
	_curMessageId        = -1;
	_msgText = "_";

	if (_scene->_priorSceneId == RETURNING_FROM_DIALOG)
		_scene->_priorSceneId = _globals[kTeleporterDestination];

	if (_scene->_priorSceneId < 101)
		_scene->_priorSceneId = 201;

	_globals[kTeleporterDestination] = _scene->_priorSceneId;
	_vm->_palette->setEntry(252, 63, 63, 0);
	_vm->_palette->setEntry(253, 0,  0, 0);
	_teleporterSceneId = _scene->_priorSceneId;
	if (_teleporterSceneId == 202)
		_teleporterSceneId = 201;

	int codeVal = 0;
	for (int i = 0; i < 10; i++) {
		if (_teleporterSceneId == _globals[kTeleporterRoom + i])
			codeVal = _globals[kTeleporterCode + i];

		if (_globals[kTeleporterRoom + i] == 301)
			_meteorologistNextPlace = _globals[kTeleporterCode + i];
	}

	Common::String msgText2 = Common::String::format("#%.4d", codeVal);

	if (_scene->_currentSceneId != 711) {
		_scene->_kernelMessages.add(Common::Point(133, 34), 0, 32, 0, INDEFINITE_TIMEOUT, msgText2);
		_scene->_kernelMessages.add(Common::Point(143, 61), 0xFDFC, 16, 0, INDEFINITE_TIMEOUT, _msgText);
	}

	_meteorologistCurPlace = 0;

	if (_globals[kMeteorologistWatch] != METEOROLOGIST_NORMAL)
		_scene->_sequences.addTimer(30, 230);

	_vm->_sound->command(36);
}

bool SceneTeleporter::teleporterActions() {
	bool retVal = false;

	if (_action.isAction(VERB_PRESS) || _action.isAction(VERB_PUSH)) {
		static int _buttonList[12] = { NOUN_0_KEY, NOUN_1_KEY, NOUN_2_KEY, NOUN_3_KEY, NOUN_4_KEY, NOUN_5_KEY, NOUN_6_KEY, NOUN_7_KEY, NOUN_8_KEY, NOUN_9_KEY, NOUN_SMILE_KEY, NOUN_FROWN_KEY };
		for (int i = 0; i < 12; i++) {
			if (_action._activeAction._objectNameId == _buttonList[i])
				_buttonTyped = i;
		}
		teleporterHandleKey();
		retVal = true;
	}

	if (_action.isAction(VERB_EXIT_FROM, NOUN_DEVICE)) {
		_globals[kTeleporterCommand] = 3;
		_scene->_nextSceneId = _teleporterSceneId;
		retVal = true;
	}

	return (retVal);
}

void SceneTeleporter::teleporterStep() {
	if (_globals[kMeteorologistWatch] == METEOROLOGIST_NORMAL)
		return;

	if (_game._trigger >= 230) {
		int place = _game._trigger - 230;
		int digit;

		if (place < 4) {
			digit = _meteorologistNextPlace;
			for (int i = 0; i < (3 - place); i++)
				digit = digit / 10;

			digit   = digit % 10;
		} else {
			digit   = 10;
		}
		_buttonTyped = digit;
		_meteorologistCurPlace = place + 1;
		_game._trigger = -1;
	}

	if (_game._trigger) {
		if (_game._trigger == -1)
			_game._trigger = 0;
		teleporterHandleKey();
	}
}

} // End of namespace Nebular

} // End of namespace MADS