/*
 * CArtifactsOfHeroBase.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
 * License: GNU General Public License v2.0 or later
 * Full text of license available in license.txt file, in main folder
 *
 */
#include "StdInc.h"
#include "CArtifactsOfHeroBase.h"

#include "../gui/CGuiHandler.h"
#include "../gui/Shortcut.h"

#include "Buttons.h"

#include "../CPlayerInterface.h"
#include "../CGameInfo.h"

#include "../../CCallback.h"

#include "../../lib/ArtifactUtils.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/networkPacks/ArtifactLocation.h"

CArtifactsOfHeroBase::CArtifactsOfHeroBase()
	: curHero(nullptr)
{
}

void CArtifactsOfHeroBase::putBackPickedArtifact()
{
	// Artifact located in artifactsTransitionPos should be returned
	if(getPickedArtifact())
	{
		auto slot = ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId());
		if(slot == ArtifactPosition::PRE_FIRST)
		{
			LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
		}
		else
		{
			LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero->id, slot));
		}
	}
}

void CArtifactsOfHeroBase::init(
	const CArtPlace::ClickFunctor & onClickPressedCallback,
	const CArtPlace::ClickFunctor & onShowPopupCallback,
	const Point & position,
	const BpackScrollFunctor & scrollCallback)
{
	// CArtifactsOfHeroBase::init may be transform to CArtifactsOfHeroBase::CArtifactsOfHeroBase if OBJECT_CONSTRUCTION_CAPTURING is removed
	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
	pos += position;
	for(int g = 0; g < ArtifactPosition::BACKPACK_START; g++)
	{
		artWorn[ArtifactPosition(g)] = std::make_shared<CArtPlace>(slotPos[g]);
	}
	backpack.clear();
	for(int s = 0; s < 5; s++)
	{
		auto artPlace = std::make_shared<CArtPlace>(Point(403 + 46 * s, 365));
		backpack.push_back(artPlace);
	}
	for(auto artPlace : artWorn)
	{
		artPlace.second->slot = artPlace.first;
		artPlace.second->setArtifact(nullptr);
		artPlace.second->setClickPressedCallback(onClickPressedCallback);
		artPlace.second->setShowPopupCallback(onShowPopupCallback);
	}
	for(auto artPlace : backpack)
	{
		artPlace->setArtifact(nullptr);
		artPlace->setClickPressedCallback(onClickPressedCallback);
		artPlace->setShowPopupCallback(onShowPopupCallback);
	}
	leftBackpackRoll = std::make_shared<CButton>(Point(379, 364), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(),
		[scrollCallback](){scrollCallback(true);}, EShortcut::MOVE_LEFT);
	rightBackpackRoll = std::make_shared<CButton>(Point(632, 364), AnimationPath::builtin("hsbtns5.def"), CButton::tooltip(),
		[scrollCallback](){scrollCallback(false);}, EShortcut::MOVE_RIGHT);
	leftBackpackRoll->block(true);
	rightBackpackRoll->block(true);

	backpackScroller = std::make_shared<BackpackScroller>(this, Rect(380, 30, 278, 382));
	backpackScroller->setScrollingEnabled(false);

	setRedrawParent(true);
}

void CArtifactsOfHeroBase::clickPrassedArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
{
	if(clickPressedCallback)
		clickPressedCallback(*this, artPlace, cursorPosition);
}

void CArtifactsOfHeroBase::showPopupArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
{
	if(showPopupCallback)
		showPopupCallback(*this, artPlace, cursorPosition);
}

void CArtifactsOfHeroBase::gestureArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
{
	if(gestureCallback)
		gestureCallback(*this, artPlace, cursorPosition);
}

void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero)
{
	curHero = hero;

	for(auto slot : artWorn)
	{
		setSlotData(slot.second, slot.first);
	}
	updateBackpackSlots();
}

const CGHeroInstance * CArtifactsOfHeroBase::getHero() const
{
	return curHero;
}

void CArtifactsOfHeroBase::scrollBackpack(bool left)
{
	LOCPLINT->cb->scrollBackpackArtifacts(curHero->id, left);
}

void CArtifactsOfHeroBase::markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved)
{
	for(auto artPlace : artWorn)
		artPlace.second->selectSlot(art->canBePutAt(curHero, artPlace.second->slot, assumeDestRemoved));
}

void CArtifactsOfHeroBase::unmarkSlots()
{
	for(auto & artPlace : artWorn)
		artPlace.second->selectSlot(false);

	for(auto & artPlace : backpack)
		artPlace->selectSlot(false);
}

CArtifactsOfHeroBase::ArtPlacePtr CArtifactsOfHeroBase::getArtPlace(const ArtifactPosition & slot)
{
	if(ArtifactUtils::isSlotEquipment(slot))
	{
		if(artWorn.find(slot) == artWorn.end())
		{
			logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot);
			return nullptr;
		}
		return artWorn[slot];
	}
	if(ArtifactUtils::isSlotBackpack(slot))
	{
		for(ArtPlacePtr artPlace : backpack)
			if(artPlace->slot == slot)
				return artPlace;
		return nullptr;
	}
	else
	{
		return nullptr;
	}
}

void CArtifactsOfHeroBase::updateWornSlots()
{
	for(auto place : artWorn)
		updateSlot(place.first);
}

void CArtifactsOfHeroBase::updateBackpackSlots()
{
	ArtifactPosition slot = ArtifactPosition::BACKPACK_START;
	for(const auto & artPlace : backpack)
	{
		setSlotData(artPlace, slot);
		slot = slot + 1;
	}
	auto scrollingPossible = static_cast<int>(curHero->artifactsInBackpack.size()) > backpack.size();
	// Blocking scrolling if there is not enough artifacts to scroll
	if(leftBackpackRoll)
		leftBackpackRoll->block(!scrollingPossible);
	if(rightBackpackRoll)
		rightBackpackRoll->block(!scrollingPossible);
	if (backpackScroller)
		backpackScroller->setScrollingEnabled(scrollingPossible);
}

void CArtifactsOfHeroBase::updateSlot(const ArtifactPosition & slot)
{
	setSlotData(getArtPlace(slot), slot);
}

const CArtifactInstance * CArtifactsOfHeroBase::getPickedArtifact()
{
	// Returns only the picked up artifact. Not just highlighted like in the trading window.
	if(!curHero || curHero->artifactsTransitionPos.empty())
		return nullptr;
	else
		return curHero->getArt(ArtifactPosition::TRANSITION_POS);
}

void CArtifactsOfHeroBase::addGestureCallback(CArtPlace::ClickFunctor callback)
{
	for(auto & artPlace : artWorn)
	{
		artPlace.second->setGestureCallback(callback);
		artPlace.second->addUsedEvents(GESTURE);
	}
}

void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot)
{
	// Spurious call from artifactMoved in attempt to update hidden backpack slot
	if(!artPlace && ArtifactUtils::isSlotBackpack(slot))
	{
		return;
	}

	artPlace->slot = slot;
	if(auto slotInfo = curHero->getSlot(slot))
	{
		artPlace->lockSlot(slotInfo->locked);
		artPlace->setArtifact(slotInfo->artifact);
		if(slotInfo->locked || slotInfo->artifact->isCombined())
			return;

		// If the artifact is part of at least one combined artifact, add additional information
		std::map<const ArtifactID, std::vector<ArtifactID>> arts;
		for(const auto combinedArt : slotInfo->artifact->artType->getPartOf())
		{
			arts.try_emplace(combinedArt->getId(), std::vector<ArtifactID>{});
			for(const auto part : combinedArt->getConstituents())
			{
				if(curHero->hasArt(part->getId(), false, false, false))
					arts.at(combinedArt->getId()).emplace_back(part->getId());
			}
		}
		artPlace->addCombinedArtInfo(arts);
	}
	else
	{
		artPlace->setArtifact(nullptr);
	}
}

BackpackScroller::BackpackScroller(CArtifactsOfHeroBase * owner, const Rect & dimensions)
	: Scrollable(0, Point(), Orientation::HORIZONTAL)
	, owner(owner)
{
	pos = dimensions + pos.topLeft();
	setPanningStep(46);
}

void BackpackScroller::scrollBy(int distance)
{
	if (distance != 0)
		owner->scrollBackpack(distance < 0);
}
