/* 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/config-manager.h"
#include "zvision/detection.h"
#include "zvision/graphics/render_manager.h"
#include "zvision/scripting/menu.h"

namespace ZVision {

enum {
	kFocusNone = -1,
	kFocusItems = 0,
	kFocusMagic = 1,
	kFocusMain = 2
};

MenuManager::MenuManager(ZVision *engine, const Common::Rect menuArea, const MenuParams params) :
	_engine(engine),
	_params(params),
	_menuBarFlag(0xFFFF),
	_menuArea(menuArea),
	_menuOrigin(menuArea.origin()),
	_menuTriggerArea(_menuOrigin, _menuArea.width(), _params.triggerHeight),
	_mainScroller(params.activePos, params.idlePos, params.period) {

	_enableFlags.set_size(6);
	for (int8 i = 0; i < 4; i++) {
		// Generate button hotspot areas
		_menuHotspots[i] = Common::Rect(_params.wxButs[i][1], _menuArea.top, _params.wxButs[i][1] + _params.wxButs[i][0], _menuArea.bottom);
		// Initialise button animation frames
		_mainFrames[i] = _params.idleFrame;
	}
	for (int i = 0; i < 4; i++)
		_buttonAnim[i] = new LinearScroller(_params.activeFrame, _params.idleFrame, _params.buttonPeriod);
	setFocus(kFocusNone); // Ensure focus list is initialised
	_mainArea = Common::Rect(_params.wMain, _hMainMenu);
	_mainArea.moveTo(_menuOrigin + _mainScroller._pos);
}

MenuManager::~MenuManager() {
	for (int i = 0; i < 4; i++)
		delete _buttonAnim[i];
	_mainBack.free();
}

void MenuManager::setEnable(uint16 flags) {
	static const uint16 flagMasks[6] = {0x8, 0x4, 0x2, 0x1, 0x100, 0x200}; // Enum order: save,restore,prefs,quit,items,magic
	_menuBarFlag = flags;
	for (uint i = 0; i <= 5; i++) {
		if (_menuBarFlag & flagMasks[i])
			_enableFlags.set(i);
		else
			_enableFlags.unset(i);
	}
}

void MenuManager::onMouseUp(const Common::Point &pos) {
	if (_menuFocus.front() == kFocusMain) {
		_mouseOnItem = mouseOverMain(pos);
		if (_mouseOnItem == _mainClicked)
			// Activate clicked action from main menu
			switch (_mouseOnItem) {
			case kMainMenuSave:
				_engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0);
				setFocus(kFocusNone);
				_mainScroller.reset();
				_redraw = true;
				break;
			case kMainMenuLoad:
				_engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0);
				setFocus(kFocusNone);
				_mainScroller.reset();
				_redraw = true;
				break;
			case kMainMenuPrefs:
				_engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0);
				setFocus(kFocusNone);
				_mainScroller.reset();
				_redraw = true;
				break;
			case kMainMenuExit:
				_engine->quit(true);
				break;
			default:
				break;
			}
	}
	_mainClicked = -1;
}

void MenuManager::onMouseDown(const Common::Point &pos) {
	if (_menuFocus.front() == kFocusMain) {
		_mouseOnItem = mouseOverMain(pos);
		// Show clicked graphic
		if ((_mouseOnItem >= 0) && (_mouseOnItem < 4))
			if (_enableFlags.get(_mouseOnItem)) {
				_mainClicked = _mouseOnItem;
				_redraw = true;
			}
	}
}

void MenuManager::onMouseMove(const Common::Point &pos) {
	bool nowInMenu = inMenu(pos);
	if (nowInMenu != _prevInMenu)
		_redraw = true;
	_prevInMenu = nowInMenu;

	int lastItem = _mouseOnItem;

	switch (_menuFocus.front()) {
	case kFocusMain:
		// Inform game scripting engine that mouse is in main menu
		if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 2)
			_engine->getScriptManager()->setStateValue(StateKey_MenuState, 2);
		_mouseOnItem = mouseOverMain(pos);
		break;
	case kFocusNone:
		// Inform game scripting engine that mouse is not in any menu
		if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0)
			_engine->getScriptManager()->setStateValue(StateKey_MenuState, 0);
		_mouseOnItem = -1;
		break;
	}
	_mainScroller.setActive(_menuFocus.front() == kFocusMain);
	// Update button animation status
	for (int i = 0; i < 4; i++)
		if (_menuFocus[0] == kFocusMain && _mouseOnItem == i)
			_buttonAnim[i]->setActive(true);
		else
			_buttonAnim[i]->setActive(false);
	if (lastItem != _mouseOnItem)
		_redraw = true;
}

int MenuManager::mouseOverMain(const Common::Point &pos) {
	// Common::Rect mainHotspot(28,_hSideMenu);
	// mainHotspot.moveTo(mainOrigin + _mainScroller._pos);
	for (int8 i = 0; i < 4; i++) {
		if (_enableFlags.get(i) && _menuHotspots[i].contains(pos))
			return i;
	}
	return -1;
}

void MenuManager::process(uint32 deltatime) {
	if (_mainScroller.update(deltatime)) {
		_mainArea.moveTo(_menuOrigin + _mainScroller._pos);
		for (int i = 0; i < 4; i++)
			_menuHotspots[i].moveTo(_menuOrigin + Common::Point(_params.wxButs[i][1], _mainScroller._pos.y));
		_redraw = true;
	}
	// Update button highlight animation frame
	for (int i = 0; i < 4; i++)
		if (_buttonAnim[i]->update(deltatime)) {
			_mainFrames[i] = _buttonAnim[i]->_pos;
			_redraw = true;
		}
	if (_redraw) {
		_engine->getRenderManager()->clearMenuSurface();
		redrawAll();
		_redraw = false;
	}
}

void MenuNemesis::redrawAll() {
	redrawMain();
}

void MenuManager::redrawMain() {
	// Draw menu background
	_engine->getRenderManager()->blitSurfaceToMenu(_mainBack, _mainScroller._pos.x, _mainScroller._pos.y, 0);
	// Draw buttons
	if (_menuFocus.front() == kFocusMain)
		for (int8 i = 0; i < 4; i++) {
			if (_enableFlags.get(i) && (_mainFrames[i] >= 0)) {
				if (_mainClicked == i)
					_engine->getRenderManager()->blitSurfaceToMenu(_mainButtons[i][_params.clickedFrame], _params.wxButs[i][1], _mainScroller._pos.y, 0);
				else
					_engine->getRenderManager()->blitSurfaceToMenu(_mainButtons[i][_mainFrames[i]], _params.wxButs[i][1], _mainScroller._pos.y, 0);
			}
		}
	_clean = false;
}

void MenuManager::setFocus(int8 currentFocus) {
	_menuFocus.set(currentFocus);
	assert(_menuFocus.size() <= 4);
}

MenuZGI::MenuZGI(ZVision *engine, const Common::Rect menuArea) :
	MenuManager(engine, menuArea, zgiParams),
	_itemsScroller(Common::Point(0, 0), Common::Point(_wSideMenuTab - _wSideMenu, 0), _sideMenuPeriod),
	_magicScroller(Common::Point(-_wSideMenu, 0), Common::Point(-_wSideMenuTab, 0), _sideMenuPeriod),
	_itemsOrigin(menuArea.left, menuArea.top),
	_magicOrigin(menuArea.right, menuArea.top) {

	_magicArea = Common::Rect(_magicOrigin + _magicScroller._pos, _wSideMenu, _hSideMenu);
	_itemsArea = Common::Rect(_itemsOrigin + _itemsScroller._pos, _wSideMenu, _hSideMenu);

	// Buffer main menu background
	_engine->getRenderManager()->readImageToSurface("gmzau031.tga", _mainBack, false);

	char buf[24];
	for (int i = 0; i < 4; i++) {
		// Buffer menu buttons
		Common::sprintf_s(buf, "gmzmu%2.2x1.tga", i);
		_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][0], false);
		Common::sprintf_s(buf, "gmznu%2.2x1.tga", i);
		_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][1], false);
	}
	for (int i = 1; i < 4; i++) {
		// Buffer full menu backgrounds
		Common::sprintf_s(buf, "gmzau%2.2x1.tga", i);
		_engine->getRenderManager()->readImageToSurface(buf, _menuBack[i - 1], false);
	}
	for (int i = 0; i < 50; i++) {
		_items[i][0] = NULL;
		_items[i][1] = NULL;
		_itemId[i] = 0;
	}
	for (int i = 0; i < 12; i++) {
		_magic[i][0] = NULL;
		_magic[i][1] = NULL;
		_magicId[i] = 0;
	}
	// Initialise focus sequence
	setFocus(kFocusMain);
	setFocus(kFocusMagic);
	setFocus(kFocusItems);
	setFocus(kFocusNone);
}

MenuZGI::~MenuZGI() {
	for (int i = 0; i < 3; i++) {
		_menuBack[i].free();
	}
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 2; j++)
			_mainButtons[i][j].free();
	}
	for (int i = 0; i < 50; i++) {
		if (_items[i][0]) {
			_items[i][0]->free();
			delete _items[i][0];
		}
		if (_items[i][1]) {
			_items[i][1]->free();
			delete _items[i][1];
		}
	}
	for (int i = 0; i < 12; i++) {
		if (_magic[i][0]) {
			_magic[i][0]->free();
			delete _magic[i][0];
		}
		if (_magic[i][1]) {
			_magic[i][1]->free();
			delete _magic[i][1];
		}
	}
}

bool MenuZGI::inMenu(const Common::Point &pos) const {
	return _menuTriggerArea.contains(pos) || (_menuFocus.front() != kFocusNone);
}

void MenuZGI::onMouseUp(const Common::Point &pos) {
	if (inMenu(pos))
		// _redraw = true;
		switch (_menuFocus.front()) {
		case kFocusItems:
			if (_enableFlags.get(kItemsMenu)) {
				int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
				if (itemCount == 0)
					itemCount = 20;
				int i = mouseOverItem(pos, itemCount);
				if (i != -1) {
					int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
					if (mouseItem >= 0  && mouseItem < 0xE0) {
						_engine->getScriptManager()->inventoryDrop(mouseItem);
						_engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i));
						_engine->getScriptManager()->setStateValue(StateKey_Inv_StartSlot + i, mouseItem);
						_redraw = true;
					}
				}
			}
			break;

		case kFocusMagic:
			if (_enableFlags.get(kMagicMenu)) {
				int i = mouseOverMagic(pos);
				if (i != -1) {

					uint itemnum = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
					if (itemnum != 0) {
						if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1)
							itemnum = 0xEE + i;
						else
							itemnum = 0xE0 + i;
					}
					if (itemnum)
						if (_engine->getScriptManager()->getStateValue(StateKey_InventoryItem) == 0 || _engine->getScriptManager()->getStateValue(StateKey_InventoryItem) >= 0xE0)
							_engine->getScriptManager()->setStateValue(StateKey_Active_Spell, itemnum);
				}

			}
			break;

		case kFocusMain:
			MenuManager::onMouseUp(pos);
			break;

		default:
			break;
		}
}

void MenuZGI::onMouseMove(const Common::Point &pos) {
	if (!inMenu(pos)) {
		_mainScroller.reset();
		_magicScroller.reset();
		_itemsScroller.reset();
	}

	// Set focus to topmost layer of menus that mouse is currently over
	for (uint8 i = 0; i < _menuFocus.size(); i++) {
		switch (_menuFocus[i]) {
		case kFocusItems:
			if (_itemsArea.contains(pos)) {
				setFocus(kFocusItems);
				i = _menuFocus.size() + 1;
			}
			break;
		case kFocusMagic:
			if (_magicArea.contains(pos)) {
				setFocus(kFocusMagic);
				i = _menuFocus.size() + 1;
			}
			break;
		case kFocusMain:
			if (_mainArea.contains(pos)) {
				setFocus(kFocusMain);
				i = _menuFocus.size() + 1;
			}
			break;
		default:
			setFocus(kFocusNone);
			break;
		}
	}
	_itemsScroller.setActive(_menuFocus.front() == kFocusItems);
	_magicScroller.setActive(_menuFocus.front() == kFocusMagic);
	if (_menuFocus.front() != kFocusNone) {
		switch (_menuFocus.front()) {
		case kFocusItems:
			if (_enableFlags.get(kItemsMenu)) {
				int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
				if (itemCount == 0)
					itemCount = 20;
				else if (itemCount > 50)
					itemCount = 50;
				int lastItem = _mouseOnItem;
				_mouseOnItem = mouseOverItem(pos, itemCount);
				if (lastItem != _mouseOnItem)
					if (_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + _mouseOnItem) || _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + lastItem))
						_redraw = true;
			}
			break;

		case kFocusMagic:
			if (_enableFlags.get(kMagicMenu)) {
				int lastItem = _mouseOnItem;
				_mouseOnItem = mouseOverMagic(pos);

				if (lastItem != _mouseOnItem)
					if (_engine->getScriptManager()->getStateValue(StateKey_Spell_1 + _mouseOnItem) || _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + lastItem))
						_redraw = true;

			}
			break;
		case kFocusMain:
			break;
		}
	}
	MenuManager::onMouseMove(pos);
}

int MenuZGI::mouseOverItem(const Common::Point &pos, int itemCount) {
	int itemWidth = (_wSideMenu - 28) / itemCount;
	Common::Rect itemHotspot = Common::Rect(28, _hSideMenu);
	itemHotspot.moveTo(_itemsOrigin + _itemsScroller._pos);
	for (int i = 0; i < itemCount; i++) {
		if (itemHotspot.contains(pos))
			return i;
		itemHotspot.translate(itemWidth, 0);
	}
	return -1;
}

int MenuZGI::mouseOverMagic(const Common::Point &pos) {
	Common::Rect magicHotspot(28, _hSideMenu);
	magicHotspot.moveTo(_magicOrigin + _magicScroller._pos);
	magicHotspot.translate(28, 0); // Offset from end of menu
	for (int i = 0; i < 12; i++) {
		if (magicHotspot.contains(pos))
			return i;
		magicHotspot.translate(_magicWidth, 0);
	}
	return -1;
}

void MenuZGI::process(uint32 deltatime) {
	if (_itemsScroller.update(deltatime)) {
		_itemsArea.moveTo(_itemsOrigin + _itemsScroller._pos);
		_redraw = true;
	}
	if (_magicScroller.update(deltatime)) {
		_magicArea.moveTo(_magicOrigin + _magicScroller._pos);
		_redraw = true;
	}
	MenuManager::process(deltatime);
}

void MenuZGI::redrawAll() {
	if (MenuManager::inMenu())
		for (int8 i = _menuFocus.size() - 1; i >= 0; i--)
			switch (_menuFocus[i]) {
			case kFocusItems:
				if (_enableFlags.get(kItemsMenu)) {
					redrawItems();
				}
				break;
			case kFocusMagic:
				if (_enableFlags.get(kMagicMenu)) {
					redrawMagic();
				}
				break;
			case kFocusMain:
				redrawMain();
				break;
			default:
				break;
			}
}

void MenuZGI::redrawMagic() {
	const int16 yOrigin = _menuArea.width();
	_engine->getRenderManager()->blitSurfaceToMenu(_menuBack[kFocusMagic], yOrigin + _magicScroller._pos.x, 0, 0);
	for (int i = 0; i < 12; i++) {
		bool inrect = false;
		if (_mouseOnItem == i)
			inrect = true;
		uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
		if (curItemId) {
			if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1)
				curItemId = 0xEE + i;
			else
				curItemId = 0xE0 + i;
		}
		if (curItemId != 0) {
			if (_itemId[i] != curItemId) {
				char buf[16];
				Common::sprintf_s(buf, "gmzwu%2.2x1.tga", curItemId);
				_magic[i][0] = _engine->getRenderManager()->loadImage(buf, false);
				Common::sprintf_s(buf, "gmzxu%2.2x1.tga", curItemId);
				_magic[i][1] = _engine->getRenderManager()->loadImage(buf, false);
				_magicId[i] = curItemId;
			}
			if (inrect)
				_engine->getRenderManager()->blitSurfaceToMenu(*_magic[i][1], yOrigin + _magicScroller._pos.x + 28 + _magicWidth * i, 0, 0);
			else
				_engine->getRenderManager()->blitSurfaceToMenu(*_magic[i][0], yOrigin + _magicScroller._pos.x + 28 + _magicWidth * i, 0, 0);
		} else {
			if (_magic[i][0]) {
				_magic[i][0]->free();
				delete _magic[i][0];
				_magic[i][0] = NULL;
			}
			if (_magic[i][1]) {
				_magic[i][1]->free();
				delete _magic[i][1];
				_magic[i][1] = NULL;
			}
			_magicId[i] = 0;
		}
	}
	_clean = false;
}

void MenuZGI::redrawItems() {
	_engine->getRenderManager()->blitSurfaceToMenu(_menuBack[kFocusItems], _itemsScroller._pos.x, 0, 0);
	int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
	if (itemCount == 0)
		itemCount = 20;
	else if (itemCount > 50)
		itemCount = 50;
	int itemWidth = (_wSideMenu - 28) / itemCount;

	for (int i = 0; i < itemCount; i++) {
		bool inrect = false;
		if (_mouseOnItem == i)
			inrect = true;
		uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i);

		if (curItemId != 0) {
			if (_itemId[i] != curItemId) {
				char buf[16];
				Common::sprintf_s(buf, "gmzwu%2.2x1.tga", curItemId);
				_items[i][0] = _engine->getRenderManager()->loadImage(buf, false);
				Common::sprintf_s(buf, "gmzxu%2.2x1.tga", curItemId);
				_items[i][1] = _engine->getRenderManager()->loadImage(buf, false);
				_itemId[i] = curItemId;
			}
			if (inrect)
				_engine->getRenderManager()->blitSurfaceToMenu(*_items[i][1], _itemsScroller._pos.x + itemWidth * i, 0, 0);
			else
				_engine->getRenderManager()->blitSurfaceToMenu(*_items[i][0], _itemsScroller._pos.x + itemWidth * i, 0, 0);
		} else {
			if (_items[i][0]) {
				_items[i][0]->free();
				delete _items[i][0];
				_items[i][0] = NULL;
			}
			if (_items[i][1]) {
				_items[i][1]->free();
				delete _items[i][1];
				_items[i][1] = NULL;
			}
			_itemId[i] = 0;
		}
	}
	_clean = false;
}

MenuNemesis::MenuNemesis(ZVision *engine, const Common::Rect menuArea) :
	MenuManager(engine, menuArea, nemesisParams) {

	// Buffer menu background image
	_engine->getRenderManager()->readImageToSurface("bar.tga", _mainBack, false);

	char buf[24];
	for (int i = 0; i < 4; i++)
		// Buffer menu buttons
		for (int j = 0; j < 6; j++) {
			Common::sprintf_s(buf, "butfrm%d%d.tga", i + 1, j);
			_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][j], false);
		}

}

MenuNemesis::~MenuNemesis() {
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 6; j++)
			_mainButtons[i][j].free();

}

bool MenuNemesis::inMenu(const Common::Point &pos) const {
	return _menuTriggerArea.contains(pos) || (_menuFocus.front() != kFocusNone);
}

void MenuNemesis::onMouseMove(const Common::Point &pos) {
	// Trigger main menu scrolldown to get mouse over main trigger area
	// Set focus to topmost layer of menus that mouse is currently over
	if (_mainArea.contains(pos) || _menuTriggerArea.contains(pos))
		setFocus(kFocusMain);
	else
		setFocus(kFocusNone);
	MenuManager::onMouseMove(pos);
}

} // End of namespace ZVision
