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

#include "command_type.h"
#include "util.h"
#include "conversion.h"
#include "unit_type.h"
#include "faction.h"
#include "world.h"
#include "leak_dumper.h"

using namespace Shared::Util;

namespace Game {
	// =====================================================
	//      class Command
	// =====================================================
	Command::Command() :unitRef() {
		this->commandType = NULL;
		unitType = NULL;
		stateType = cst_None;
		stateValue = -1;
		unitCommandGroupId = -1;
	}

	Command::Command(const CommandType * ct, const Vec2i & pos) :unitRef() {
		//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] ct = [%p]\n",__FILE__,__FUNCTION__,__LINE__,ct);

		this->commandType = ct;
		this->pos = pos;
		this->originalPos = this->pos;
		unitType = NULL;
		stateType = cst_None;
		stateValue = -1;
		unitCommandGroupId = -1;
	}

	Command::Command(const CommandType * ct, Unit * unit) {
		this->commandType = ct;
		this->pos = Vec2i(0);
		this->originalPos = this->pos;
		this->unitRef = unit;
		unitType = NULL;
		if (unit != NULL) {
			//unit->resetHighlight(); is in gui now
			pos = unit->getCellPos();
		}
		stateType = cst_None;
		stateValue = -1;
		unitCommandGroupId = -1;
	}

	Command::Command(const CommandType * ct, const Vec2i & pos,
		const UnitType * unitType,
		CardinalDir facing) :unitRef() {
		this->commandType = ct;
		this->pos = pos;
		this->originalPos = this->pos;
		this->unitType = unitType;
		this->facing = facing;
		stateType = cst_None;
		stateValue = -1;
		unitCommandGroupId = -1;

		//if(this->unitType != NULL) {
		//      SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] unitType = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->unitType->toString().c_str());
		//}
	}

	int Command::getPriority() {
		if (this->commandType->commandTypeClass == ccAttack
			&& getUnit() == NULL) {
			return 5;               // attacks to the ground have low priority
		}
		return this->commandType->getTypePriority();
	}
	// =============== set ===============

	void Command::setCommandType(const CommandType * commandType) {
		this->commandType = commandType;
	}

	void Command::setPos(const Vec2i & pos) {
		this->pos = pos;
	}

	//void Command::setOriginalPos(const Vec2i &pos) {
	//     this->originalPos= pos;
	//}

	void Command::setPosToOriginalPos() {
		this->pos = this->originalPos;
	}

	void Command::setUnit(Unit * unit) {
		this->unitRef = unit;
	}

	std::string Command::toString(bool translatedValue) const {
		//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__);

		std::string result = "";
		if (commandType != NULL) {
			result =
				"commandType id = " + intToStr(commandType->getId()) +
				", desc = " + commandType->toString(translatedValue);
		} else {
			result = "commandType = NULL";
		}
		//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__);

		result +=
			", pos = " + pos.getString() + ", originalPos = " +
			originalPos.getString() + ", facing = " + intToStr(facing.asInt());

		//if(unitRef.getUnit() != NULL) {
		if (unitRef.getUnitId() >= 0) {
			//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__);

			result +=
				", unitRef.getUnit() id = " + intToStr(unitRef.getUnitId());

			// The code below causes a STACK OVERFLOW!
			//if(unitRef.getUnit() != NULL) {
			//      result += ", unitRef.getUnit() = " + unitRef.getUnit()->toString();
			//}
		}

		//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__);

		if (unitType != NULL) {
			result += ", unitTypeId = " + intToStr(unitType->getId());
			result +=
				", unitTypeDesc = " + unitType->getReqDesc(translatedValue);
		}

		//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__);

		result +=
			", stateType = " + intToStr(stateType) + ", stateValue = " +
			intToStr(stateValue);


		result += ", unitCommandGroupId = " + intToStr(unitCommandGroupId);

		return result;
	}

	Checksum Command::getCRC() {
		Checksum crcForCmd;

		crcForCmd.addInt(commandType->getId());
		crcForCmd.addInt(originalPos.x);
		crcForCmd.addInt(originalPos.y);
		crcForCmd.addInt(pos.x);
		crcForCmd.addInt(pos.y);
		crcForCmd.addInt(unitRef.getUnitId());
		crcForCmd.addInt(facing);
		if (unitType != NULL) {
			crcForCmd.addInt(unitType->getId());
		}
		crcForCmd.addInt(stateType);
		crcForCmd.addInt(stateValue);
		crcForCmd.addInt(unitCommandGroupId);

		return crcForCmd;
	}

	void Command::saveGame(XmlNode * rootNode, Faction * faction) {
		std::map < string, string > mapTagReplacements;
		XmlNode *commandNode = rootNode->addChild("Command");

		//    const CommandType *commandType;
		if (commandType != NULL) {
			commandNode->addAttribute("commandType",
				intToStr(commandType->getId()),
				mapTagReplacements);
		}
		//    Vec2i originalPos;
		commandNode->addAttribute("originalPos", originalPos.getString(),
			mapTagReplacements);
		//    Vec2i pos;
		commandNode->addAttribute("pos", pos.getString(), mapTagReplacements);
		//      UnitReference unitRef;          //target unit, used to move and attack optionally
		unitRef.saveGame(commandNode);
		//      CardinalDir facing;                     // facing, for build command
		commandNode->addAttribute("facing", intToStr(facing),
			mapTagReplacements);
		//      const UnitType *unitType;       //used for build
		if (unitType != NULL) {
			commandNode->addAttribute("unitTypeId",
				intToStr(unitType->getId()),
				mapTagReplacements);
			commandNode->addAttribute("unitTypeFactionIndex",
				intToStr(faction->getIndex()),
				mapTagReplacements);
		}
		//      CommandStateType stateType;
		commandNode->addAttribute("stateType", intToStr(stateType),
			mapTagReplacements);
		//      int stateValue;
		commandNode->addAttribute("stateValue", intToStr(stateValue),
			mapTagReplacements);
		//      int unitCommandGroupId;
		commandNode->addAttribute("unitCommandGroupId",
			intToStr(unitCommandGroupId),
			mapTagReplacements);
	}

	Command *Command::loadGame(const XmlNode * rootNode, const UnitType * ut,
		World * world) {
		Command *result = new Command();
		const XmlNode *commandNode = rootNode;

		//description = commandNode->getAttribute("description")->getValue();

		//    const CommandType *commandType;
		if (commandNode->hasAttribute("commandType") == true) {
			int cmdTypeId =
				commandNode->getAttribute("commandType")->getIntValue();
			result->commandType = ut->findCommandTypeById(cmdTypeId);
		}
		//    Vec2i originalPos;
		result->originalPos =
			Vec2i::strToVec2(commandNode->getAttribute("originalPos")->
				getValue());
		//    Vec2i pos;
		result->pos =
			Vec2i::strToVec2(commandNode->getAttribute("pos")->getValue());
		//      UnitReference unitRef;          //target unit, used to move and attack optionally
		result->unitRef.loadGame(commandNode, world);
		//      CardinalDir facing;                     // facing, for build command
		result->facing =
			static_cast <CardinalDir>
			(commandNode->getAttribute("facing")->getIntValue());
		//      const UnitType *unitType;       //used for build
		if (commandNode->hasAttribute("unitTypeId") == true) {
			//result->unitType = ut;
			int unitTypeId =
				commandNode->getAttribute("unitTypeId")->getIntValue();
			int unitTypeFactionIndex =
				commandNode->getAttribute("unitTypeFactionIndex")->getIntValue();
			Faction *faction = world->getFaction(unitTypeFactionIndex);
			result->unitType =
				world->findUnitTypeById(faction->getType(), unitTypeId);
		}
		//      CommandStateType stateType;
		result->stateType =
			static_cast <CommandStateType>
			(commandNode->getAttribute("stateType")->getIntValue());
		//      int stateValue;
		result->stateValue =
			commandNode->getAttribute("stateValue")->getIntValue();
		//      int unitCommandGroupId;
		result->unitCommandGroupId =
			commandNode->getAttribute("unitCommandGroupId")->getIntValue();

		return result;
	}
} //end namespace
