#pragma once

#include "BasicItems.h"
#include "BlockFilter.h"
#include "Item.h"
#include "ItemSkill.h"

class BasicToolItemType;

class XPBasedLevelUpRule : public ItemSkillLevelUpRule
{
private:
    float xpIncrease;
    float xpMultiplier;
    float levelIncrease;
    float levelMultiplier;
    float maxLevel;

public:
    XPBasedLevelUpRule();
    virtual void applyOn(ItemSkill* zSkill) override;
    void setXpIncrease(float xpIncrease);
    float getXpIncrease() const;
    void setXpMultiplier(float xpMultiplier);
    float getXpMultiplier() const;
    void setLevelIncrease(float levelIncrease);
    float getLevelIncrease() const;
    void setLevelMultiplier(float levelMultiplier);
    float getLevelMultiplier() const;
    void setMaxLevel(float maxLevel);
    float getMaxLevel() const;
};

class XPBasedLevelUpRuleFactory
    : public SubTypeFactory<ItemSkillLevelUpRule, XPBasedLevelUpRule>
{
public:
    XPBasedLevelUpRuleFactory();
    XPBasedLevelUpRule* fromJson(
        Framework::JSON::JSONObject* zJson) const override;
    Framework::JSON::JSONObject* toJsonObject(
        XPBasedLevelUpRule* zObject) const override;
    JSONObjectValidationBuilder* addToValidator(
        JSONObjectValidationBuilder* builder) const override;
    const char* getTypeToken() const override;
};

class BasicToolItem : public Item
{
private:
    float headMaterialHardness;
    float rodMaterialHardness;
    float handleMaterialHardness;

public:
    BasicToolItem(
        int itemTypeId, Framework::Text name, float maxHp, float maxDurability);
    void setHeadMaterialHardness(float hardness);
    void setRodMaterialHardness(float hardness);
    void setHandleMaterialHardness(float hardness);
    float getHeadMaterialHardness() const;
    float getRodMaterialHardness() const;
    float getHandleMaterialHardness() const;
};

class BasicToolItemType : public ItemType
{
private:
    float headMaterialHardness;
    float rodMaterialHardness;
    float handleMaterialHardness;
    float baseDurability;
    float baseDurabilityMultiplier;
    float headMaterialDurability;
    float headMaterialDurabilityMultiplier;
    float rodMaterialDurability;
    float rodMaterialDurabilityMultiplier;
    float handleMaterialDurability;
    float handleMaterialDurabilityMultiplier;
    ItemSkillLevelUpRule* levelUpRule;
    int brokenItemTypeId;
    Framework::Text brokenItemTypeName;
    Framework::JSON::JSONObject* itemSkillConfigJson;

public:
    BasicToolItemType();
    ~BasicToolItemType();

protected:
    virtual void loadSuperItem(
        Item* zItem, Framework::StreamReader* zReader) const override;
    virtual void saveSuperItem(
        const Item* zItem, Framework::StreamWriter* zWriter) const override;

public:
    virtual bool initialize(Game* zGame) override;
    virtual const ItemType* zBrokenItemType() const override;
    virtual Item* createItem() const override;
    virtual void levelUpItemSkill(ItemSkill* zSkill) const override;
    virtual void setItemAttribute(Item* zItem,
        Framework::Text name,
        Framework::JSON::JSONValue* zValue) const override;
    virtual void addItemAttributes(
        Item* zItem, Framework::JSON::JSONObject* zItemObjet) const override;
    virtual ItemSkill* createDefaultItemSkill() const override;
    void setBrokenItemTypeName(Framework::Text brokenItemTypeName);
    Framework::Text getBrokenItemTypeName() const;
    void setHeadMaterialHardness(float hardness);
    float getHeadMaterialHardness() const;
    void setRodMaterialHardness(float hardness);
    float getRodMaterialHardness() const;
    void setHandleMaterialHardness(float hardness);
    float getHandleMaterialHardness() const;
    void setBaseDurability(float durability);
    float getBaseDurablility() const;
    void setBaseDurabilityMultiplier(float multiplier);
    float getBaseDurabilityMultiplier() const;
    void setHeadMaterialDurability(float durability);
    float getHeadMaterialDurability() const;
    void setHeadMaterialDurabilityMultiplier(float multiplier);
    float getHeadMaterialDurabilityMultiplier() const;
    void setRodMaterialDurability(float durability);
    float getRodMaterialDurability() const;
    void setRodMaterialDurabilityMultiplier(float multiplier);
    float getRodMaterialDurabilityMultiplier() const;
    void setHandleMaterialDurability(float durability);
    float getHandleMaterialDurability() const;
    void setHandleMaterialDurabilityMultiplier(float multiplier);
    float getHandleMaterialDurabilityMultiplier() const;
    void setLevelUpRule(ItemSkillLevelUpRule* rule);
    ItemSkillLevelUpRule* zLevelUpRule() const;
    void setItemSkillConfigJson(Framework::JSON::JSONObject* zJson);
    Framework::JSON::JSONObject* getItemSkillConfigJson() const;
};

class BasicToolItemTypeFactory : public ItemTypeFactoryBase<BasicToolItemType>
{
public:
    BasicToolItemTypeFactory();
    BasicToolItemType* createValue(
        Framework::JSON::JSONObject* zJson) const override;
    BasicToolItemType* fromJson(
        Framework::JSON::JSONObject* zJson) const override;
    Framework::JSON::JSONObject* toJsonObject(
        BasicToolItemType* zObject) const override;
    JSONObjectValidationBuilder* addToValidator(
        JSONObjectValidationBuilder* builder) const override;
    const char* getTypeToken() const override;
};

class BlockReplaceItemSkillConfig : public Framework::ReferenceCounter
{
private:
    BlockFilter* targetBlockFilter;
    int replacementBlockTypeId;
    int cooldownTicks;
    float staminaCost;
    float staminaCostDevider;
    float additionalStaminaCostDeviderPerLevel;
    float durabilityCost;
    float durabilityCostDevider;
    float additionalDurabilityCostDeviderPerLevel;
    float xpGain;

public:
    BlockReplaceItemSkillConfig();
    ~BlockReplaceItemSkillConfig();
    void setTargetBlockFilter(BlockFilter* targetBlockFilter);
    BlockFilter* zTargetBlockFilter() const;
    void setReplacementBlockTypeId(int replacementBlockTypeId);
    int getReplacementBlockTypeId() const;
    void setCooldownTicks(int cooldownTicks);
    int getCooldownTicks() const;
    void setStaminaCost(float staminaCost);
    float getStaminaCost() const;
    void setStaminaCostDevider(float staminaCostDevider);
    float getStaminaCostDevider() const;
    void setAdditionalStaminaCostDeviderPerLevel(
        float additionalStaminaCostDeviderPerLevel);
    float getAdditionalStaminaCostDeviderPerLevel() const;
    void setDurabilityCost(float durabilityCost);
    float getDurabilityCost() const;
    void setDurabilityCostDevider(float durabilityCostDevider);
    float getDurabilityCostDevider() const;
    void setAdditionalDurabilityCostDeviderPerLevel(
        float additionalDurabilityCostDeviderPerLevel);
    float getAdditionalDurabilityCostDeviderPerLevel() const;
    void setXpGain(float xpGain);
    float getXpGain() const;
};

class BlockReplaceItemSkillConfigFactory
    : public ObjectTypeFactory<BlockReplaceItemSkillConfig>
{
public:
    BlockReplaceItemSkillConfigFactory();
    BlockReplaceItemSkillConfig* fromJson(
        Framework::JSON::JSONObject* zJson) const override;
    Framework::JSON::JSONObject* toJsonObject(
        BlockReplaceItemSkillConfig* zObject) const override;
    JSONObjectValidationBuilder* addToValidator(
        JSONObjectValidationBuilder* builder) const override;
};

class BlockReplaceItemSkill : public ItemSkill
{
private:
    BlockReplaceItemSkillConfig* invalidUseConfig;
    Framework::RCArray<BlockReplaceItemSkillConfig> configs;
    int cooldownTicks;

public:
    BlockReplaceItemSkill();
    ~BlockReplaceItemSkill();
    virtual void load(Framework::StreamReader* zReader) override;
    virtual void save(Framework::StreamWriter* zWriter) override;
    virtual bool use(Entity* zActor, Item* zUsedItem, Block* zTarget) override;
    virtual bool use(Entity* zActor, Item* zUsedItem, Entity* zTarget) override;
    void setInvalidUseConfig(BlockReplaceItemSkillConfig* config);
    BlockReplaceItemSkillConfig* zInvalidUseConfig() const;
    void addConfig(BlockReplaceItemSkillConfig* config);
    const Framework::RCArray<BlockReplaceItemSkillConfig>& getConfigs() const;
    void setCooldownTicks(int cooldownTicks);
    int getCooldownTicks() const;
};

class BlockReplaceItemSkillFactory
    : public ItemSkillFactoryBase<BlockReplaceItemSkill>
{
public:
    BlockReplaceItemSkillFactory();
    BlockReplaceItemSkill* createValue(
        Framework::JSON::JSONObject* zJson) const override;
    BlockReplaceItemSkill* fromJson(
        Framework::JSON::JSONObject* zJson) const override;
    Framework::JSON::JSONObject* toJsonObject(
        BlockReplaceItemSkill* zObject) const override;
    JSONObjectValidationBuilder* addToValidator(
        JSONObjectValidationBuilder* builder) const override;
    const char* getTypeToken() const override;
};

class DamagingItemSkillConfig : public Framework::ReferenceCounter
{
private:
    BlockFilter* targetBlockFilter;
    float damage;
    float damagePerHeadHardness;
    float baseDamageMultiplier;
    float damageMultiplierPerHeadHardness;
    float damagePerLevel;
    float damageMultiplierPerLevel;
    float damageDevider;
    float damageDeviderPerHardness;
    float staminaCost;
    float staminaCostPerDamage;
    float staminaCostPerHardness;
    float staminaCostDevider;
    float staminaCostDeviderPerLevel;
    float durabilityCost;
    float durabilityCostPerDamage;
    float durabilityCostPerHardness;
    float durabilityCostDevider;
    float additionalDurabilityCostDeviderPerLevel;
    float xpGainPerDamage;

public:
    DamagingItemSkillConfig();
    ~DamagingItemSkillConfig();
    void setTargetBlockFilter(BlockFilter* targetBlockFilter);
    BlockFilter* zTargetBlockFilter() const;
    void setDamage(float damage);
    float getDamage() const;
    void setDamagePerHeadHardness(float damagePerHeadHardness);
    float getDamagePerHeadHardness() const;
    void setBaseDamageMultiplier(float baseDamageMultiplier);
    float getBaseDamageMultiplier() const;
    void setDamageMultiplierPerHeadHardness(
        float damageMupliplierPerHeadHardness);
    float getDamageMultiplierPerHeadHardness() const;
    void setDamagePerLevel(float damagePerLevel);
    float getDamagePerLevel() const;
    void setDamageMultiplierPerLevel(float damageMultiplierPerLevel);
    float getDamageMultiplierPerLevel() const;
    void setDamageDevider(float damageDevider);
    float getDamageDevider() const;
    void setDamageDeviderPerHardness(float damageDeviderPerHardness);
    float getDamageDeviderPerHardness() const;
    void setStaminaCost(float staminaCost);
    float getStaminaCost() const;
    void setStaminaCostPerDamage(float staminaCostPerDamage);
    float getStaminaCostPerDamage() const;
    void setStaminaCostPerHardness(float staminaCostPerHardness);
    float getStaminaCostPerHardness() const;
    void setStaminaCostDevider(float staminaCostDevider);
    float getStaminaCostDevider() const;
    void setStaminaCostDeviderPerLevel(float staminaCostDeviderPerLevel);
    float getStaminaCostDeviderPerLevel() const;
    void setDurabilityCost(float durabilityCost);
    float getDurabilityCost() const;
    void setDurabilityCostPerDamage(float durabilityCostPerDamage);
    float getDurabilityCostPerDamage() const;
    void setDurabilityCostPerHardness(float durabilityCostPerHardness);
    float getDurabilityCostPerHardness() const;
    void setDurabilityCostDevider(float durabilityCostDevider);
    float getDurabilityCostDevider() const;
    void setAdditionalDurabilityCostDeviderPerLevel(
        float additionalDurabilityCostDeviderPerLevel);
    float getAdditionalDurabilityCostDeviderPerLevel() const;
    void setXpGainPerDamage(float xpGainPerDamage);
    float getXpGainPerDamage() const;
};

class DamagingItemSkillConfigFactory
    : public ObjectTypeFactory<DamagingItemSkillConfig>
{
public:
    DamagingItemSkillConfigFactory();
    DamagingItemSkillConfig* fromJson(
        Framework::JSON::JSONObject* zJson) const override;
    Framework::JSON::JSONObject* toJsonObject(
        DamagingItemSkillConfig* zObject) const override;
    JSONObjectValidationBuilder* addToValidator(
        JSONObjectValidationBuilder* builder) const override;
};

class DamagingItemSkill : public ItemSkill
{
private:
    DamagingItemSkillConfig* invalidUseConfig;
    Framework::RCArray<DamagingItemSkillConfig> configs;

public:
    DamagingItemSkill();
    ~DamagingItemSkill();
    virtual bool use(Entity* zActor, Item* zUsedItem, Block* zTarget) override;
    virtual bool use(Entity* zActor, Item* zUsedItem, Entity* zTarget) override;
    void setInvalidUseConfig(DamagingItemSkillConfig* config);
    DamagingItemSkillConfig* zInvalidUseConfig() const;
    void addConfig(DamagingItemSkillConfig* config);
    const Framework::RCArray<DamagingItemSkillConfig>& getConfigs() const;
};

class DamagingItemSkillFactory : public ItemSkillFactoryBase<DamagingItemSkill>
{
public:
    DamagingItemSkillFactory();
    DamagingItemSkill* createValue(
        Framework::JSON::JSONObject* zJson) const override;
    DamagingItemSkill* fromJson(
        Framework::JSON::JSONObject* zJson) const override;
    Framework::JSON::JSONObject* toJsonObject(
        DamagingItemSkill* zObject) const override;
    JSONObjectValidationBuilder* addToValidator(
        JSONObjectValidationBuilder* builder) const override;
    const char* getTypeToken() const override;
};