#include "inventory.hpp"
#include "item_db.hpp"
#include "unit.hpp"
#include "utils/godot_macros.hpp"

void Inventory::_bind_methods() {
#define CLASSNAME Inventory
    GDPROPERTY_HINTED(weapon_id, gd::Variant::INT, gd::PROPERTY_HINT_ENUM, ItemDB::get_enum_hint());
    GDPROPERTY_HINTED(armour_id, gd::Variant::INT, gd::PROPERTY_HINT_ENUM, ItemDB::get_enum_hint());
    GDPROPERTY_HINTED(utility_id, gd::Variant::INT, gd::PROPERTY_HINT_ENUM, ItemDB::get_enum_hint());
    GDPROPERTY(inventory_inspector, gd::Variant::DICTIONARY);
}

void Inventory::_ready() {
    if(gd::Engine::get_singleton()->is_editor_hint())
        return;
    this->parent_unit = gd::Object::cast_to<Unit>(this->get_parent());
}

Stats Inventory::get_stats() const {
    Stats stats{};
    if(this->weapon != nullptr)  this->weapon->apply_stats(stats);
    if(this->utility != nullptr) this->utility->apply_stats(stats);
    if(this->armour != nullptr)  this->armour->apply_stats(stats);
    if(this->helmet != nullptr)  this->helmet->apply_stats(stats);
    return stats;
}

void Inventory::add_item(const Item *item, unsigned num) {
    if(!this->inventory.has(item))
        this->inventory.insert(item, num);
    else if(num > 0)
        this->inventory[item] += num;
}

unsigned Inventory::get_item_amount(Item const *item) {
    return this->inventory.has(item) ? this->inventory[item] : 0;
}

bool Inventory::has_item(Item const *item) {
    return this->inventory.has(item);
}

void Inventory::set_weapon(Item const *item) {
    this->weapon = item != nullptr && item->has_capability(ItemCapability::WeaponEquip) ? item : nullptr;
}

Item const *Inventory::get_weapon() const {
    return this->weapon;
}

void Inventory::set_armour(Item const *item) {
    this->armour = item != nullptr && item->has_capability(ItemCapability::ArmourEquip) ? item : nullptr;
}

Item const *Inventory::get_armour() const {
    return this->armour;
}

void Inventory::set_utility(Item const *item) {
    this->utility = item != nullptr && item->has_capability(ItemCapability::UtilityEquip) ? item : nullptr;
}

Item const *Inventory::get_utility() const {
    return this->utility;
}

void Inventory::set_weapon_id(int item_id) {
    this->set_weapon(ItemDB::get_item(item_id));
}

int Inventory::get_weapon_id() const {
    return this->weapon != nullptr ? this->weapon->get_id() : 0;
}

void Inventory::set_armour_id(int item_id) {
    this->set_armour(ItemDB::get_item(item_id));
}

int Inventory::get_armour_id() const {
    return this->armour != nullptr ? this->armour->get_id() : 0;
}

void Inventory::set_utility_id(int item_id) {
    this->set_utility(ItemDB::get_item(item_id));
}

int Inventory::get_utility_id() const {
    return this->utility != nullptr ? this->utility->get_id() : 0;
}

void Inventory::set_inventory_inspector(gd::Dictionary dict) {
    this->inventory.clear();
    gd::Array a{dict.keys()};
    for(int i{0}; i < a.size(); ++i) {
        gd::StringName const item_name{a[i]};
        unsigned const amount{dict[item_name]};
        Item const *item{ItemDB::get_item_by_name(item_name)};
        if(item != nullptr && amount > 0)
            this->inventory[item] = amount;
    }
}

gd::Dictionary Inventory::get_inventory_inspector() const {
    gd::Dictionary dict{};
    for(gd::KeyValue<Item const *, unsigned> const &item : this->inventory)
        dict[item.key->get_class()] = item.value;
    return dict;
}
