/* 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/ultima8/gumps/cru_menu_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/quit_gump.h"
#include "ultima/ultima8/gumps/difficulty_gump.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/games/game.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/metaengine.h"

#include "engines/dialogs.h"
#include "gui/saveload.h"

namespace Ultima {
namespace Ultima8 {

DEFINE_RUNTIME_CLASSTYPE_CODE(CruMenuGump)

static const int MENU_MUSIC_REMORSE = 20;
static const int MENU_MUSIC_REGRET = 18;

CruMenuGump::CruMenuGump()
	: ModalGump(0, 0, 640, 480, 0, FLAG_DONT_SAVE) {

	Mouse *mouse = Mouse::get_instance();
	mouse->pushMouseCursor(Mouse::MOUSE_HAND);

	// Save old music state
	MusicProcess *musicprocess = MusicProcess::get_instance();
	if (musicprocess) {
		musicprocess->saveTrackState();

		int track = GAME_IS_REMORSE ? MENU_MUSIC_REMORSE : MENU_MUSIC_REGRET;
		// Play the menu music
		musicprocess->playMusic(track);
	}
}

CruMenuGump::~CruMenuGump() {
}

void CruMenuGump::Close(bool no_del) {
	// Restore old music state and palette.
	// Music state can be changed by the Intro and Credits
	MusicProcess *musicprocess = MusicProcess::get_instance();
	if (musicprocess)
		musicprocess->restoreTrackState();

	Mouse *mouse = Mouse::get_instance();
	mouse->popMouseCursor();

	ModalGump::Close(no_del);
}

static const int FRAME_TOP_LEFT = 54;
static const int FIRST_MENU_ENTRY = 58;

static const int NUM_MENU_ENTRIES = 6;
static const int MENU_ENTRY_X_REM[] = {45, 45, 45, 446, 488, 550};
static const int MENU_ENTRY_Y_REM[] = {50, 101, 151, 58, 151, 198};
static const int MENU_ENTRY_X_REG[] = {45, 45, 45, 446, 489, 550};
static const int MENU_ENTRY_Y_REG[] = {95, 147, 197, 103, 196, 243};

void CruMenuGump::InitGump(Gump *newparent, bool take_focus) {
	ModalGump::InitGump(newparent, take_focus);

	GumpShapeArchive *shapeArchive = GameData::get_instance()->getGumps();

	Shape *topLeft = shapeArchive->getShape(FRAME_TOP_LEFT);
	Shape *topRight = shapeArchive->getShape(FRAME_TOP_LEFT + 1);
	Shape *botLeft = shapeArchive->getShape(FRAME_TOP_LEFT + 2);
	Shape *botRight = shapeArchive->getShape(FRAME_TOP_LEFT + 3);

	if (!topLeft || !topRight || !botLeft || !botRight) {
		error("Couldn't load shapes for menu background");
		return;
	}

	PaletteManager *palman = PaletteManager::get_instance();
	assert(palman);
	const Palette *pal = palman->getPalette(PaletteManager::Pal_Misc);
	assert(pal);
	topLeft->setPalette(pal);
	topRight->setPalette(pal);
	botLeft->setPalette(pal);
	botRight->setPalette(pal);

	const ShapeFrame *tlFrame = topLeft->getFrame(0);
	const ShapeFrame *trFrame = topRight->getFrame(0);
	const ShapeFrame *blFrame = botLeft->getFrame(0);
	const ShapeFrame *brFrame = botRight->getFrame(0);
	if (!tlFrame || !trFrame || !blFrame || !brFrame) {
		error("Couldn't load shape frames for menu background");
		return;
	}

	_dims.left = 0;
	_dims.top = 0;
	_dims.setWidth(tlFrame->_width + trFrame->_width);
	_dims.setHeight(tlFrame->_height + brFrame->_height);

	Gump *tlGump = new Gump(0, 0, tlFrame->_width, tlFrame->_height, 0, 0, _layer);
	tlGump->SetShape(topLeft, 0);
	tlGump->InitGump(this, false);
	Gump *trGump = new Gump(tlFrame->_width, 0, trFrame->_width, trFrame->_height, 0, 0, _layer);
	trGump->SetShape(topRight, 0);
	trGump->InitGump(this, false);
	Gump *blGump = new Gump(0, tlFrame->_height, blFrame->_width, blFrame->_height, 0, 0, _layer);
	blGump->SetShape(botLeft, 0);
	blGump->InitGump(this, false);
	Gump *brGump = new Gump(blFrame->_width, trFrame->_height, brFrame->_width, brFrame->_height, 0, 0, _layer);
	brGump->SetShape(botRight, 0);
	brGump->InitGump(this, false);

	const int *MENU_ENTRY_X = GAME_IS_REMORSE ? MENU_ENTRY_X_REM : MENU_ENTRY_X_REG;
	const int *MENU_ENTRY_Y = GAME_IS_REMORSE ? MENU_ENTRY_Y_REM : MENU_ENTRY_Y_REG;

	for (int i = 0; i < NUM_MENU_ENTRIES; i++) {
		uint32 entryShapeNum = FIRST_MENU_ENTRY + i;
		Shape *menuEntry = shapeArchive->getShape(entryShapeNum);
		if (!menuEntry) {
			error("Couldn't load shape for menu entry %d", i);
			return;
		}
		menuEntry->setPalette(pal);

		const ShapeFrame *menuEntryFrame = menuEntry->getFrame(0);
		if (!menuEntryFrame || menuEntry->frameCount() != 2) {
			error("Couldn't load shape frame for menu entry %d", i);
			return;
		}

		FrameID frame_up(GameData::GUMPS, entryShapeNum, 0);
		FrameID frame_down(GameData::GUMPS, entryShapeNum, 1);
		Gump *widget = new ButtonWidget(MENU_ENTRY_X[i], MENU_ENTRY_Y[i],
										frame_up, frame_down, true, _layer + 1);
		widget->InitGump(this, false);
		widget->SetIndex(i + 1);
	}
}

void CruMenuGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
	Gump::PaintThis(surf, lerp_factor, scaled);
}

bool CruMenuGump::OnKeyDown(int key, int mod) {
	if (Gump::OnKeyDown(key, mod)) return true;

	if (key == Common::KEYCODE_ESCAPE) {
		// FIXME: this check should probably be in Game or GUIApp
		MainActor *av = getMainActor();
		if (av && !av->hasActorFlags(Actor::ACT_DEAD))
			Close(); // don't allow closing if dead/game over
	} else if (key >= Common::KEYCODE_1 && key <= Common::KEYCODE_6) {
		selectEntry(key - Common::KEYCODE_1 + 1);
	}

	return true;
}

void CruMenuGump::ChildNotify(Gump *child, uint32 message) {
	ButtonWidget *buttonWidget = dynamic_cast<ButtonWidget *>(child);
	if (buttonWidget && message == ButtonWidget::BUTTON_CLICK) {
		selectEntry(child->GetIndex());
	}
}

void CruMenuGump::selectEntry(int entry) {
	switch (entry) {
	case 1: { // New Game
		Ultima8Engine::get_instance()->newGame(-1);
		// When starting a new game from the menu, we skip intro movies.
		CruGame *game = dynamic_cast<CruGame *>(Game::get_instance());
		assert(game);
		game->setSkipIntroMovie();
		break;
	}
	case 2:
		Ultima8Engine::get_instance()->loadGameDialog();
		break;
	case 3: // Load/Save Game
		Ultima8Engine::get_instance()->saveGameDialog();
		break;
	case 4: {
		// Options - show the ScummVM options dialog
		GUI::ConfigDialog dlg;
		dlg.runModal();
	}
	break;
	case 5: { // Credits
		CruGame *game = dynamic_cast<CruGame *>(Game::get_instance());
		assert(game);
		game->playCreditsNoMenu();
		break;
	}
	case 6: // Quit
		QuitGump::verifyQuit();
		break;
	default:
		break;
	}
}

bool CruMenuGump::OnTextInput(int unicode) {
	return Gump::OnTextInput(unicode);
}

} // End of namespace Ultima8
} // End of namespace Ultima
