/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * Copyright 2020 Google
 *
 */
#include "hadesch/hadesch.h"
#include "hadesch/video.h"
#include "hadesch/ambient.h"

namespace Hadesch {

static const char *kBackground = "background";
static const char *kHotspots = "HallTrph.hot";
static const char *kArgo = "argo";

static const char *kZeusYooHoo = "zeus yoo-hoo";
static const char *kZeusYellsPhil = "zeus yells phil";
static const char *kZeusVacationIsOver = "zeus vacation's over";
static const char *kZeusFineChoiceHero = "zeus fine choice hero";
static const char *kZeusShaftOfLightLeft = "zeus shaft of light left";

static const char *kRope = "rope";
static const char *kHerculesLintel = "hercules lintel";

static const char *kApplicationHotspots = "applicat.hot";
static const char *kApplication = "application";
static const char *kApplicationHeroine = "application heroine";
static const char *kApplicationHeroineHotzone = "heroine";
static const char *kApplicationHero = "application hero";
static const char *kApplicationHeroHotzone = "hero";
static const char *kApplicationEnterButton = "application enter button";
static const char *kApplicationEnterHotzone = "enter";
static const char *kApplicationBurntHole = "application burnt hole";
static const char *kApplicationText = "application text";
static const char *kApplicationHeroineText = "application heroine text";
static const char *kApplicationHeroText = "application hero text";
static const char *kApplicationNameText = "application name text";
static const char *kApplicationUnfurls = "application unfurls";
static const char *kApplicationUnfurlsSound = "application unfurls sound";
static const char *kApplicationRollsUpHeroine = "application rolls up heroine";
static const char *kApplicationRollsUpHero = "application rolls up hero";
static const char *kApplicationChooseHeroSound = "application choose hero sound";
static const char *kApplicationChooseHeroineSound = "application choose heroine sound";

static const char *kHadesBurstsIn = "hades bursts in";
static const char *kHadesNoMoreHeroes = "hades no more heroes got it";
static const char *kHadesNoMoreHeroines = "hades no more heroines got it";

#define kPhilWalksCenterToLeft "phil walks center to left"
#define kPhilSighsLeft "phil sighs left"

#define kPhilSnores "phil snores"
static const char *kPhilSnoresSound = "phil snores sound";
#define kPhilRollsOver "phil rolls over"
#define kPhilJumpsOffPillow "phil jumps off pillow"
static const char *kPhilOfCourseIdo = "phil of course i do";
static const char *kPhilWhatAHothead = "phil what a hothead";
static const char *kPhilTimeToGetStarted = "phil time to get started";
static const char *kPhilTreasuresOfHercules = "phil treasures of hercules";
static const char *kPhilBasicInformation = "phil basic information";
static const char *kPhilFlakyPlaster = "phil flaky plaster";
static const char *kPhilGettinStarted = "phil gettin started";
static const char *kPhilToolsOfTrade = "phil tools of the trade";
static const char *kPhilHeroBelt = "phil hero belt";
static const char *kPhilHeroBeltBuckle = "phil left belt buckle";
static const char *kPhilClickAnItem = "phil click an item";
static const char *kPhilHeroPowers = "phil hero powers";
static const char *kPhilRightNotches = "phil right notches";
static const char *kPhilEyeOfFates = "phil eye of fates";
static const char *kPhilBadNews = "phil bad news";
static const char *kPhilFirstQuest = "phil first quest";
#define kPhilTakesScroll "phil takes scroll"
#define kPhilDropsScroll "phil drops scroll"
static const char *kPhilDaedalusNeedsHelp = "phil daedalus needs help";
static const char *kPhilOffToCrete = "phil off to crete";
static const char *kPhilArgo = "phil argo";
#define kPhilTapsFootLeft "phil taps foot left"

#define kPhilYapsWithoutSound "phil yaps without sound"

static const char *kMugshot = "mug shot";
static const char *kMugshotSound = "mug shot sound";
static const char *kPanicAndPainSting = "pain and panic sting";

#define kHeroStatue "hero statue"
#define kHeroineStatue "heroine statue"
static const char *kScroll = "scroll";
static const char *kScrollSound = "scroll sound";

static const char *kHeroBelt = "hero belt";
static const char *kHeroBeltUpSound = "hero belt up sound";
static const char *kHeroBeltDownSound = "hero belt down sound";

static const char *herculesRoomElements[] = {
	"mares",
	"cattle",
	"girdle",
	"horns",
	"hydra",
	"bull",
	"lion",
	"apples",
	"stables",
	"boar",
	"birds",
	"cerberus",
	"hercules",
};

static const char *allPhils[] = {
	"phil walks left to center",
	kPhilWalksCenterToLeft,
	"phil faces backward left",
	kPhilSighsLeft,
	kPhilTapsFootLeft,
	"phil dusts left",
	"phil dusts center",
	kPhilSnores,
	kPhilRollsOver,
	kPhilJumpsOffPillow,
	kPhilYapsWithoutSound,
	kPhilTakesScroll,
	kPhilDropsScroll,
	kPhilSighsLeft,
	"phil scans left"
};

static const char *powerLevelNames[] = {
	"thunderbolt",
	"trojan horse",
	"owl"
};

static const char *zeusComments[] = {
	"minotaur",
	"medusa",
	"trojan armor",
	"cyclops",
	kHeroStatue,
	kHeroineStatue
};

enum {
	kPhilJokeEvent = 19003,
	kPhilNewQuestScroll = 1019020,
	kPhilBecomesIdle = 1019025,
	kPhilJokeEventCleanup = 1019029,
	kPhilRereadDropScroll = 1019030
};

enum {
	kBackgroundZ = 10000,
	kQuestOverlayZ = 925,
	kMugshotZ = 900,
	kScrollZ = 900,
	kShaftOfLightLeftZ = 500,
	kShaftOfLightRightZ = 500,
	kTrophyZ = 450,
	kPhilZ = 400,
	kApplicationZ = 300,
	kApplicationButtonZ = 299,

	kSoundOnlyZ = 0,

	// TODO: figure proper values
	kHadesVideoZ = 0,
	kHeroBeltZ = 0,
	kGlowZ = 0
};

Common::String nameQuest() {
	Persistent *persistent = g_vm->getPersistent();
	switch(persistent->_quest) {
	case kNoQuest:
		return "";
	case kCreteQuest:
		return "crete quest";
	case kTroyQuest:
		return "troy quest";
	case kMedusaQuest:
		return "medusa quest";
	case kRescuePhilQuest:
		return "rescue phil quest";
	case kEndGame:
		return "finished game";
	// To silence warning
	case kNumQuests:
		return "";
	}

	return "";
}

Common::String getShaftOfLightRight() {
	Persistent *persistent = g_vm->getPersistent();
	if (persistent->_quest == kEndGame)
		return "zeus shaft of light right";
	return "zeus shaft of light " + nameQuest();
}

class WallOfFameHandler : public Handler {
public:
	WallOfFameHandler() {
		bool isNewGame = !g_vm->isRoomVisited(kWallOfFameRoom);
		Persistent *persistent = g_vm->getPersistent();
		if (isNewGame) {
			_gender = kUnknown;
			_philIsOnTheRight = false;
		} else {
			_gender = persistent->_gender;
			_philIsOnTheRight = true;
		}
		_isEnterMouseover = false;
		_philWalkPhase = 0;
		_philIsBusy = isNewGame;
		_endGameOutro = false;
		_applicationIsActive = false;
		_philSaidDoingGreat = false;
	}

	void handleClick(const Common::String &hotname) override {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		Persistent *persistent = g_vm->getPersistent();
		Quest quest = persistent->_quest;
		if (hotname == kApplicationHeroineHotzone) {
			room->selectFrame(kApplicationHeroine, kApplicationButtonZ, 1);
			room->selectFrame(kApplicationHero, kApplicationButtonZ, 0);
			room->playSFX(kApplicationChooseHeroineSound);
			_gender = kFemale;
			computeEnter();
			return;
		}
		if (hotname == kApplicationHeroHotzone) {
			room->selectFrame(kApplicationHeroine, kApplicationButtonZ, 0);
			room->selectFrame(kApplicationHero, kApplicationButtonZ, 1);
			room->playSFX(kApplicationChooseHeroSound);
			_gender = kMale;
			computeEnter();
			return;
		}
		if (hotname == kApplicationEnterHotzone) {
			pressEnter();
			return;
		}

		if (hotname == "argo") {
			g_vm->moveToRoom(kArgoRoom);
			return;
		}

		for (unsigned i = 0; i < sizeof (herculesRoomElements) / sizeof(herculesRoomElements[0]); i++)
			if (hotname == herculesRoomElements[i]) {
				room->disableMouse();
				room->playAnimKeepLastFrame(hotname + " glow", kGlowZ);
				room->playSFX("click");
				room->playVideo("zeus " + hotname, kSoundOnlyZ, 1019026);
				return;
			}

		for (unsigned power = 0; power < ARRAYSIZE(powerLevelNames); power++) {
			if (hotname.matchString(Common::String::format("%s#", powerLevelNames[power]))) {
				Common::String pl(powerLevelNames[power]);
				room->playSFX("click");
				if (strcmp(powerLevelNames[power], "thunderbolt") == 0)
					room->playSFX("thunder sound");
				zeusCommentRight("zeus " + pl);
				return;
			}
		}

		for (unsigned i = 0; i < sizeof (zeusComments) / sizeof(zeusComments[0]); i++)
			if (hotname == zeusComments[i]) {
				room->playSFX("click");
				zeusCommentRight(Common::String("zeus ") + hotname);
				return;
			}

		if (hotname == "typhon") {
			room->playSFX("click");
			zeusCommentRight(persistent->_gender == kFemale ? "zeus typhon heroine" : "zeus typhon hero");
			return;
		}

		if (hotname == "phil" && quest >= kCreteQuest && quest <= kMedusaQuest) {
			philBecomesListening();
			playPhilVideo(Common::String("phil reread ") + nameQuest(), kPhilRereadDropScroll,
				      Common::Point(22, 301));
			return;
		}

		if (hotname == "hades note") {
			g_vm->cancelTimer(19048);
			handleEvent(19048);
			return;
		}

		if (hotname == "hades note popup background" ||
		    hotname == "hades note popup background2") {
			room->popHotZones();
			room->setPannable(true);
			room->stopAnim("hades note popup");
			room->stopAnim("hades note text");
			room->stopAnim("hades note");
			if (persistent->_hintsAreEnabled) {
				zeusCommentRight("zeus counting on ya");
			}
			room->setHotzoneEnabled("argo", true);
			return;
		}

		if (hotname == "monster frieze") {
			g_vm->moveToRoom(kMonsterPuzzle);
			return;
		}

		if (hotname == "ferryman frieze") {
			g_vm->moveToRoom(kFerrymanPuzzle);
			return;
		}

		/*
		  TODO:
		  MNSH: medusa frieze
		  MNSH: trojan horse frieze
		  MNSH: labyrinth frieze
		*/
	}

	void handleMouseOver(const Common::String &hotname) override {
		Persistent *persistent = g_vm->getPersistent();

		if (hotname == kApplicationEnterHotzone) {
			_isEnterMouseover = true;
			computeEnter();
			return;
		}
		if (persistent->_quest <= kMedusaQuest
		    && (hotname == "labyrinth frieze"
			|| hotname == "trojan horse frieze")) {
			if (_philIsBusy)
				return;
			_philIsBusy = true;
			if (hotname == "labyrinth frieze")
				playPhilVideo("phil labyrinth frieze", kPhilBecomesIdle, Common::Point(40, 324)); // state 51
			else
				playPhilVideo("phil trojan horse frieze", kPhilBecomesIdle, Common::Point(14, 320)); // state 52
			return;
		}

		if (hotname.hasSuffix(" frieze")) {
			zeusCommentRight("zeus " + hotname, true);
			return;
		}

	}

	void handleMouseOut(const Common::String &hotname) override {
		if (hotname == kApplicationEnterHotzone) {
			_isEnterMouseover = false;
			computeEnter();
			return;
		}
	}

	void handleEvent(int eventId) override {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		Persistent *persistent = g_vm->getPersistent();
		Quest quest = persistent->_quest;
		switch (eventId) {
		case 19004:
			g_vm->addSkippableTimer(19005, 7000);
			room->playAnim(kZeusShaftOfLightLeft, kShaftOfLightLeftZ,
				       PlayAnimParams::keepLastFrame().speed(500));
			room->playVideo(kZeusYooHoo, kSoundOnlyZ, 19701); // zeus yoo-hoo
			break;
		case 19005:
			playPhilAnimSFX(kPhilRollsOver, 1019001, Common::Point(-26, 2)); // state 1
			break;
		case 19011:
			room->playVideo(kZeusVacationIsOver, kSoundOnlyZ, 19012);
			break;
		case 19012:
			playPhilVideo(kPhilOfCourseIdo, 1019002, Common::Point(30, 304)); // state 5
			break;
		case 1019002:
			displayPhilIdle();
			room->playVideo(kZeusFineChoiceHero, kSoundOnlyZ, 19013);
			break;
		case 19013:
			room->playAnim(kZeusShaftOfLightLeft, kShaftOfLightLeftZ, PlayAnimParams::disappear().backwards().speed(500));
			room->setLayerEnabled(kRope, false);
			playPhilVideo(kPhilBasicInformation, 1019003, Common::Point(-4, 2)); // state 6
			break;
		case 19016:
			room->addStaticLayer(kApplicationBurntHole, kApplicationButtonZ);
			room->playVideo(persistent->_gender == kFemale ? kHadesNoMoreHeroines : kHadesNoMoreHeroes,
					   kHadesVideoZ, 19018,
					   Common::Point(207, 103));
			break;
		case 19018:
			applicationClose();
			break;
		case 19019:
			playPhilVideo(kPhilWhatAHothead, 1019005, Common::Point(40, 327)); // state 7
			break;
		case 1019001:
			cancelAllPhils();
			playPhilAnimSFX(kPhilJumpsOffPillow, -1, Common::Point(-26, 2)); // state 4
			room->playVideo(kZeusYellsPhil, kSoundOnlyZ, 19011);
			break;
		case 1019003:
			room->setLayerEnabled(kRope, true);
			displayPhilIdle();
			room->playAnimWithSFX(
				kApplicationUnfurls, kApplicationUnfurlsSound,
				kApplicationZ, PlayAnimParams::disappear().speed(200), 1019004);
			break;
		case 1019004:
			room->enableMouse();
			startApplication();
			break;
		case 1019005:
			playPhilVideo(kPhilTimeToGetStarted, 1019006,
				      Common::Point(36, 319)); // state 8
			break;
		case 1019006:
			playPhilVideo(kPhilTreasuresOfHercules, 1019007,
				      Common::Point(40, 323)); // state 9
			break;
		case 1019007:
			displayPhilIdle();
			room->panRightAnim(1019008);
			break;
		case 1019008:
			_philIsOnTheRight = true;
			updatePhilHotzone();
			playPhilVideo(kPhilFlakyPlaster, 1019009,
				      Common::Point(0, 0)); //state 10
			break;
		case 1019009:
			playPhilVideo(kPhilGettinStarted, 1019010,
					Common::Point(14, 324)); //state 11
			break;
		case 1019010:
			room->disableHeroBelt();
			playPhilVideo(kPhilToolsOfTrade, 1019011,
				      Common::Point(40, 320)); //state 12
			break;
		case 1019011:
			displayPhilYap();
			room->playAnimWithSFX(
				kHeroBelt, kHeroBeltUpSound, kHeroBeltZ,
				PlayAnimParams::keepLastFrame().partial(0, 9),
				19022, kOffsetRightRoom);
			break;
		case 19022:
			room->playVideo(kPhilHeroBelt, kPhilZ, 1019012); // state 13
			break;
		case 1019012:
			room->playVideo(kPhilHeroBeltBuckle, kPhilZ, 1019013); // state 14
			break;
		case 1019013:
			room->selectFrame(kHeroBelt, kHeroBeltZ, 10,
					     kOffsetRightRoom);
			room->playSFX("hero belt items highlight sound");
			room->playVideo(kPhilClickAnItem, kPhilZ, 1019014); // state 15
			break;
		case 1019014:
			room->selectFrame(kHeroBelt, kHeroBeltZ, 11,
					     kOffsetRightRoom);
			room->playSFX("hero belt items highlight sound");
			room->playVideo(kPhilHeroPowers, kPhilZ, 1019015); // state 16
			break;
		case 1019015:
			room->selectFrame(kHeroBelt, kHeroBeltZ, 12,
					     kOffsetRightRoom);
			room->playSFX("hero belt items highlight sound");
			g_vm->addTimer(19029, 4000);
			room->playVideo(kPhilRightNotches, kPhilZ, 1019016); // state 17
			break;
		case 1019016:
			g_vm->cancelTimer(19029);
			room->playAnimWithSFX(
				kHeroBelt, kHeroBeltDownSound, kHeroBeltZ, PlayAnimParams::disappear().partial(9, 0),
				1019017, kOffsetRightRoom);
			break;
		case 1019017:
			room->enableHeroBelt();
			playPhilVideo(kPhilEyeOfFates, 1019018,
				      Common::Point(-8, 144)); // state 18
			break;
		case 1019018:
			room->playSFX(kPanicAndPainSting);
			playPhilVideo(kPhilBadNews, 1019019, Common::Point(14, 320)); // state 19
			break;
		case 1019019:
			displayPhilIdle();
			room->playAnimWithSFX(kMugshot, kMugshotSound,
					      kMugshotZ, PlayAnimParams::keepLastFrame(),
					      19006, kOffsetRightRoom);
			break;
		case 19006:
			room->playAnimWithSFX(kScroll, kScrollSound,
					      kScrollZ, PlayAnimParams::keepLastFrame(),
					      19009, kOffsetRightRoom);
			break;
		case 19009:
			playPhilVideo(kPhilFirstQuest, kPhilNewQuestScroll, Common::Point(40, 324)); // state 21
			break;
		case kPhilNewQuestScroll:
			room->selectFrame(kScroll, kScrollZ, 0, kOffsetRightRoom);
			room->playMusic("quest intro music");
			playPhilAnimSFX(kPhilTakesScroll, 1019021); // state 22
			break;
		case 1019021:
			switch (quest) {
			default:
			case kCreteQuest:
				playPhilVideo(kPhilDaedalusNeedsHelp, 1019022, Common::Point(28, 312)); // state 24
				break;
			case kTroyQuest:
				playPhilVideo("phil holy hera", 1019022, Common::Point(28, 312)); // state 29
				break;
			case kMedusaQuest:
				playPhilVideo("phil who writes these things", 1019022, Common::Point(28, 312)); // state 36
				break;
			}
			break;
		case 1019022:
			playPhilAnimSFX(kPhilDropsScroll, 1019023); // state 23
			break;
		case 1019023:
			switch (quest) {
			default:
			case kCreteQuest:
				playPhilVideo(kPhilOffToCrete, 1019024, Common::Point(40, 324)); // state 25
				break;
			case kTroyQuest:
				playPhilVideo("phil hourly rates", 1019033, Common::Point(28, 312)); // state 30
				break;
			case kMedusaQuest:
				playPhilVideo("phil off to seriphos", kPhilBecomesIdle, Common::Point(14, 320)); // state 37
			}
			break;
		case 1019024:
			playPhilVideo(kPhilArgo, kPhilBecomesIdle, Common::Point(40, 324)); // state 26
			break;
		case kPhilBecomesIdle:
			philBecomesIdle();
			break;
		case 1019026:
			room->enableMouse();
			for (unsigned i = 0; i < sizeof (herculesRoomElements) / sizeof(herculesRoomElements[0]); i++)
				room->stopAnim(Common::String(herculesRoomElements[i]) + " glow");
			break;
		case 1019027:
			if (!_philIsBusy)
				schedulePhilJoke();
			break;
		case 1019028:
			g_vm->cancelTimer(kPhilJokeEvent);
			break;
		case kPhilJokeEvent: {
			if (_philIsBusy) {
				schedulePhilJoke();
				break;
			}

			if (!_philSaidDoingGreat && quest != kCreteQuest) {
				_philSaidDoingGreat = true;
				// state 50
				playPhilVideo("phil doing great", kPhilJokeEventCleanup,
					      Common::Point(14, 320));
				break;
			}

			int rnd = g_vm->getRnd().getRandomNumberRng(1, 10);
			debug("rnd = %d", rnd);
			switch(rnd) {
			case 1:
				// state 40
				playPhilVideo("phil break time over", kPhilJokeEventCleanup,
					      Common::Point(40, 320));
				break;
			case 2:
				// state 41
				playPhilVideo("phil back to work", kPhilJokeEventCleanup,
					Common::Point(14, 320));
				break;
			case 3:
				// state 42
				playPhilVideo("phil lets go", kPhilJokeEventCleanup,
					Common::Point(16, 320));
				break;
			case 4:
				// state 43
				playPhilAnimWithoutSound("phil scans left", kPhilJokeEventCleanup,
							 Common::Point(-26, 2));
				break;
			case 5:
				// state 44, go right
				if (_philWalkPhase == 3) {
					schedulePhilJoke();
					break;
				}
				_philWalkPhase++;
				updatePhilHotzone();
				playPhilAnimSFX("phil walks left to center", kPhilJokeEventCleanup,
						Common::Point(-166, +2));
				break;
			case 6:
				// state 45, go left
				if (_philWalkPhase == 0) {
					schedulePhilJoke();
					break;
				}
				_philWalkPhase--;
				updatePhilHotzone();
				playPhilAnimSFX(kPhilWalksCenterToLeft, kPhilJokeEventCleanup);
				break;
			case 7:
				// state 46
				playPhilAnimSFX("phil faces backward left", kPhilJokeEventCleanup, Common::Point(-640, 0));
				break;
			case 8:
				// state 47
				playPhilAnimSFX(kPhilSighsLeft, kPhilJokeEventCleanup);
				break;
			case 9:
				// state 48
				playPhilAnimSFX(kPhilTapsFootLeft, kPhilJokeEventCleanup);
				break;
			case 10:
				// state 49, dusts
				switch (_philWalkPhase) {
				case 0:
					playPhilAnimSFX("phil dusts left", kPhilJokeEventCleanup);
					break;
				case 1:
				case 2:
					playPhilAnimSFX("phil dusts center", kPhilJokeEventCleanup, Common::Point(-166, 2));
					break;
				case 3:
					schedulePhilJoke();
					break;
				}
				break;
			}
			break;
		}
		case 19029:
			room->selectFrame(kHeroBelt, kHeroBeltZ, 13,
					  kOffsetRightRoom);
			room->playSFX("hero belt items highlight sound");
			break;
		case kPhilJokeEventCleanup:
			schedulePhilJoke();
			break;
		case 19045:
			philBecomesIdle();
			room->playAnim(
				getShaftOfLightRight(),
				kShaftOfLightRightZ,
				PlayAnimParams::disappear().speed(500));
			room->enableMouse();
			break;
		case kPhilRereadDropScroll:
			playPhilAnimSFX(kPhilDropsScroll, 19047);
			break;
		case 19047:
			philBecomesIdle();
			break;
		case 19048:
			room->disableMouse();
			room->disableHotzone("hades note");
			room->enableHotzone("hades note popup");
			room->disableHotzone("hades note popup background");
			room->enableHotzone("hades note popup background2");
			room->selectFrame("hades note popup", 150, 0, kOffsetRightRoom);
			room->selectFrame("hades note text", 149, 0, kOffsetRightRoom);
			room->playVideo("hades reads note", 0, 19049);
			break;
		case 19049:
			room->enableMouse();
			break;
		case 19051:
			_hercules = AmbientAnim("herc blinks", "", 0, 2000, 5000,
						AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
						AmbientAnim::PAN_ANY);
			_hercules.start();
			playPhilVideo("phil that was awesome", 19052,
				      Common::Point(14, 320)); // state 56
			break;
		case 19052:
			philBecomesListening();
			room->fadeOut(250, 19053);
			break;
		case 19053:
			_hercules.hide();
			room->resetFade();
			room->disableHeroBelt();
			room->resetLayers();
			room->disableMouse();
			room->setPannable(false);
			room->addStaticLayer("black background", kBackgroundZ, kOffsetRightRoom);
			room->playVideo("hades eye of fates", 0, 19054, kOffsetRightRoom);
			room->playSFX("herc laughs");
			room->playSFX("phil laughs");
			break;
		case 19054:
			g_vm->moveToRoom(kCreditsRoom);
			break;
		case 1019031:
			_philWalkPhase--;
			updatePhilHotzone();
			playPhilAnimSFX(kPhilWalksCenterToLeft, 1019032);
			break;
		case 1019032:
			playPhilVideo("phil lots more to do", kPhilNewQuestScroll, Common::Point(14, 312)); // state 28
			break;
		case 1019033:
			playPhilVideo(persistent->_gender == kFemale ? "phil go to it heroine" : "phil go to it hero",
				      1019034, Common::Point(40, 324)); // state 32/31
			break;
		case 1019034:
			playPhilVideo("phil off to troy", kPhilBecomesIdle, Common::Point(40, 324)); // state 33
			break;
		case 1019035:
			playPhilVideo("phil sneaky devil", 1019036, Common::Point(40, 324)); // state 34
			break;
		case 1019036:
			_philWalkPhase--;
			updatePhilHotzone();
			playPhilAnimSFX(kPhilWalksCenterToLeft, 1019037);
			break;
		case 1019037:
			playPhilVideo("phil good work", kPhilNewQuestScroll, Common::Point(40, 324)); // state 35
			break;
		case 19900:
			if (_gender != kUnknown && !_heroName.empty()) {
				break;
			}
			if (!_heroName.empty()) {
				room->playVideo("phil pick a statue", 0, 19901);
				break;
			}

			if (g_vm->getRnd().getRandomBit()) {
				room->playVideo("phil not a mind reader", 0, 19901);
			} else {
				room->playVideo("phil type in your name", 0, 19901);
			}
			break;
		case 19901:
			g_vm->addTimer(19900, 12000);
			break;
		}
	}

	void handleKeypress(uint32 code) override {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		if (_applicationIsActive && room->isMouseEnabled()) {
			if (_heroName.size() < 18 &&
			    (('a' <= code && code <= 'z')
			     || ('A' <= code && code <= 'Z')
			     || code == '.' || code == '-' || code == ' ')
			    && room->computeStringWidth("application sign-in text", _heroName + code) <= 318) {
				_heroName += code;
				room->playSFX("application click");
				renderNameInApplication();
				computeEnter();
				return;
			}

			if (_heroName.size() > 0 && code == '\b') {
				_heroName.deleteLastChar();
				room->playSFX("application click");
				renderNameInApplication();
				computeEnter();
				return;
			}

			if (_heroName.size() > 0 && (code == '\r' || code == '\n')) {
				pressEnter();
				return;
			}
		}
	}

	void prepareRoom() override {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		bool newGame = !g_vm->isRoomVisited(kWallOfFameRoom);
		Persistent *persistent = g_vm->getPersistent();
		room->loadHotZones(kHotspots, false);
		room->addStaticLayer(kBackground, kBackgroundZ);
		room->addStaticLayer(kHerculesLintel, 900);
		room->addStaticLayer(kRope, 900);
		Quest quest = persistent->_quest;
		if (quest != kEndGame) {
			room->addStaticLayer(kArgo, 950, kOffsetRightRoom);
			room->enableHotzone("argo");
			room->playAnimLoop("argo ambient", 949, kOffsetRightRoom);
		}
		room->playAnimLoop("water ambient", 975, kOffsetRightRoom);
		room->playAnimLoop("hercules hall ambients", 900);
		room->addStaticLayer(nameQuest() + " overlay", kQuestOverlayZ, kOffsetRightRoom);
		if (quest == kEndGame) {
			const char *heroStatue = persistent->_gender == kFemale ? kHeroineStatue : kHeroStatue;
			room->selectFrame(heroStatue, 450, 0, kOffsetRightRoom);
			room->enableHotzone(heroStatue);
			room->setHotZoneOffset(heroStatue, kOffsetRightRoom);
			room->enableHotzone("monster frieze");
			room->enableHotzone("ferryman frieze");
			room->enableHotzone("cyclops");
			room->enableHotzone("typhon");
		}

		if (quest > kMedusaQuest) {
			room->enableHotzone("medusa frieze");
			room->enableHotzone("medusa");
		}
		if (quest > kTroyQuest) {
			room->enableHotzone("trojan horse frieze");
			room->enableHotzone("trojan armor");
		}
		if (quest > kCreteQuest) {
			room->enableHotzone("labyrinth frieze");
			room->enableHotzone("minotaur");
		}

		if (quest < kRescuePhilQuest) {
			room->enableHotzone("phil");
			room->setHotZoneOffset("phil", kOffsetRightRoom);
		}

		// Hercules room
		for (unsigned i = 0; i < sizeof (herculesRoomElements) / sizeof(herculesRoomElements[0]); i++)
			room->enableHotzone(herculesRoomElements[i]);

		for (unsigned power = 0; power < sizeof(powerLevelNames) / sizeof(powerLevelNames[0]); power++) {
			int level = persistent->_powerLevel[power];
			if (level > 0) {
				int off = (4 - level) * 30;
				Common::String hz = Common::String::format("%s%d", powerLevelNames[power], level);
				room->playAnim(powerLevelNames[power], kTrophyZ,
					       PlayAnimParams::loop().partial(off, off + 29),
					       -1, kOffsetRightRoom);
				room->enableHotzone(hz);
				room->setHotZoneOffset(hz, kOffsetRightRoom);
			}
		}

		room->selectFrame(kScroll, kScrollZ, 0, kOffsetRightRoom);
		room->setPannable(true);
		if (newGame) {
			room->disableMouse();
			room->playAnimWithSFX(kPhilSnores, kPhilSnoresSound, kPhilZ, PlayAnimParams::loop()); //state 0
			room->selectFrame(kMugshot, kMugshotZ, 0, kOffsetRightRoom);
			g_vm->addSkippableTimer(19004, 4000);
		} else if (persistent->_doQuestIntro) {
			persistent->_doQuestIntro = false;
			room->disableMouse();
			switch (quest) {
			case kTroyQuest:
				_philWalkPhase = 1;
				playPhilVideo("phil congrats trapped minotaur", 1019031, Common::Point(14, 320)); // state 27
				room->playMusic("done crete quest theme");
				break;
			case kMedusaQuest:
				_philWalkPhase = 1;
				playPhilVideo("phil dances", 1019035, Common::Point(36, 257)); // state 38
				room->playMusic("done troy quest theme");
				break;
			case kRescuePhilQuest:
				g_vm->addTimer(19048, 5000, 1);
				room->pushHotZones("HTNote.HOT", true, kOffsetRightRoom);
				room->disableHotzone("hades note popup background2");
				room->setPannable(false);
				room->enableMouse();
				room->playAnimWithSFX("hades note", "hades note sound", 150, PlayAnimParams::loop(),
						      EventHandlerWrapper(), kOffsetRightRoom);
				room->setHotzoneEnabled("argo", false);
				room->playMusic("done medusa quest theme");
				_endGameOutro = true;
				break;

			case kNumQuests: // To silence warning
			case kEndGame:
			case kNoQuest:
				_philWalkPhase = 1;
				philBecomesListening();
				room->playVideo("herc congratulates", 0, 19051, kOffsetRightRoom);
				break;
			case kCreteQuest:
				break;
			}

			room->panRightInstant();
		} else {
			if (persistent->_quest < kRescuePhilQuest)
				schedulePhilJoke();
			room->panRightInstant();
		}

		renderLintel();

		room->playMusicLoop("C1010eA0");

		g_vm->getHeroBelt()->setColour(HeroBelt::kWarm);
	}

	~WallOfFameHandler() override {}

private:
	void clearNameInApplication() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		// One character more to handle possible backspace clicks.
		room->hideString("application sign-in text", _heroName.size() + 1);
	}

	void renderNameInApplication() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		clearNameInApplication();
		room->renderString("application sign-in text", _heroName, Common::Point(202, 338), 298);
	}

	void philBecomesIdle() {
		_philIsBusy = false;
		schedulePhilJoke();
	}

	void philBecomesBusy() {
		_philIsBusy = true;
		g_vm->cancelTimer(kPhilJokeEvent);
	}

	void philBecomesListening() {
		philBecomesBusy();
		displayPhilIdle();
	}

	void schedulePhilJoke() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		displayPhilIdle();
		room->enableMouse();
		g_vm->addTimer(kPhilJokeEvent, g_vm->getRnd().getRandomNumberRng(10000, 15000));
		room->setUserPanCallback(1019028, -1, -1, 1019027);
	}

	void pressEnter() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		Persistent *persistent = g_vm->getPersistent();

		// originally event 19015
		if (_gender != kUnknown /*&& name != ""*/) {
			persistent->_gender = _gender;
			persistent->_heroName = _heroName;
			room->disableMouse();
			// Or should it be music? Unclear to me
			room->playSFX("hades evil intro theme");
			g_vm->cancelTimer(19900);
			room->playVideo(kHadesBurstsIn, kHadesVideoZ, 19016);
		}
	}

	void zeusCommentRight(const Common::String &id, bool mouseIsEnabled = false) {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		if (!mouseIsEnabled)
			room->disableMouse();
		room->playVideo(id, kSoundOnlyZ, 19045);
		room->playAnim(
			getShaftOfLightRight(), kShaftOfLightRightZ, PlayAnimParams::keepLastFrame().speed(500), -1, kOffsetRightRoom);
		philBecomesListening();
	}

	Common::Point getPhilBase() const {
		Common::Point v;
		if (_philIsOnTheRight)
			v = Common::Point(640, 0);
		else
			v = Common::Point(26, -2);
		if (_philWalkPhase >= 0 && _philWalkPhase <= 3)
			v += Common::Point(166 * _philWalkPhase, -2 * _philWalkPhase);
		return v;
	}
	void playPhilVideo(const Common::String &name, int callback, const Common::Point videoOffset) {
		Persistent *persistent = g_vm->getPersistent();
		cancelAllPhils();
		if (persistent->_quest == kRescuePhilQuest)
			return;
		g_vm->getVideoRoom()->playVideo(name, kPhilZ, callback,
						videoOffset + getPhilBase());
	}

	void playPhilAnimSFX(const Common::String &name,
			     int callback, const Common::Point videoOffset = Common::Point(0, 0)) {
		Persistent *persistent = g_vm->getPersistent();
		cancelAllPhils();
		if (persistent->_quest == kRescuePhilQuest)
			return;
		g_vm->getVideoRoom()->playAnimWithSFX(
			name, name + " sound", kPhilZ, PlayAnimParams::keepLastFrame(), callback,
			videoOffset + getPhilBase());
	}

	void playPhilAnimWithoutSound(const Common::String &name,
				      int callback, const Common::Point videoOffset = Common::Point(0, 0)) {
		Persistent *persistent = g_vm->getPersistent();
		cancelAllPhils();
		if (persistent->_quest == kRescuePhilQuest)
			return;
		g_vm->getVideoRoom()->playAnimKeepLastFrame(
			name, kPhilZ, callback,
			videoOffset + getPhilBase());
	}

	void displayPhilIdle() {
		Persistent *persistent = g_vm->getPersistent();
		cancelAllPhils();
		if (persistent->_quest == kRescuePhilQuest
		    || (!_endGameOutro && persistent->_quest == kEndGame))
			return;
		g_vm->getVideoRoom()->selectFrame(kPhilSighsLeft, kPhilZ, 0,
						  getPhilBase());
	}

	void displayPhilYap() {
		cancelAllPhils();
		g_vm->getVideoRoom()->playAnimLoop(
			kPhilYapsWithoutSound, kPhilZ, Common::Point(640-166, 0));
	}

	void computeEnter() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		if (_gender == kUnknown /*|| name == ""*/) {
			room->selectFrame(kApplicationEnterButton, kApplicationButtonZ, 0);
			room->disableHotzone(kApplicationEnterHotzone);
			return;
		}
		room->selectFrame(kApplicationEnterButton, kApplicationButtonZ, _isEnterMouseover ? 2 : 1);
		room->enableHotzone(kApplicationEnterHotzone);
	}

	void startApplication() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		room->addStaticLayer(kApplication, kApplicationZ);
		room->selectFrame(kApplicationHeroine, kApplicationButtonZ, 0); // heroine
		room->selectFrame(kApplicationHero, kApplicationButtonZ, 0); // hero
		room->selectFrame(kApplicationEnterButton, kApplicationButtonZ, 0);
		room->addStaticLayer(kApplicationText, kApplicationButtonZ);
		room->addStaticLayer(kApplicationHeroineText, kApplicationButtonZ);
		room->addStaticLayer(kApplicationHeroText, kApplicationButtonZ);
		room->addStaticLayer(kApplicationNameText, kApplicationButtonZ);
		room->pushHotZones(kApplicationHotspots);
		room->disableHotzone(kApplicationEnterHotzone);
		_applicationIsActive = true;
		g_vm->addTimer(19900, 5000);
	}

	void applicationClose() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		Persistent *persistent = g_vm->getPersistent();
		room->setLayerEnabled(kApplication, false);
		room->setLayerEnabled(kApplicationHeroine, false);
		room->setLayerEnabled(kApplicationHero, false);
		room->setLayerEnabled(kApplicationEnterButton, false);
		room->setLayerEnabled(kApplicationText, false);
		room->setLayerEnabled(kApplicationHeroineText, false);
		room->setLayerEnabled(kApplicationHeroText, false);
		room->setLayerEnabled(kApplicationNameText, false);
		room->setLayerEnabled(kApplicationBurntHole, false);
		room->setLayerEnabled(kApplicationUnfurls, false);
		room->popHotZones();
		clearNameInApplication();
		_applicationIsActive = false;
		// TODO: does it have any sound?
		room->playAnim(persistent->_gender == kFemale ? kApplicationRollsUpHeroine : kApplicationRollsUpHero,
			       kApplicationZ, PlayAnimParams::disappear(), 19019);
		renderLintel();
	}

	void cancelAllPhils() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();

		for (uint i = 0; i < ARRAYSIZE(allPhils); i++)
			room->stopAnim(allPhils[i]);
	}

	void updatePhilHotzone() {
		g_vm->getVideoRoom()->setHotZoneOffset(
			"phil", getPhilBase());
	}

	void renderLintel() {
		Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
		Persistent *persistent = g_vm->getPersistent();
		room->renderStringCentered("player lintel text", persistent->_heroName, Common::Point(942, 19), 900, -1);
	}

	Gender _gender;
	bool _isEnterMouseover;
	bool _philIsOnTheRight;
	int _philWalkPhase;
	bool _philIsBusy;
	Common::U32String _heroName;
	AmbientAnim _hercules;
	bool _endGameOutro;
	bool _applicationIsActive;
	bool _philSaidDoingGreat;
};

Common::SharedPtr<Hadesch::Handler> makeWallOfFameHandler() {
	return Common::SharedPtr<Hadesch::Handler>(new WallOfFameHandler());
}

}
