// This file is part of ZetaGlest <https://github.com/ZetaGlest>
//
// Copyright (C) 2018  The ZetaGlest team
//
// ZetaGlest is a fork of MegaGlest <https://megaglest.org>
//
// 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 <https://www.gnu.org/licenses/>

#include "resource_type.h"

#include "util.h"
#include "element_type.h"
#include "logger.h"
#include "renderer.h"
#include "xml_parser.h"
#include "game_util.h"
#include "properties.h"
#include "lang.h"
#include "leak_dumper.h"

using namespace Shared::Util;
using namespace Shared::Xml;

namespace Game {
	// =====================================================
	//      class ResourceType
	// =====================================================

	ResourceType::ResourceType() {
		resourceClass = rcTech;
		tilesetObject = 0;
		resourceNumber = 0;
		interval = 0;
		defResPerPatch = 0;
		recoup_cost = false;
		model = NULL;
		displayInHud = false;
		cleanupMemory = true;
	}

	ResourceType::~ResourceType() {
		if (cleanupMemory == true) {
			while (particleTypes.empty() == false) {
				delete particleTypes.back();
				particleTypes.pop_back();
			}
		}
	}

	string ResourceType::getName(bool translatedValue) const {
		if (translatedValue == false)
			return name;

		Lang & lang = Lang::getInstance();
		return lang.getTechTreeString("ResourceTypeName_" + name,
			name.c_str());
	}

	void ResourceType::load(const string & dir, Checksum * checksum,
		Checksum * techtreeChecksum, std::map < string,
		vector < pair < string,
		string > > >&loadedFileList, string techtreePath) {

		string path, str;
		Renderer & renderer = Renderer::getInstance();

		try {
			recoup_cost = true;

			name = lastDir(dir);

			char szBuf[8096] = "";
			snprintf(szBuf, 8096,
				Lang::getInstance().
				getString("LogScreenGameLoadingResourceType", "").c_str(),
				formatString(getName(true)).c_str());
			Logger::getInstance().add(szBuf, true);

			string currentPath = dir;
			endPathWithSlash(currentPath);
			path = currentPath + name + ".xml";
			string sourceXMLFile = path;
			checksum->addFile(path);
			techtreeChecksum->addFile(path);

			//tree
			XmlTree xmlTree;
			std::map < string, string > mapExtraTagReplacementValues;
			mapExtraTagReplacementValues["$COMMONDATAPATH"] =
				techtreePath + "/commondata/";
			xmlTree.load(path,
				Properties::
				getTagReplacementValues
				(&mapExtraTagReplacementValues));
			loadedFileList[path].push_back(make_pair(currentPath, currentPath));

			const XmlNode *resourceNode = xmlTree.getRootNode();

			//image
			const XmlNode *imageNode = resourceNode->getChild("image");
			image = renderer.newTexture2D(rsGame);
			if (image) {
				image->load(imageNode->getAttribute("path")->
					getRestrictedValue(currentPath));
			}
			loadedFileList[imageNode->getAttribute("path")->
				getRestrictedValue(currentPath)].
				push_back(make_pair
				(sourceXMLFile,
					imageNode->getAttribute("path")->
					getRestrictedValue()));

			//type
			const XmlNode *typeNode = resourceNode->getChild("type");
			resourceClass =
				strToRc(typeNode->getAttribute("value")->getRestrictedValue());

			switch (resourceClass) {
				case rcTech:
				{
					//model
					const XmlNode *modelNode = typeNode->getChild("model");
					string modelPath =
						modelNode->getAttribute("path")->
						getRestrictedValue(currentPath);

					model =
						renderer.newModel(rsGame, modelPath, false, &loadedFileList,
							&sourceXMLFile);
					loadedFileList[modelPath].
						push_back(make_pair
						(sourceXMLFile,
							modelNode->getAttribute("path")->
							getRestrictedValue()));

					if (modelNode->hasChild("particles")) {
						const XmlNode *particleNode = modelNode->getChild("particles");
						bool particleEnabled =
							particleNode->getAttribute("value")->getBoolValue();
						if (particleEnabled == true) {
							for (int k = 0; k < (int) particleNode->getChildCount(); ++k) {
								const XmlNode *particleFileNode =
									particleNode->getChild("particle-file", k);
								string particlePath =
									particleFileNode->getAttribute("path")->
									getRestrictedValue();

								ObjectParticleSystemType *objectParticleSystemType =
									new ObjectParticleSystemType();
								objectParticleSystemType->load(particleFileNode, dir,
									currentPath + particlePath,
									&Renderer::getInstance(),
									loadedFileList,
									sourceXMLFile,
									techtreePath);
								loadedFileList[currentPath +
									particlePath].
									push_back(make_pair
									(sourceXMLFile,
										particleFileNode->getAttribute("path")->
										getRestrictedValue()));

								particleTypes.push_back(objectParticleSystemType);
							}
						}
					}

					//default resources
					const XmlNode *defaultAmountNode =
						typeNode->getChild("default-amount");
					defResPerPatch =
						defaultAmountNode->getAttribute("value")->getIntValue();

					//resource number
					const XmlNode *resourceNumberNode =
						typeNode->getChild("resource-number");
					resourceNumber =
						resourceNumberNode->getAttribute("value")->getIntValue();
				}
				break;

				case rcTileset:
				{
					//resource number
					const XmlNode *defaultAmountNode =
						typeNode->getChild("default-amount");
					defResPerPatch =
						defaultAmountNode->getAttribute("value")->getIntValue();

					//resource number
					const XmlNode *tilesetObjectNode =
						typeNode->getChild("tileset-object");
					tilesetObject =
						tilesetObjectNode->getAttribute("value")->getIntValue();
				}
				break;

				case rcConsumable:
				{
					//interval
					const XmlNode *intervalNode = typeNode->getChild("interval");
					interval = intervalNode->getAttribute("value")->getIntValue();
				}
				break;

				case rcStatic:
				{
					//recoup_cost
					if (typeNode->hasChild("recoup_cost") == true) {
						const XmlNode *recoup_costNode =
							typeNode->getChild("recoup_cost");
						if (recoup_costNode != NULL) {
							recoup_cost =
								recoup_costNode->getAttribute("value")->getBoolValue();
						}
					}
				}
				break;

				default:
					break;
			}

			//displayInHud
			if (resourceNode->hasChild("display") == true) {
				const XmlNode *displayNode = resourceNode->getChild("display");
				displayInHud = displayNode->getAttribute("value")->getBoolValue();
			} else {
				displayInHud = true;
			}
		} catch (game_runtime_error & ex) {
			SystemFlags::OutputDebug(SystemFlags::debugError,
				"In [%s::%s Line: %d] Error [%s]\n",
				extractFileFromDirectoryPath(__FILE__).
				c_str(), __FUNCTION__, __LINE__,
				ex.what());
			throw game_runtime_error("Error loading resource type: " +
				path + "\nMessage: " + ex.what(),
				!ex.wantStackTrace());
		} catch (const exception & e) {
			SystemFlags::OutputDebug(SystemFlags::debugError,
				"In [%s::%s Line: %d] Error [%s]\n",
				__FILE__, __FUNCTION__, __LINE__,
				e.what());
			throw game_runtime_error("Error loading resource type: " +
				path + "\n" + e.what());
		}
	}

	// ==================== misc ====================

	ResourceClass ResourceType::strToRc(const string & s) {
		if (s == "tech") {
			return rcTech;
		}
		if (s == "tileset") {
			return rcTileset;
		}
		if (s == "static") {
			return rcStatic;
		}
		if (s == "consumable") {
			return rcConsumable;
		}
		throw
			game_runtime_error
			("Error converting from string ro resourceClass, found: " + s);
	}

	void ResourceType::deletePixels() {
		if (model != NULL) {
			model->deletePixels();
		}
	}

	void ResourceType::saveGame(XmlNode * rootNode) {
		//DisplayableType::saveGame(rootNode);

		std::map < string, string > mapTagReplacements;
		XmlNode *resourceTypeNode = rootNode->addChild("ResourceType");

		resourceTypeNode->addAttribute("name", this->getName(),
			mapTagReplacements);
		//    ResourceClass resourceClass;
		resourceTypeNode->addAttribute("resourceClass",
			intToStr(resourceClass),
			mapTagReplacements);
		//    int tilesetObject;        //used only if class==rcTileset
		resourceTypeNode->addAttribute("tilesetObject",
			intToStr(tilesetObject),
			mapTagReplacements);
		//    int resourceNumber;       //used only if class==rcTech, resource number in the map
		resourceTypeNode->addAttribute("resourceNumber",
			intToStr(resourceNumber),
			mapTagReplacements);
		//    int interval;             //used only if class==rcConsumable
		resourceTypeNode->addAttribute("interval", intToStr(interval),
			mapTagReplacements);
		//      int defResPerPatch;     //used only if class==rcTileset || class==rcTech
		resourceTypeNode->addAttribute("defResPerPatch",
			intToStr(defResPerPatch),
			mapTagReplacements);
		//      bool recoup_cost;
		resourceTypeNode->addAttribute("recoup_cost", intToStr(recoup_cost),
			mapTagReplacements);
		//
		//    Model *model;
		if (model != NULL) {
			resourceTypeNode->addAttribute("model", model->getFileName(),
				mapTagReplacements);
		}
		//    ObjectParticleSystemTypes particleTypes;
		for (unsigned int i = 0; i < particleTypes.size(); ++i) {
			ObjectParticleSystemType *opst = particleTypes[i];
			opst->saveGame(resourceTypeNode);
		}
	}

} //end namespace
