/*
Copyright © 2011-2012 Clint Bellanger
Copyright © 2012 Stefan Beller
Copyright © 2012-2016 Justin Jacobs

This file is part of FLARE.

FLARE 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.

FLARE 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
FLARE.  If not, see http://www.gnu.org/licenses/
*/

/**
 * class QuestLog
 *
 * Helper text to remind the player of active quests
 */

#include "CampaignManager.h"
#include "CommonIncludes.h"
#include "EventManager.h"
#include "FileParser.h"
#include "FontEngine.h"
#include "Menu.h"
#include "MenuLog.h"
#include "MessageEngine.h"
#include "ModManager.h"
#include "QuestLog.h"
#include "SharedGameResources.h"
#include "SharedResources.h"
#include "UtilsFileSystem.h"
#include "UtilsParsing.h"

QuestLog::QuestLog(MenuLog *_log) {
	log = _log;

	newQuestNotification = false;
	loadAll();
}

QuestLog::~QuestLog() {
}

/**
 * Load each [mod]/quests/index.txt file
 */
void QuestLog::loadAll() {
	// load each items.txt file. Individual item IDs can be overwritten with mods.
	std::vector<std::string> files = mods->list("quests", !ModManager::LIST_FULL_PATHS);
	std::sort(files.begin(), files.end());
	for (unsigned int i = 0; i < files.size(); i++)
		load(files[i]);
}

/**
 * Load the quests in the specific quest file.
 * Searches for the last-defined such file in all mods
 *
 * @param filename The quest file name and extension, no path
 */
void QuestLog::load(const std::string& filename) {
	FileParser infile;
	// @CLASS QuestLog|Description of quest files in quests/
	if (!infile.open(filename, FileParser::MOD_FILE, FileParser::ERROR_NORMAL))
		return;

	quests.resize(quests.size()+1);

	while (infile.next()) {
		if (infile.new_section) {
			if (infile.section == "quest") {
				quest_sections.push_back(std::vector<EventComponent>());
			}
		}

		if (infile.section == "") {
			if (infile.key == "name") {
				// @ATTR name|string|A displayed name for this quest.
				quests.back().name = msg->get(infile.val);
			}
			else if (infile.key == "complete_status") {
				// @ATTR complete_status|string|If this status is set, the quest will be displayed as completed.
				quests.back().complete_status = camp->registerStatus(infile.val);
			}

			continue;
		}

		if (quest_sections.empty())
			continue;

		Event ev;
		if (infile.key == "requires_status") {
			// @ATTR quest.requires_status|list(string)|Quest requires this campaign status
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_not_status") {
			// @ATTR quest.requires_not_status|list(string)|Quest requires not having this campaign status.
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_level") {
			// @ATTR quest.requires_level|int|Quest requires hero level
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_not_level") {
			// @ATTR quest.requires_not_level|int|Quest requires not hero level
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_currency") {
			// @ATTR quest.requires_currency|int|Quest requires atleast this much currency
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_not_currency") {
			// @ATTR quest.requires_not_currency|int|Quest requires no more than this much currency
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_item") {
			// @ATTR quest.requires_item|list(item_id)|Quest requires specific item (not equipped)
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_not_item") {
			// @ATTR quest.requires_not_item|list(item_id)|Quest requires not having a specific item (not equipped)
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_class") {
			// @ATTR quest.requires_class|predefined_string|Quest requires this base class
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "requires_not_class") {
			// @ATTR quest.requires_not_class|predefined_string|Quest requires not this base class
			EventManager::loadEventComponent(infile, &ev, NULL);
		}
		else if (infile.key == "quest_text") {
			// @ATTR quest.quest_text|string|Text that gets displayed in the Quest log when this quest is active.
			EventComponent ec;
			ec.type = EventComponent::QUEST_TEXT;
			ec.s = msg->get(infile.val);

			// quest group id
			ec.data[0].Int = static_cast<int>(quests.size()-1);

			ev.components.push_back(ec);
		}
		else {
			Utils::logError("QuestLog: %s is not a valid key.", infile.key.c_str());
		}

		for (size_t i=0; i<ev.components.size(); ++i) {
			if (ev.components[i].type != EventComponent::NONE)
				quest_sections.back().push_back(ev.components[i]);
		}
	}
	infile.close();
}

void QuestLog::logic() {
	createQuestList();
}

/**
 * All active quests are placed in the Quest tab of the Log Menu
 */
void QuestLog::createQuestList() {
	std::vector<size_t> temp_quest_ids;
	std::vector<size_t> temp_complete_quest_ids;

	// check quest requirements
	for (size_t i=0; i<quest_sections.size(); i++) {
		if (camp->checkRequirementsInVector(quest_sections[i])) {
			// passed requirement checks, add ID to active quest list
			temp_quest_ids.push_back(i);
		}
	}

	// check quest completion status
	for (size_t i=0; i<quests.size(); ++i) {
		if (!quests[i].name.empty() && quests[i].complete_status != 0) {
			if (camp->checkStatus(quests[i].complete_status)) {
				temp_complete_quest_ids.push_back(i);
			}
		}
	}

	// check if we actually need to update the quest log
	bool refresh_quest_list = false;
	if (temp_quest_ids != active_quest_ids || temp_complete_quest_ids != complete_quest_ids)
		refresh_quest_list = true;

	// update the quest log
	if (refresh_quest_list) {
		active_quest_ids = temp_quest_ids;
		complete_quest_ids = temp_complete_quest_ids;
		newQuestNotification = true;

		log->clear(MenuLog::TYPE_QUESTS);

		bool complete_header = false;
		for (size_t i = complete_quest_ids.size(); i > 0; i--) {
			if (!complete_header) {
				complete_header = true;
			}
			log->setNextColor(font->getColor(FontEngine::COLOR_WIDGET_DISABLED), MenuLog::TYPE_QUESTS);
			log->add(quests[complete_quest_ids[i-1]].name, MenuLog::TYPE_QUESTS, WidgetLog::MSG_UNIQUE);
		}

		if (complete_header) {
			std::stringstream ss;
			ss << msg->get("Completed Quests") << " (" << complete_quest_ids.size() << ")";
			log->setNextStyle(WidgetLog::FONT_BOLD, MenuLog::TYPE_QUESTS);
			log->setNextColor(font->getColor(FontEngine::COLOR_WIDGET_DISABLED), MenuLog::TYPE_QUESTS);
			log->add(ss.str(), MenuLog::TYPE_QUESTS, WidgetLog::MSG_UNIQUE);
			if (!active_quest_ids.empty())
				log->addSeparator(MenuLog::TYPE_QUESTS);
		}

		for (size_t i=active_quest_ids.size(); i>0; i--) {
			size_t k = active_quest_ids[i-1];

			size_t i_next = (i > 1) ? i-2 : 0;
			size_t k_next = active_quest_ids[i_next];

			// get the group id of the next active quest
			int next_quest_id = 0;
			for (size_t j=0; j<quest_sections[k_next].size(); j++) {
				if (quest_sections[k_next][j].type == EventComponent::QUEST_TEXT) {
					next_quest_id = quest_sections[k_next][j].data[0].Int;
					break;
				}
			}

			for (size_t j=0; j<quest_sections[k].size(); j++) {
				if (quest_sections[k][j].type == EventComponent::QUEST_TEXT) {
					log->add(quest_sections[k][j].s, MenuLog::TYPE_QUESTS, WidgetLog::MSG_UNIQUE);

					int quest_id = quest_sections[k][j].data[0].Int;
					if (next_quest_id != quest_id) {
						if (!quests[quest_id].name.empty()) {
							log->setNextStyle(WidgetLog::FONT_BOLD, MenuLog::TYPE_QUESTS);
							log->add(quests[quest_id].name, MenuLog::TYPE_QUESTS, WidgetLog::MSG_UNIQUE);
						}

						log->addSeparator(MenuLog::TYPE_QUESTS);
					}
					else if (i == 1) {
						if (!quests[quest_id].name.empty()) {
							log->setNextStyle(WidgetLog::FONT_BOLD, MenuLog::TYPE_QUESTS);
							log->add(quests[quest_id].name, MenuLog::TYPE_QUESTS, WidgetLog::MSG_UNIQUE);
						}
					}

					break;
				}
			}
		}
	}
}
