#include "PhasmalistInventoryFunctions.h"
#include "skse64\GameExtraData.h"
#include "skse64\GameBSExtraData.h"
#include "skse64\GameRTTI.h"
#include "skse64\PapyrusWornObject.h"
#include "skse64\PapyrusEnchantment.h"
#include "skse64\PapyrusWeapon.h"
#include "skse64\PapyrusArmor.h"
#include "skse64\PapyrusBook.h"
#include "skse64\PapyrusSpell.h"
#include <string>

namespace PhasmalistScripts {

	static constexpr float magicWeight = 0.1;

	EntryDataList * getEntryData(TESObjectREFR *container) {
		ExtraContainerChanges * ecc = DYNAMIC_CAST
		(container->extraData.GetByType(kExtraData_ContainerChanges), BSExtraData, ExtraContainerChanges);
		if (!ecc) {
			return 0;
		}
		return ecc->data->objList;
	}

	float getMagicEffectStrength(EffectSetting * magicEffect, float magnitude, float area, float duration) {
		float result = magicEffect->properties.baseCost;
		if (magnitude > 0) {
			result *= magnitude;
		}
		if (area > 0) {
			result *= area;
		}
		if (duration > 0) {
			result *= duration;
		}
		return result;
	}

	float getEnchantmentStrength(EnchantmentItem * ench) {
		if (!ench) {
			return 0.0;
		}
		float result = 0;
		for (int i = papyrusEnchantment::GetNumEffects(ench) - 1; i >= 0; i--) {
			result += getMagicEffectStrength(
				papyrusEnchantment::GetNthEffectMagicEffect(ench, i),
				papyrusEnchantment::GetNthEffectMagnitude(ench, i),
				papyrusEnchantment::GetNthEffectArea(ench, i),
				papyrusEnchantment::GetNthEffectDuration(ench, i));
		}
		return result * magicWeight;
	}

	float getSpellStrength(SpellItem * spell) {
		if (!spell) {
			return 0.0;
		}
		float result = 0;
		for (int i = papyrusSpell::GetNumEffects(spell) - 1; i >= 0; i--) {
			result += getMagicEffectStrength(
				papyrusSpell::GetNthEffectMagicEffect(spell, i),
				papyrusSpell::GetNthEffectMagnitude(spell, i),
				papyrusSpell::GetNthEffectDuration(spell, i),
				papyrusSpell::GetNthEffectArea(spell, i));
		}
		return result * magicWeight;
	}

	float getAdditionalExtendDataStrength(InventoryEntryData * itemStack, float physicalStrength) {
		float strength = 0;
		for (int i = itemStack->extendDataList->Count() - 1; i >= 0; i--) {
			BaseExtraList * extraData = itemStack->extendDataList->GetNthItem(i);
			if (!extraData) {
				continue;
			}
			ExtraCount * exCount = DYNAMIC_CAST(extraData->GetByType(kExtraData_Count), BSExtraData, ExtraCount);
			int count = 1;
			if (exCount) {
				count = exCount->count;
			}
			if (extraData->HasType(kExtraData_Enchantment)) {
				//item has been enchanted
				EnchantmentItem * ench = referenceUtils::GetEnchantment(extraData);
				strength += getEnchantmentStrength(ench) * count;
			}
			if (extraData->HasType(kExtraData_Health)) {
				//item has been tempered
				ExtraHealth * health = DYNAMIC_CAST(extraData->GetByType(kExtraData_Health), BSExtraData, ExtraHealth);
				if (health) {
					strength += physicalStrength * (health->health - 1.0) * count;
				}
			}
		}
		return strength;
	}

	float getItemStackStrength(InventoryEntryData * itemStack) {
		float strength = 0;
		float physicalStrength = 0;

		if (itemStack->type->IsWeapon()) {
			TESObjectWEAP * asWeapon = static_cast<TESObjectWEAP*>(itemStack->type);
			if (asWeapon) {
				float baseDmg = papyrusWeapon::GetBaseDamage(asWeapon);
				float speed = papyrusWeapon::GetSpeed(asWeapon);
				physicalStrength = baseDmg * speed;
				strength += physicalStrength;
				strength += getEnchantmentStrength(papyrusWeapon::GetEnchantment(asWeapon));
			}
		}

		if (itemStack->type->IsArmor()) {
			TESObjectARMO * asArmor = static_cast<TESObjectARMO*>(itemStack->type);
			if (asArmor) {
				physicalStrength = papyrusArmor::GetArmorRating(asArmor);
				strength += physicalStrength;
				strength += getEnchantmentStrength(papyrusArmor::GetEnchantment(asArmor));
			}
		}

		if (itemStack->type->formType == kFormType_Book) {
			TESObjectBOOK * asBook = static_cast<TESObjectBOOK*>(itemStack->type);
			if (asBook) {
				SpellItem * spell = papyrusBook::GetSpell(asBook);
				return getSpellStrength(spell);
			}
		}

		strength = strength * itemStack->countDelta;

		strength += getAdditionalExtendDataStrength(itemStack, physicalStrength);

		return strength;
	}

	float calculateContentStrength(StaticFunctionTag * tag, TESObjectREFR *container) {
		if (!container) {
			return 0.0;
		}
		float strength = 0;
		for (int i = getEntryData(container)->Count() - 1; i >= 0; --i) {
			InventoryEntryData * ed = getEntryData(container)->GetNthItem(i);
			if (!ed) {
				continue;
			}
			strength += getItemStackStrength(ed);
		}
		return strength;
	}

	bool RegisterFuncs(VMClassRegistry* registry) {
		registry->RegisterFunction(
			new NativeFunction1<StaticFunctionTag, float, TESObjectREFR *>("calculateContentStrength", "EnderalLib", PhasmalistScripts::calculateContentStrength, registry));
		return true;
	}
}