/* 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/>.
 *
 */

#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/map/mapmgr.h"
#include "ultima/ultima4/map/shrine.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_string_controller.h"
#include "ultima/ultima4/controllers/wait_controller.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/events/event_handler.h"
#include "ultima/ultima4/filesys/u4file.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/game.h"
#include "ultima/ultima4/game/creature.h"
#include "ultima/ultima4/game/names.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/game/portal.h"
#include "ultima/ultima4/gfx/imagemgr.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/map/tileset.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/ultima4.h"

namespace Ultima {
namespace Ultima4 {

Shrines *g_shrines;

Shrines::Shrines() : _cycles(0), _completedCycles(0) {
	g_shrines = this;
}

Shrines::~Shrines() {
	g_shrines = nullptr;
}

void Shrines::loadAdvice() {
	_advice = u4read_stringtable("shrines");
}

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

bool shrineCanEnter(const Portal *p) {
	Shrine *shrine = dynamic_cast<Shrine *>(mapMgr->get(p->_destid));
	assert(shrine);

	if (!g_context->_party->canEnterShrine(shrine->getVirtue())) {
		g_screen->screenMessage("Thou dost not bear the rune of entry!  A strange force keeps you out!\n");
		return 0;
	}
	return 1;
}

bool isShrine(Map *punknown) {
	Shrine *ps;
	if ((ps = dynamic_cast<Shrine *>(punknown)) != nullptr)
		return true;
	else
		return false;
}

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

Shrine::Shrine() : _virtue(VIRT_HONESTY) {}

Common::String Shrine::getName() {
	if (_name.empty()) {
		_name = "Shrine of ";
		_name += getVirtueName(_virtue);
	}
	return _name;
}

Virtue Shrine::getVirtue() const {
	return _virtue;
}

Common::String Shrine::getMantra() const {
	return _mantra;
}

void Shrine::setVirtue(Virtue v) {
	_virtue = v;
}

void Shrine::setMantra(Common::String m) {
	_mantra = m;
}

void Shrine::enter() {
	if (!g_shrines->isAdviceLoaded())
		g_shrines->loadAdvice();

#ifdef IOS_ULTIMA4
	U4IOS::IOSHideGameControllerHelper hideControllsHelper;
#endif
	if (settings._enhancements && settings._enhancementsOptions._u5Shrines)
		enhancedSequence();
	else
		g_screen->screenMessage("You enter the ancient shrine and sit before the altar...");

	g_screen->screenMessage("\nUpon which virtue dost thou meditate?\n");
	Common::String virtue;
#ifdef IOS_ULTIMA4
	{
		U4IOS::IOSConversationHelper inputVirture;
		inputVirture.beginConversation(U4IOS::UIKeyboardTypeDefault, "Upon which virtue dost thou meditate?");
#endif
		virtue = ReadStringController::get(32, TEXT_AREA_X + g_context->_col, TEXT_AREA_Y + g_context->_line);
#ifdef IOS_ULTIMA4
	}
#endif

	int choice;
	g_screen->screenMessage("\n\nFor how many Cycles (0-3)? ");
#ifdef IOS_ULTIMA4
	{
		U4IOS::IOSConversationChoiceHelper cyclesChoice;
		cyclesChoice.updateChoices("0123 \015\033");
#endif
		choice = ReadChoiceController::get("0123\015\033");
#ifdef IOS_ULTIMA4
	}
#endif
	if (choice == '\033' || choice == '\015')
		g_shrines->_cycles = 0;
	else
		g_shrines->_cycles = choice - '0';
	g_shrines->_completedCycles = 0;

	g_screen->screenMessage("\n\n");

	// ensure the player chose the right virtue and entered a valid number for cycles
	if (scumm_strnicmp(virtue.c_str(), getVirtueName(getVirtue()), 6) != 0 || g_shrines->_cycles == 0) {
		g_screen->screenMessage("Thou art unable to focus thy thoughts on this subject!\n");
		eject();
		return;
	}

	if (((g_ultima->_saveGame->_moves / SHRINE_MEDITATION_INTERVAL) >= 0x10000) ||
	        (((g_ultima->_saveGame->_moves / SHRINE_MEDITATION_INTERVAL) & 0xffff) != g_ultima->_saveGame->_lastMeditation)) {
		g_screen->screenMessage("Begin Meditation\n");
		meditationCycle();
	} else {
		g_screen->screenMessage("Thy mind is still weary from thy last Meditation!\n");
		eject();
	}
}

void Shrine::enhancedSequence() {
	// Replace the 'static' avatar tile with grass
	_annotations->add(Coords(5, 6, g_context->_location->_coords.z), _tileSet->getByName("grass")->getId(), false, true);

	g_screen->screenDisableCursor();
	g_screen->screenMessage("You approach\nthe ancient\nshrine...\n");
	gameUpdateScreen();
	EventHandler::wait_cycles(settings._gameCyclesPerSecond);

	Object *obj = addCreature(creatureMgr->getById(BEGGAR_ID), Coords(5, 10, g_context->_location->_coords.z));
	obj->setTile(_tileSet->getByName("avatar")->getId());

	gameUpdateScreen();
	EventHandler::wait_msecs(400);
	g_context->_location->_map->move(obj, DIR_NORTH);
	gameUpdateScreen();
	EventHandler::wait_msecs(400);
	g_context->_location->_map->move(obj, DIR_NORTH);
	gameUpdateScreen();
	EventHandler::wait_msecs(400);
	g_context->_location->_map->move(obj, DIR_NORTH);
	gameUpdateScreen();
	EventHandler::wait_msecs(400);
	g_context->_location->_map->move(obj, DIR_NORTH);
	gameUpdateScreen();
	EventHandler::wait_msecs(800);
	obj->setTile(creatureMgr->getById(BEGGAR_ID)->getTile());
	gameUpdateScreen();

	g_screen->screenMessage("\n...and kneel before the altar.\n");
	EventHandler::wait_cycles(settings._gameCyclesPerSecond);
	g_screen->screenEnableCursor();
}

void Shrine::meditationCycle() {
	// Find our interval for meditation
	int interval = (settings._shrineTime * 1000) / MEDITATION_MANTRAS_PER_CYCLE;
	interval -= (interval % settings._eventTimerGranularity);
	interval /= settings._eventTimerGranularity;
	if (interval <= 0)
		interval = 1;

	g_ultima->_saveGame->_lastMeditation = (g_ultima->_saveGame->_moves / SHRINE_MEDITATION_INTERVAL) & 0xffff;

	g_screen->screenDisableCursor();
	for (int i = 0; i < MEDITATION_MANTRAS_PER_CYCLE; i++) {
		WaitController controller(interval);
		eventHandler->pushController(&controller);
		controller.wait();
		g_screen->screenMessage(".");
		g_screen->update();
	}
	askMantra();
}

void Shrine::askMantra() {
	g_screen->screenEnableCursor();
	g_screen->screenMessage("\nMantra: ");
	g_screen->update();       // FIXME: needed?
	Common::String mantra;
#ifdef IOS_ULTIMA4
	{
		U4IOS::IOSConversationHelper mantraHelper;
		mantraHelper.beginConversation(U4IOS::UIKeyboardTypeASCIICapable, "Mantra?");
#endif
		mantra = ReadStringController::get(4, TEXT_AREA_X + g_context->_col, TEXT_AREA_Y + g_context->_line);
		g_screen->screenMessage("\n");
#ifdef IOS_ULTIMA4
	}
#endif

	if (scumm_stricmp(mantra.c_str(), getMantra().c_str()) != 0) {
		g_context->_party->adjustKarma(KA_BAD_MANTRA);
		g_screen->screenMessage("Thou art not able to focus thy thoughts with that Mantra!\n");
		eject();
	} else if (--g_shrines->_cycles > 0) {
		g_shrines->_completedCycles++;
		g_context->_party->adjustKarma(KA_MEDITATION);
		meditationCycle();
	} else {
		g_shrines->_completedCycles++;
		g_context->_party->adjustKarma(KA_MEDITATION);

		bool elevated = g_shrines->_completedCycles == 3 && g_context->_party->attemptElevation(getVirtue());
		if (elevated)
			g_screen->screenMessage("\nThou hast achieved partial Avatarhood in the Virtue of %s\n\n",
			              getVirtueName(getVirtue()));
		else
			g_screen->screenMessage("\nThy thoughts are pure. "
			              "Thou art granted a vision!\n");

#ifdef IOS_ULTIMA4
		U4IOS::IOSConversationChoiceHelper choiceDialog;
		choiceDialog.updateChoices(" ");
		U4IOS::testFlightPassCheckPoint(Common::String("Gained avatarhood in: ")
		                                + getVirtueName(getVirtue()));
#endif
		ReadChoiceController::get("");
		showVision(elevated);
		ReadChoiceController::get("");
		gameSetViewMode(VIEW_NORMAL);
		eject();
	}
}

void Shrine::showVision(bool elevated) {
	static const char *visionImageNames[] = {
		BKGD_SHRINE_HON, BKGD_SHRINE_COM, BKGD_SHRINE_VAL, BKGD_SHRINE_JUS,
		BKGD_SHRINE_SAC, BKGD_SHRINE_HNR, BKGD_SHRINE_SPI, BKGD_SHRINE_HUM
	};

	if (elevated) {
		g_screen->screenMessage("Thou art granted a vision!\n");
		gameSetViewMode(VIEW_RUNE);
		g_screen->screenDrawImageInMapArea(visionImageNames[getVirtue()]);
	} else {
		g_screen->screenMessage("\n%s", g_shrines->_advice[
			getVirtue() * 3 + g_shrines->_completedCycles - 1].c_str());
	}
}

void Shrine::eject() {
	g_game->exitToParentMap();
	g_music->playMapMusic();
	g_context->_location->_turnCompleter->finishTurn();
}

} // End of namespace Ultima4
} // End of namespace Ultima
