/* 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/textconsole.h"

#include "tetraedge/tetraedge.h"
#include "tetraedge/game/application.h"
#include "tetraedge/game/cellphone.h"
#include "tetraedge/game/character.h"
#include "tetraedge/game/game.h"
#include "tetraedge/game/inventory.h"
#include "tetraedge/game/inventory_object.h"
#include "tetraedge/game/inventory_objects_xml_parser.h"

#include "tetraedge/te/te_core.h"

namespace Tetraedge {

Inventory::Inventory() : _cellphone(nullptr), _selectedObject(nullptr) {
}

void Inventory::enter() {
	setVisible(true);
	Game *game = g_engine->getGame();
	Character *character = game->scene()._character;
	character->stop();
	character->setAnimation(character->characterSettings()._idleAnimFileName, true);
	_gui.layoutChecked("textObject")->setVisible(false);

	if (!game->_firstInventory) {
		_gui.buttonLayoutChecked("Aide")->setVisible(false);
	} else {
		game->_firstInventory = false;
	}

	if (_selectedObject)
		selectedObject(_selectedObject);
}

void Inventory::leave() {
	setVisible(false);
	if (_selectedObject) {
		Game *game = g_engine->getGame();
		if (game->entered())
			game->luaScript().execute("OnSelectedObject", _selectedObject->name());
	}
}

void Inventory::load() {
	setName("inventory");
	setSizeType(RELATIVE_TO_PARENT);
	setSize(TeVector3f32(1.0f, 1.0f, userSize().z()));
	_gui.load("Inventory/Inventory.lua");
	TeLayout *invlayout = _gui.layoutChecked("inventory");
	addChild(invlayout);

	TeButtonLayout *btn;
	btn = _gui.buttonLayoutChecked("cellphone");
	btn->onMouseClickValidated().add(this, &Inventory::onVisibleCellphone);

	btn = _gui.buttonLayoutChecked("prendre");
	btn->setVisible(false);
	btn->onMouseClickValidated().add(this, &Inventory::onTakeObjectSelected);

	btn = _gui.buttonLayoutChecked("lire");
	btn->setEnable(false);
	btn->onMouseClickValidated().add(this, &Inventory::onZoomed);

	btn = _gui.buttonLayoutChecked("quitButton");
	btn->setVisible(true);
	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);

	btn = _gui.buttonLayoutChecked("quitBackground");
	btn->setVisible(true);
	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);

	btn = _gui.buttonLayoutChecked("mainMenuButton");
	btn->setVisible(true);
	btn->onMouseClickValidated().add(this, &Inventory::onMainMenuButton);

	_selectedObject = nullptr;
	loadCellphone();

	const Common::Path objectsPathPrefix("Inventory/Objects/Objects_");
	const Common::String &lang = g_engine->getCore()->language();
	Common::Path langXmlPath = objectsPathPrefix.append(lang).appendInPlace(".xml");
	if (!Common::File::exists(langXmlPath)) {
		langXmlPath = objectsPathPrefix.append(".xml");
		if (!Common::File::exists(langXmlPath)) {
			langXmlPath = objectsPathPrefix.append("en.xml");
			if (!Common::File::exists(langXmlPath)) {
				error("Inventory::load Couldn't find inventory objects xml.");
			}
		}
	}

	loadXMLFile(langXmlPath);

	TeLayout *layout = _gui.layout("selectionSprite");
	layout->setVisible(false);
	_invObjects.clear();

	setVisible(false);
}

void Inventory::loadXMLFile(const Common::Path &path) {
	Common::File xmlfile;
	xmlfile.open(path);
	int64 fileLen = xmlfile.size();
	char *buf = new char[fileLen + 1];
	buf[fileLen] = '\0';
	xmlfile.read(buf, fileLen);
	const Common::String xmlContents = Common::String::format("<?xml version=\"1.0\" encoding=\"UTF-8\"?><document>%s</document>", buf);
	delete [] buf;
	xmlfile.close();

	InventoryObjectsXmlParser parser;
	if (!parser.loadBuffer((const byte *)xmlContents.c_str(), xmlContents.size()))
		error("Couldn't load inventory xml.");
	if (!parser.parse())
		error("Couldn't parse inventory xml.");
	_objectData = parser._objects;
}

void Inventory::unload() {
	int pageNo = 0;
	while (true) {
		const Common::String pageStr = Common::String::format("page%d", pageNo);
		if (_gui.layout(pageStr)) {
			int slotNo = 0;
			while (true) {
				const Common::String slotStr = Common::String::format("page%dSlot%d", pageNo, slotNo);
				TeLayout *slotLayout = _gui.layout(slotStr);
				if (!slotLayout)
					break;

				// Take a copy of the list as we may be deleting some
				// and that removes them from the parent.
				Common::Array<Te3DObject2 *> children = slotLayout->childList();
				for (Te3DObject2 *child : children) {
					InventoryObject *invObj = dynamic_cast<InventoryObject *>(child);
					if (invObj)
						delete invObj;
				}
				slotNo++;
			}
			pageNo++;
		} else {
			break;
		}
	}
	_gui.unload();
}

void Inventory::loadCellphone() {
	_cellphone = new Cellphone();
	_cellphone->load();
}

//void loadFromBackup(TiXmlNode *node);
//void saveToBackup(TiXmlNode *node);

void Inventory::addObject(const Common::String &objId) {
	InventoryObject *newobj = new InventoryObject();
	newobj->load(objId);
	if (!addObject(newobj))
		delete newobj;
}

bool Inventory::addObject(InventoryObject *obj) {
	_invObjects.push_front(obj);
	obj->selectedSignal().add(this, &Inventory::onObjectSelected);
	if (_invObjects.size() > 1) {
		int pageNo = 0;
		while (true) {
			TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
			int slotNo = 0;
			if (!page)
				break;
			while (true) {
				TeLayout *slot = _gui.layout(Common::String::format("page%dSlot%d", pageNo, slotNo));
				if (!slot)
					break;
				for (long c = 0; c < slot->childCount(); c++) {
					Te3DObject2 *child = slot->child(c);
					InventoryObject *iobj = dynamic_cast<InventoryObject *>(child);
					if (iobj) {
						slot->removeChild(child);
						c--;
					}
				}
				slotNo++;
			}
			pageNo++;
		}
	}

	int pageno = 0;
	uint totalSlots = 0;
	bool retval = false;
	const Common::String newObjName = obj->name();
	auto invObjIter = _invObjects.begin();
	bool finished = false;
	while (!finished) {
		TeLayout *page = _gui.layout(Common::String::format("page%d", pageno));
		retval = false;
		if (!page)
			break;
		int slotno = 0;
		while (true) {
			TeLayout *slot = _gui.layout(Common::String::format("page%dSlot%d", pageno, slotno));
			if (!slot)
				break;
			retval = true;

			if (totalSlots == _invObjects.size()) {
				finished = true;
				break;
			}

			TeTextLayout *newText = new TeTextLayout();
			newText->setSizeType(CoordinatesType::RELATIVE_TO_PARENT);
			newText->setPosition(TeVector3f32(1.0, 1.0, 0.0));
			newText->setSize(TeVector3f32(1.0, 1.0, 0.0));
			newText->setTextSizeType(1);
			newText->setTextSizeProportionalToWidth(200);
			newText->setText(_gui.value("textAttributs").toString() + objectName((*invObjIter)->name()));
			newText->setName((*invObjIter)->name());
			newText->setVisible(false);

			TeLayout *layout = _gui.layout("textObject");
			layout->addChild(newText);
			slot->addChild(*invObjIter);

			totalSlots++;
			slotno++;
			invObjIter++;
		}
		pageno++;
	}
	return retval;
}

bool Inventory::isDocument(const Common::String &objId) {
	if (!_objectData.contains(objId))
		return false;
	return _objectData.getVal(objId)._isDocument;
}

int Inventory::objectCount(const Common::String &objId) {
	for (const InventoryObject *obj : _invObjects) {
		if (obj->name() == objId)
			return 1;
	}
	return 0;
}

Common::String Inventory::objectDescription(const Common::String &objId) {
	// Note: The game XML files don't actually include descriptions.
	return "";
}

Common::String Inventory::objectName(const Common::String &objId) {
	if (!_objectData.contains(objId))
		return "";
	return _objectData.getVal(objId)._name;
}

bool Inventory::onMainMenuButton() {
	leave();
	Game *game = g_engine->getGame();
	game->leave(false);
	Application *app = g_engine->getApplication();
	app->mainMenu().enter();
	return true;
}

bool Inventory::onObjectSelected(InventoryObject &obj) {
	selectedObject(&obj);
	if (_selectedTimer.running()) {
		if (_selectedTimer.timeElapsed() < 300000)
			g_engine->getGame()->inventoryMenu().leave();
	} else {
		_selectedTimer.start();
	}
	return false;
}

bool Inventory::onQuitButton() {
	Game *game = g_engine->getGame();
	game->inventoryMenu().leave();
	return true;
}

bool Inventory::onTakeObjectSelected() {
	Game *game = g_engine->getGame();
	game->inventoryMenu().leave();
	return false;
}

bool Inventory::onVisibleCellphone() {
	_cellphone->enter();
	Game *game = g_engine->getGame();
	game->inventoryMenu().leave();
	leave();
	return false;
}

bool Inventory::onZoomed() {
	const Common::String &selected = selectedObject();
	if (!selected.empty()) {
		Game *game = g_engine->getGame();
		game->documentsBrowser().showDocument(selected, 0);
	}
	return false;
}

void Inventory::pauseAnims() {
	Game *game = g_engine->getGame();
	if (game->scene()._character) {

	}
	error("TODO: implement Inventory::pauseAnims");
}

void Inventory::unPauseAnims() {
	Game *game = g_engine->getGame();
	if (game->scene()._character) {

	}
	error("TODO: implement Inventory::unPauseAnims");
}

void Inventory::removeObject(const Common::String &objname) {
	int pageNo = 0;
	bool finished = false;
	while (!finished) {
		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
		if (!page)
			break;
		int slotNo = 0;
		while (true) {
			const Common::String slotStr = Common::String::format("page%dSlot%d", pageNo, slotNo);
			TeLayout *slotLayout = _gui.layout(slotStr);
			if (!slotLayout)
				break;

			for (Te3DObject2 *child : slotLayout->childList()) {
				InventoryObject *childObj = dynamic_cast<InventoryObject *>(child);
				if (childObj && childObj->name() == objname) {
					if (_selectedObject == childObj)
						selectedObject(nullptr);
					for (auto iter = _invObjects.begin(); iter != _invObjects.end(); iter++) {
						if ((*iter)->name() == objname) {
							_invObjects.erase(iter);
							break;
						}
					}
					delete childObj;
					updateLayout();
					return;
				}
			}
			slotNo++;
		}
		pageNo++;
	}
}

void Inventory::removeSelectedObject() {
	if (_selectedObject) {
		removeObject(_selectedObject->name());
		selectedObject(nullptr);
	}
}

InventoryObject *Inventory::selectedInventoryObject() {
	return _selectedObject;
}

void Inventory::selectedObject(const Common::String &objname) {
	error("TODO: implement Inventory::selectedObject('%s')", objname.c_str());
}

void Inventory::selectedObject(InventoryObject *obj) {
	Game *game = g_engine->getGame();
	game->setCurrentObjectSprite("");
	_gui.layoutChecked("prendre")->setVisible(false);
	_gui.layoutChecked("textObject")->setVisible(false);
	_selectedObject = obj;
	if (!obj) {
		_gui.spriteLayoutChecked("selectionSprite")->setVisible(false);
		_gui.textLayout("text")->setText("");
		game->inGameGui().spriteLayoutChecked("selectedObject")->unload();
	} else {
		TeSpriteLayout *selection = _gui.spriteLayoutChecked("selectionSprite");
		selection->setVisible(obj->worldVisible());
		TeLayout *parentLayout = dynamic_cast<TeLayout *>(obj->parent());
		TeVector3f32 pos = parentLayout->position();
		pos.z() = selection->position().z();
		selection->setPosition(pos);
		const Common::String &objId = obj->name();
		static const char *textStyle = "<section style=\"center\" /><color r=\"200\" g=\"200\" b=\"200\"/><font file=\"Common/Fonts/Colaborate-Regular.otf\" size=\"24\" />";
		Common::String text = Common::String::format("%s%s<br/>%s", textStyle,
					objectName(objId).c_str(),
					objectDescription(objId).c_str());
		_gui.textLayout("text")->setText(text);
		_gui.buttonLayoutChecked("lire")->setEnable(isDocument(objId));
		game->setCurrentObjectSprite(obj->spritePath());
		TeLayout *textObj = _gui.layout("textObject");
		for (long i = 0; i < textObj->childCount(); i++) {
			if (textObj->child(i)->name() == obj->name()) {
				textObj->setVisible(true);
				textObj->child(i)->setVisible(true);
			} else {
				textObj->child(i)->setVisible(false);
			}
		}
		game->inGameGui().spriteLayoutChecked("selectedObject")->load(obj->spritePath());
	}
}

const Common::String &Inventory::selectedObject() {
	if (_selectedObject == nullptr)
		return _blankStr;
	else
		return _selectedObject->name();
}

bool Inventory::updateLayout() {
	int pageNo = 0;
	bool finished = false;
	while (!finished) {
		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
		if (!page)
			break;
		int slotNo = 0;
		while (true) {
			const Common::String slotStr = Common::String::format("page%dSlot%d", pageNo, slotNo);
			TeLayout *slotLayout = _gui.layout(slotStr);
			if (!slotLayout)
				break;

			// Take a copy of the list as we are deleting some
			// and that removes them from the parent's list.
			Common::Array<Te3DObject2 *> children = slotLayout->childList();
			for (Te3DObject2 *child : children) {
				InventoryObject *invObj = dynamic_cast<InventoryObject *>(child);
				if (invObj)
					slotLayout->removeChild(child);
			}
			slotNo++;
		}
		pageNo++;
	}

	pageNo = 0;
	auto invObjIter = _invObjects.begin();
	while (!finished) {
		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
		if (!page)
			break;
		int slotNo = 0;
		while (true) {
			const Common::String slotStr = Common::String::format("page%dSlot%d", pageNo, slotNo);
			TeLayout *slotLayout = _gui.layout(slotStr);
			if (!slotLayout)
				break;
			slotLayout->addChild(*invObjIter);
			invObjIter++;
			slotNo++;
			if (invObjIter == _invObjects.end())
				return true;
		}
		pageNo++;
	}
	return false;
}

//#define TETRAEDGE_DEBUG_SAVELOAD

Common::Error Inventory::syncState(Common::Serializer &s) {
	uint nitems = _invObjects.size();
	s.syncAsUint32LE(nitems);
	if (s.isLoading()) {
#ifdef TETRAEDGE_DEBUG_SAVELOAD
		debug("Inventory::syncState: --- Loading %d inventory items: ---", nitems);
#endif
		for (uint i = 0; i < nitems; i++) {
			Common::String objname;
			s.syncString(objname);
			addObject(objname);
#ifdef TETRAEDGE_DEBUG_SAVELOAD
			debug("Inventory::syncState: 	%s", objname.c_str());
#endif
		}
	} else if (nitems) {
#ifdef TETRAEDGE_DEBUG_SAVELOAD
		debug("Inventory::syncState: --- Saving %d inventory items: --- ", _invObjects.size());
#endif
		// Add items in reverse order as the "addObject" on load will
		// add to front of list.InventoryObject
		auto iter = _invObjects.end();
		while (iter != _invObjects.begin()) {
			iter--;
			Common::String objname = (*iter)->name();
			s.syncString(objname);
#ifdef TETRAEDGE_DEBUG_SAVELOAD
			debug("Inventory::syncState: 	%s", objname.c_str());
#endif
		}
	}

#ifdef TETRAEDGE_DEBUG_SAVELOAD
	debug("Inventory::syncState: -------- end --------");
#endif
	return Common::kNoError;
}


} // end namespace Tetraedge
