/* 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 "common/engine_data.h"
#include "common/file.h"
#include "engines/engine.h"

#include "bagel/baglib/bagel.h"
#include "bagel/boflib/sound.h"
#include "bagel/dialogs/next_cd_dialog.h"
#include "bagel/dialogs/opt_window.h"
#include "bagel/baglib/paint_table.h"
#include "bagel/baglib/master_win.h"
#include "bagel/boflib/debug.h"
#include "bagel/boflib/file_functions.h"
#include "bagel/boflib/log.h"

namespace Bagel {

// Initialize global variables.
//
CBofWindow *g_hackWindow;

CBagel::CBagel(const BagelReg *gameReg) {
	assert(gameReg != nullptr);

	registerGame(gameReg);
}

CBagel::~CBagel() {
	assert(isValidObject(this));

	// Release options file
	release();

	_szAppName[0] = '\0';
	_pMainWnd = nullptr;
	_gameReg = nullptr;
}

void CBagel::registerGame(const BagelReg *gameReg) {
	assert(isValidObject(this));
	_gameReg = gameReg;

	// Init statics
	g_hackWindow = nullptr;

	// Use registration info to init this game object
	if (gameReg != nullptr) {
		// Keep application name
		setAppName(gameReg->_gameName);

		// Load this game's .ini file
		if (gameReg->_optionFile != nullptr)
			loadOptionFile(gameReg->_optionFile);
	}
}

ErrorCode CBagel::setOption(const char *section, const char *option, const char *stringValue) {
	assert(isValidObject(this));
	return writeSetting(section, option, stringValue);
}

ErrorCode CBagel::setOption(const char *section, const char *option, int intValue) {
	assert(isValidObject(this));
	return writeSetting(section, option, intValue);
}

ErrorCode CBagel::getOption(const char *section, const char *option, char *stringValue, const char *defaultValue, uint32 size) {
	assert(isValidObject(this));
	return readSetting(section, option, stringValue, defaultValue, size);
}

ErrorCode CBagel::getOption(const char *section, const char *option, int *intValue, int defaultValue) {
	assert(isValidObject(this));
	return readSetting(section, option, intValue, defaultValue);
}

ErrorCode CBagel::getOption(const char *section, const char *option, bool *boolValue, int defaultValue) {
	assert(isValidObject(this));
	return readSetting(section, option, boolValue, defaultValue);
}

ErrorCode CBagel::initialize() {
	assert(isValidObject(this));

	// Game must already be registered with registerGame()
	assert(_gameReg != nullptr);

	CBofApp::initialize();

	// Initialise engine data for the game
	Common::U32String errMsg;
	if (!Common::load_engine_data("bagel.dat", "", 1, 0, errMsg)) {
		GUIErrorMessage("Could not find bagel.dat data file");
		const Common::String msg(errMsg);
		bofMessageBox("Engine Data", msg.c_str());
		_errCode = ERR_FREAD;
		return _errCode;
	}

	Common::File paintTable;
	if (!paintTable.open("paint_table.txt")) {
		bofMessageBox("Engine Data", "Invalid Paint Table");
		_errCode = ERR_FREAD;
		return _errCode;
	}
	PaintTable::initialize(paintTable);

	getOption("UserOptions", "WrongCDRetries", &_numRetries, 20);
	if (_numRetries < 1) {
		_numRetries = 100;
	}

	logInfo("Initializing BAGEL");

	// Turn off System cursor
	CBofCursor::hide();

	// Initialize local game paths
	initLocalFilePaths();

	// Child class must instantiate the Main Window
	return _errCode;
}

ErrorCode CBagel::runApp() {
	assert(isValidObject(this));

	// The main game window must have been created by now
	assert(_pMainWnd != nullptr);

	return CBofApp::runApp();
}

ErrorCode CBagel::shutdown() {
	assert(isValidObject(this));

	logInfo("Shutting down BAGEL");

	// Just in case the child class forgot to delete the main window,
	// I'll do it.
	delete _pMainWnd;
	_pMainWnd = nullptr;

	return CBofApp::shutdown();
}


ErrorCode CBagel::initLocalFilePaths() {
	assert(isValidObject(this));

	// Check for Installed state of game
	getOption("Startup", "InstallCode", &_installCode, BAG_INSTALL_DEFAULT);

	return _errCode;
}

ErrorCode CBagel::verifyCDInDrive(int diskId, const char *waveFile) {
	assert(isValidObject(this));

	if (_gameReg->_numberOfCDs > 0) {
		char szBuf[MAX_DIRPATH];

		// Find the drive that this disk is in
		Common::sprintf_s(szBuf, "DISK%d", diskId);
		Common::sprintf_s(szBuf, "$SBARDIR%sDISK%d", PATH_DELIMETER, diskId);

		CBofString cString(szBuf, MAX_DIRPATH);
		fixPathName(cString);

		// If the disk is not in drive, then inform the user
		int i = 0;
		while (!shouldQuit() && !fileExists(cString.getBuffer())) {
			// display a dialog box that tells the user to put the CD back into
			// the drive, or Abort.
			logInfo(buildString("Unable to find game's DiskID as '%s'", cString.getBuffer()));

			if (i++ > _numRetries) {
				reportError(ERR_FFIND, "Could not recover from missing CD");
				break;
			}

			// Play the Zelda "Alias, you should insert disk 1 at this time."
			if (i == 1 && waveFile != nullptr) {
				BofPlaySound(waveFile, SOUND_WAVE | SOUND_ASYNCH);
			}

			if (g_hackWindow == nullptr) {
				showNextCDDialog(_pMainWnd, diskId);
			} else {
				showNextCDDialog(g_hackWindow, diskId);
			}
		}
	}

	return _errCode;
}

void fixPathName(CBofString &s) {
	// Remove any homedir prefix. In ScummVM, all paths are relative
	// to the game folder automatically
	char *p = strstr(s.getBuffer(), HOMEDIR_TOKEN);
	if (p != nullptr)
		s = p + strlen(HOMEDIR_TOKEN) + 1;

	// Replace any backslashes with forward slashes
	while ((p = strchr(s.getBuffer(), '\\')) != nullptr)
		*p = '/';
}


void CBagel::showNextCDDialog(CBofWindow *parentWin, int diskId) {
	CBagNextCDDialog cNextCDDialog;

	// Use specified bitmap as this dialog's image
	CBofPalette *pPal = nullptr;
	CBofBitmap *pBmp = nullptr;

	switch (diskId) {

	case 1:
		pBmp = Bagel::loadBitmap(buildSysDir("DISK1.BMP"), pPal);
		break;

	case 2:
		pBmp = Bagel::loadBitmap(buildSysDir("DISK2.BMP"), pPal);
		break;

	case 3:
		pBmp = Bagel::loadBitmap(buildSysDir("DISK3.BMP"), pPal);
		break;

	// Shouldn't ever get here
	default:
		assert(false);
		break;
	}

	cNextCDDialog.setBackdrop(pBmp);

	const CBofRect cRect = cNextCDDialog.getBackdrop()->getRect();

	// Create the dialog box
	cNextCDDialog.create("NextCD", cRect.left, cRect.top, cRect.width(), cRect.height(), parentWin);
	cNextCDDialog.center();

	cNextCDDialog.doModal();
}

} // namespace Bagel
