#include "stdafx.h"
#include "Thinker.h"
#include <algorithm>

std::vector<Thinker::ItemMemory>::iterator Thinker::FindLeastValueItem(const eItemType& itemType) {
    auto minIt = m_ItemMemory.end();

    for (auto it = m_ItemMemory.begin(); it != m_ItemMemory.end(); ++it) {
        if (it->ItemInfo.Type != itemType)
            continue;

        if (minIt == m_ItemMemory.end() || it->ItemInfo.Value < minIt->ItemInfo.Value) {
            minIt = it;
        }
    }

    return minIt;
}

bool Thinker::IsInvNotFull() const {
	return m_ItemMemory.capacity() < m_MaxStorageSlots;
}

bool Thinker::IsItemInInv(const eItemType& itemType) {
	return std::any_of(std::begin(m_ItemMemory), std::end(m_ItemMemory),
		[itemType](const ItemMemory& memory)->bool { return memory.ItemInfo.Type == itemType && memory.ItemInfo.Value > 0; });
}

bool Thinker::EmptyValue() {
	return std::any_of(std::begin(m_ItemMemory), std::end(m_ItemMemory),
		[](const ItemMemory& memory)->bool { return memory.ItemInfo.Value <= 0; });
}


int Thinker::FindEmptyValue(const ItemInfo& item) {
	const auto foundItem = std::find_if(std::begin(m_ItemMemory), std::end(m_ItemMemory),
		[item](const ItemMemory& memory)->bool { return memory.ItemInfo.Type == item.Type; });
	if (foundItem != std::end(m_ItemMemory)) {
		return foundItem->invIndex;
	}
	return -1;
}

int Thinker::AddItemToMemory(const ItemInfo& item) {
	if (m_ItemMemory.capacity() >= m_MaxStorageSlots)
		return -1;
	ItemMemory newItem{};
	newItem.ItemInfo = item;
	newItem.invIndex = static_cast<int>(m_ItemMemory.size());
	m_ItemMemory.push_back(newItem);
	return newItem.invIndex;
}

int Thinker::CheckItem(const ItemInfo& item) {
	if (std::ranges::any_of(m_ItemMemory,
		[item](const ItemMemory& memory)->bool { return memory.ItemInfo.Type == item.Type; })) {
		const auto minItem{ FindLeastValueItem(item.Type) };

		if (minItem->ItemInfo.Value <= item.Value) {
			minItem->ItemInfo = item;
			return minItem->invIndex;
		}
		else {
			return m_MaxStorageSlots - 1;
		}
	}
	else {
		std::ranges::sort(m_ItemMemory,
			[](const ItemMemory& lhs, const ItemMemory& rhs)->bool { return lhs.ItemInfo.Type < rhs.ItemInfo.Type; });

		const auto duplicate =
			std::ranges::adjacent_find(m_ItemMemory,
				[](const ItemMemory& lhs, const ItemMemory& rhs)->bool { return lhs.ItemInfo.Type == rhs.ItemInfo.Type; });

		const auto minItem =
			std::min_element(duplicate, duplicate + 1,
				[](const ItemMemory& lhs, const ItemMemory& rhs)->bool { return lhs.ItemInfo.Value < rhs.ItemInfo.Value; });

		minItem->ItemInfo = item;
		return minItem->invIndex;
	}
}

bool Thinker::CheckIfTargetIsInside(const HouseInfo& targetHouse, Elite::Vector2 playerPos) {
	const float houseOffset{ 5.f };
	const Elite::Vector2 houseCenter{ targetHouse.Center };
	return (playerPos.x >= houseCenter.x - houseOffset && playerPos.x <= houseCenter.x + houseOffset &&
		playerPos.y >= houseCenter.y - houseOffset && playerPos.y <= houseCenter.y + houseOffset);
}

bool Thinker::CheckIfTargetIsExplored(Elite::Vector2 target, float offset) const {
	return std::any_of(std::begin(m_HousesMemory), std::end(m_HousesMemory),
		[target, offset](const HouseMemory& memory)->bool {
		return memory.info.Center.x >= target.x - offset && memory.info.Center.x <= target.x + offset &&
			memory.info.Center.y >= target.y - offset && memory.info.Center.y <= target.y + offset;
	});
}


bool Thinker::NewHouseToExplore() {
	if (m_HousesMemory.capacity() != 0) {
		if (std::ranges::any_of(m_HousesMemory,
			[](const HouseMemory& houseMemory)->bool { return houseMemory.newHouse == true; })) {
			return true;
		}
	}
	return false;
}

bool Thinker::HouseToReExplore() {
	if (m_HousesMemory.capacity() != 0) {
		const std::chrono::steady_clock::time_point currentTime{ std::chrono::steady_clock::now() };

		if (std::ranges::any_of(m_HousesMemory,
			[=](const HouseMemory& houseMemory)->bool {
			const std::chrono::duration<float> elapsedSec{ currentTime - houseMemory.lastSaw };
			return elapsedSec.count() >= m_MaxWaitTimer;
		})) {
			return true;
		}
	}
	return false;
}

void Thinker::SetTargetHouseExpireDate(const HouseInfo& targetHouse) {
	assert(m_HousesMemory.capacity() != 0);

	const auto foundHouse = FindHouseInMemory(targetHouse);

	foundHouse->lastSaw = std::chrono::steady_clock::now();
	foundHouse->newHouse = false;
}

HouseInfo Thinker::CheckHouseValidTarget(Elite::Vector2 playerPos, float maxRadius) const {
	HouseInfo targetHouse{};
	float closestHouse{ FLT_MAX };

	for (auto house : m_HousesMemory) {
		const float houseDistance{ house.info.Center.DistanceSquared(playerPos) };

		if (houseDistance > maxRadius * maxRadius)
			continue;

		if (closestHouse < houseDistance)
			continue;

		if (house.newHouse == true) {
			targetHouse = house.info;
			closestHouse = houseDistance;
		}
		else {
			const std::chrono::steady_clock::time_point currentTime{ std::chrono::steady_clock::now() };
			const std::chrono::duration<float> elapsedSec{ currentTime - house.lastSaw };

			if (elapsedSec.count() < m_MaxWaitTimer)
				continue;

			targetHouse = house.info;
			closestHouse = houseDistance;
		}
	}

	return targetHouse;
}

bool Thinker::CheckHousesForMemory(const std::vector<HouseInfo>& FOVHouses) {
	bool result{};

	for (auto& newHouse : FOVHouses) {
		if (m_HousesMemory.capacity() != 0) {
			if (std::ranges::any_of(m_HousesMemory,
				[newHouse](const HouseMemory& houseMemory)->bool { return houseMemory.info.Center == newHouse.Center; })) {
				continue;
			}
		}

		HouseMemory houseToMemory{};
		houseToMemory.info = newHouse;

		m_HousesMemory.push_back(houseToMemory);

		result = true;
	}

	return result;
}

std::vector<Thinker::HouseMemory>::iterator Thinker::FindHouseInMemory(const HouseInfo& targetHouse) {
	return
		std::ranges::find_if(m_HousesMemory,
			[targetHouse](const HouseMemory& houseMemory)->bool { return houseMemory.info.Center == targetHouse.Center; });
}
