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

//
// Quit game procedure
//

#include "ags/shared/core/platform.h"
#include "ags/engine/ac/cd_audio.h"
#include "ags/engine/ac/game.h"
#include "ags/engine/ac/game_setup.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/room_status.h"
#include "ags/engine/ac/route_finder.h"
#include "ags/engine/ac/translation.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/debugging/ags_editor_debugger.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/debugging/debugger.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/font/fonts.h"
#include "ags/engine/main/config.h"
#include "ags/engine/main/engine.h"
#include "ags/engine/main/main.h"
#include "ags/engine/main/quit.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/engine/gfx/graphics_driver.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/core/asset_manager.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/engine/platform/base/sys_main.h"
#include "ags/plugins/plugin_engine.h"
#include "ags/shared/script/cc_common.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/globals.h"
#include "ags/ags.h"

namespace AGS3 {

using namespace AGS::Shared;
using namespace AGS::Engine;

void quit_tell_editor_debugger(const String &qmsg, QuitReason qreason) {
	if (_G(editor_debugging_initialized)) {
		if (qreason & kQuitKind_GameException)
			_G(handledErrorInEditor) = send_exception_to_debugger(qmsg.GetCStr());
		send_state_to_debugger("EXIT");
		_G(editor_debugger)->Shutdown();
	}
}

void quit_stop_cd() {
	if (_G(need_to_stop_cd))
		cd_manager(3, 0);
}

void quit_check_dynamic_sprites(QuitReason qreason) {
	if ((qreason & kQuitKind_NormalExit) && _G(check_dynamic_sprites_at_exit) && (_GP(game).options[OPT_DEBUGMODE] != 0)) {
		// Check that the dynamic sprites have been deleted;
		// ignore those that are owned by the game objects.
		for (size_t i = 1; i < _GP(spriteset).GetSpriteSlotCount(); i++) {
			if ((_GP(game).SpriteInfos[i].Flags & SPF_DYNAMICALLOC) &&
				((_GP(game).SpriteInfos[i].Flags & SPF_OBJECTOWNED) == 0)) {
				debug_script_warn("Dynamic sprite %d was never deleted", i);
			}
		}
	}
}

void quit_shutdown_audio() {
	set_our_eip(9917);
	_GP(game).options[OPT_CROSSFADEMUSIC] = 0;
	shutdown_sound();
}

// Parses the quit message; returns:
// * QuitReason - which is a code of the reason we're quitting (game error, etc);
// * errmsg - a pure error message (extracted from the parsed string).
// * alertis - a complex message to post into the engine output (stdout, log);
QuitReason quit_check_for_error_state(const char *qmsg, String &errmsg, String &alertis) {
	if (qmsg[0] == '|') {
		return kQuit_GameRequest;
	} else if (qmsg[0] == '!') {
		QuitReason qreason;
		qmsg++;

		if (qmsg[0] == '|') {
			qreason = kQuit_UserAbort;
			alertis = "Abort key pressed.\n\n";
		} else if (qmsg[0] == '?') {
			qmsg++;
			qreason = kQuit_ScriptAbort;
			alertis = "A fatal error has been generated by the script using the AbortGame function. Please contact the game author for support.\n\n";
		} else {
			qreason = kQuit_GameError;
			alertis.Format("An error has occurred. Please contact the game author for support, as this "
			               "is likely to be a scripting error and not a bug in AGS.\n"
			               "(Engine version %s)\n\n", _G(EngineVersion).LongString.GetCStr());
		}

		alertis.Append(cc_get_err_callstack());

		if (qreason != kQuit_UserAbort) {
			alertis.AppendFmt("\nError: %s", qmsg);
			errmsg = qmsg;
			Debug::Printf(kDbgMsg_Fatal, "ERROR: %s\n%s", qmsg, cc_get_error().CallStack.GetCStr());
		}
		return qreason;
	} else if (qmsg[0] == '%') {
		qmsg++;
		alertis.Format("A warning has been generated. This is not normally fatal, but you have selected "
		               "to treat warnings as errors.\n"
		               "(Engine version %s)\n\n%s\n%s", _G(EngineVersion).LongString.GetCStr(), cc_get_err_callstack().GetCStr(), qmsg);
		errmsg = qmsg;
		return kQuit_GameWarning;
	} else {
		alertis.Format("An internal error has occurred. Please note down the following information.\n"
		               "(Engine version %s)\n"
		               "\nError: %s", _G(EngineVersion).LongString.GetCStr(), qmsg);
		return kQuit_FatalError;
	}
}

void quit_delete_temp_files() {
#ifdef TODO
	al_ffblk    dfb;
	int dun = al_findfirst("~ac*.tmp", &dfb, FA_SEARCH);
	while (!dun) {
		File::DeleteFile(dfb.name);
		dun = al_findnext(&dfb);
	}
	al_findclose(&dfb);
#endif
}

// quit - exits the engine, shutting down everything gracefully
// The parameter is the message to print. If this message begins with
// an '!' character, then it is printed as a "contact game author" error.
// If it begins with a '|' then it is treated as a "thanks for playing" type
// message. If it begins with anything else, it is treated as an internal
// error.
// "!|" is a special code used to mean that the player has aborted (Alt+X)
void quit(const char *quitmsg) {
	if (!_G(abort_engine)) {
		strncpy(_G(quit_message), quitmsg, sizeof(_G(quit_message)) - 1);
		_G(quit_message)[sizeof(_G(quit_message)) - 1] = '\0';
		_G(abort_engine) = true;
	}
}

void quit_free() {
	if (strlen(_G(quit_message)) == 0)
		Common::strcpy_s(_G(quit_message), "|bye!");

	const char *quitmsg = _G(quit_message);

	Debug::Printf(kDbgMsg_Info, "Quitting the game...");

	// NOTE: we must not use the quitmsg pointer past this step,
	// as it may be from a plugin and we're about to free plugins
	String errmsg, fullmsg;
	QuitReason qreason = quit_check_for_error_state(quitmsg, errmsg, fullmsg);

	if (qreason & kQuitKind_NormalExit)
		save_config_file();

	_G(handledErrorInEditor) = false;

	quit_tell_editor_debugger(errmsg, qreason);

	set_our_eip(9900);

	quit_stop_cd();

	set_our_eip(9020);

	// Be sure to unlock mouse on exit, or users will hate us
	sys_window_lock_mouse(false);

	set_our_eip(9016);

	quit_check_dynamic_sprites(qreason);

	if (_G(use_cdplayer))
		_G(platform)->ShutdownCDPlayer();

	set_our_eip(9019);

	quit_shutdown_audio();

	set_our_eip(9901);

	_GP(spriteset).Reset();

	set_our_eip(9908);

	shutdown_pathfinder();

	unload_game();

	engine_shutdown_gfxmode();

	_G(platform)->PreBackendExit();

	// On abnormal exit: display the message (at this point the window still exists)
	if ((qreason & kQuitKind_NormalExit) == 0 && !_G(handledErrorInEditor)) {
		_G(platform)->DisplayAlert("%s", fullmsg.GetCStr());
	}

	// release backed library
	// WARNING: no Allegro objects should remain in memory after this,
	// if their destruction is called later, program will crash!
	shutdown_font_renderer();
	allegro_exit();
	sys_main_shutdown();

	_G(platform)->PostAllegroExit();

	set_our_eip(9903);

	quit_delete_temp_files();

	_G(proper_exit) = 1;

	Debug::Printf(kDbgMsg_Alert, "***** ENGINE HAS SHUTDOWN");

	shutdown_debug();

	set_our_eip(9904);
}

} // namespace AGS3
