#pragma once

#include <JSON.h>

#include "QuestEvent.h"
#include "QuestRequirement.h"
#include "QuestReward.h"
#include "TypeRegistry.h"

class QuestRequirementStorageType;

class QuestRequirementStorage : public virtual Framework::ReferenceCounter
{
private:
    Framework::Text requirementId;
    bool fulfilled;

public:
    QuestRequirementStorage();

    void setFullfilled(bool fullfilled);
    bool isFullfilled() const;
    void setRequirementId(Framework::Text requirementId);
    const Framework::Text& getRequirementId() const;

    friend QuestRequirementStorageType;
};

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

class QuestStorageType;

class QuestStorage : public virtual Framework::ReferenceCounter
{
private:
    Framework::Text questId;
    bool finished;
    bool rewarded;
    bool visible;
    Framework::RCArray<QuestRequirementStorage> requirements;
    Framework::JSON::JSONObject* data;

public:
    QuestStorage();
    ~QuestStorage();

    void setQuestFinished(bool finished);
    void setQuestRewarded(bool rewarded);
    bool isQuestFinished() const;
    bool isQuestRewarded() const;
    QuestRequirementStorage* zStorage(Framework::Text requirementId);
    void setQuestId(Framework::Text questId);
    const Framework::Text& getQuestId() const;
    bool isVisible() const;
    void setVisible(bool visible);
    void putValue(Framework::Text key, Framework::JSON::JSONValue* value);
    Framework::JSON::JSONValue* getValue(Framework::Text key) const;
    Framework::JSON::JSONValue* zValue(Framework::Text key) const;
    bool containsKey(Framework::Text key) const;

    friend QuestStorageType;
};

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

class QuestPartyType;

class QuestParty : public virtual Framework::ReferenceCounter
{
private:
    Framework::Array<int> memberEntityIds;
    Framework::RCArray<QuestStorage> questStorage;

public:
    QuestParty();

    void addMember(int memberEntityId);
    void removeMember(int memberEntityId);
    bool isMember(int memberEntityId) const;
    bool isEmpty() const;
    QuestStorage* zQuestStorage(Framework::Text questId);
    QuestParty* clone() const;

    friend QuestPartyType;
};

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

class QuestType;
class QuestManager;
class QuestDialog;

class Quest : public virtual Framework::ReferenceCounter
{
private:
    Framework::Text questId;
    Framework::Text questName;
    Framework::Text description;
    Framework::Text imagePath;
    bool mainQuest;
    Framework::RCArray<Framework::Text> requiredQuestsIds;
    Framework::Array<int> requiredQuestsGroups;
    Framework::RCArray<QuestRequirement> requirements;
    Framework::RCArray<QuestReward> rewards;

public:
    Quest();

    void processEvent(QuestEvent* zEvent, QuestStorage* zStorage);
    bool isVisible(QuestParty* zParty, QuestManager* zManager);
    bool isActive(QuestParty* zParty);
    void setQuestId(Framework::Text questId);
    const Framework::Text& getQuestId() const;
    void setVisible(bool visible, QuestParty* zParty, QuestManager* zManager);

    friend QuestType;
    friend QuestDialog;
};

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

class QuestCollectionType;

class QuestCollection : public virtual Framework::ReferenceCounter
{
private:
    Framework::Text name;
    Framework::RCArray<Quest> quests;

public:
    QuestCollection();

    void processEvent(QuestEvent* zEvent, QuestParty* zParty);
    bool isVisible(QuestParty* zParty, QuestManager* zManager);
    void addQuest(Quest* zQuest);

    void setName(Framework::Text name);
    const Framework::Text& getName() const;
    void setQuestVisible(bool visible,
        Framework::Text questId,
        QuestParty* zParty,
        QuestManager* zManager);

    friend QuestCollectionType;
    friend QuestDialog;
};

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

class QuestManager : public virtual Framework::ReferenceCounter
{
private:
    Framework::RCArray<QuestCollection> questCollections;
    Framework::RCArray<QuestParty> parties;

    QuestParty* zParty(int entityId);
    QuestCollection* zCollection(Framework::Text collectionName);

public:
    QuestManager();

    void loadQuests();
    void saveQuests();

    void processEvent(QuestEvent* event);
    void setQuestVisible(
        bool visible, Framework::Text questId, QuestParty* zParty);

    friend QuestDialog;
};