#pragma once

#include <Font.h>
#include <UIMLView.h>

#include "NetworkAPIProcessor.h"

class QuestGraphElement : public Framework::UIMLElement
{
public:
    QuestGraphElement();
    //! prüft, ob dieses UIML Element für ein bestimmtes xml Element zuständig
    //! ist
    bool isApplicableFor(Framework::XML::Element& element) override;
    //! erstellt eine neue Drawable zu einem gegebenen xml Element
    Framework::Drawable* parseElement(Framework::XML::Element& element,
        Framework::UIMLContainer& generalFactory) override;
    bool updateElement(Framework::XML::Element& element,
        Framework::Drawable& z,
        Framework::UIMLContainer& generalFactory) override;
    //! wendet die layout parameter zu einer Drawable an
    void layout(Framework::XML::Element& element,
        Framework::Drawable& z,
        int pWidth,
        int pHeight,
        Framework::UIMLContainer& generalLayouter) override;
};

class QuestGraphItemElement : public Framework::UIMLElement
{
public:
    QuestGraphItemElement();
    //! prüft, ob dieses UIML Element für ein bestimmtes xml Element zuständig
    //! ist
    bool isApplicableFor(Framework::XML::Element& element) override;
    //! erstellt eine neue Drawable zu einem gegebenen xml Element
    Framework::Drawable* parseElement(Framework::XML::Element& element,
        Framework::UIMLContainer& generalFactory) override;
    bool updateElement(Framework::XML::Element& element,
        Framework::Drawable& z,
        Framework::UIMLContainer& generalFactory) override;
    //! wendet die layout parameter zu einer Drawable an
    void layout(Framework::XML::Element& element,
        Framework::Drawable& z,
        int pWidth,
        int pHeight,
        Framework::UIMLContainer& generalLayouter) override;
};

class QuestGraphItem;

struct ConnectionInfo
{
    QuestGraphItem* target;
    int id;
};

class AndNode;

struct ConnectionTarget
{
    int targetIndex;
    AndNode* zTarget;
};

class Connection : public Framework::ReferenceCounter
{
private:
    QuestGraphItem* zSource;
    Framework::Array<ConnectionTarget> targets;
    int vx;
    int minY;
    int maxY;

public:
    Connection(QuestGraphItem* zSource);
    void renderVertical(Framework::Image& rObj);
    void renderHorizontal(Framework::Image& rObj);
    void setVx(int vx);
    void addConnectionTarget(ConnectionTarget target);
    void setTargetIndex(AndNode* zTarget, int index);
    void layout();
    bool isActive() const;
    int getOrderNum() const;
    int getTargetCount() const;
    int getVerticalLength() const;
    int getVx() const;
    bool hasTarget(AndNode* zAndNode, int index) const;
};

class AndNode : public Framework::DrawableBackground
{
private:
    Framework::Array<Connection*> connections;
    Framework::TextRenderer tr;

public:
    AndNode();
    void addConnection(Connection* zConnection);
    void render(Framework::Image& rObj);
    Framework::Point getTargetPosition(int index);
    void layout();
    bool isActive() const;
    int getConnectionCount() const;
};

class OrConnection : public Framework::ReferenceCounter
{
private:
    Framework::RCArray<AndNode> andNodes;
    QuestGraphItem* zTarget;
    int minY;
    int maxY;

public:
    OrConnection(QuestGraphItem* zTarget,
        const Framework::Array<ConnectionInfo>& connections);
    void render(Framework::Image& rObj);
    void layout();
    int getNeededHeight() const;
    bool isActive() const;
    bool isHorizontalLineConflict(
        int y, Connection* zIgnoredIncommingConnection) const;
};

class QuestGraphItem : public Framework::DrawableBackground
{
private:
    int layerIndex;
    int nodeIndex;
    Framework::Array<ConnectionInfo> previousLayersConnections;
    Framework::Array<ConnectionInfo> nextLayersConnections;
    Framework::Text id;
    Framework::Text requirements;
    Framework::Text onClick;
    Connection* outgoingConnection;
    OrConnection* incommingConnection;
    bool finished;
    bool rewarded;
    bool mainQuest;
    bool virtualNode;
    double animationProgress;

public:
    QuestGraphItem();
    ~QuestGraphItem();

    bool tick(double tickVal);
    void render(Framework::Image& rObj);
    void renderVerticalConnections(Framework::Image& rObj);
    void renderHorizontalConnections(Framework::Image& rObj);
    void replacePreviousConnections(
        QuestGraphItem* zBefore, QuestGraphItem* zAfter);
    const Framework::Text& getId() const;
    const Framework::Text& getRequirements() const;

    bool calculateLaxerIndex();
    int getLayerIndex() const;
    void setVirtual(bool virtualNode);
    void setNodeIndex(int index);

    void addNextLayerRef(ConnectionInfo zItem);
    void addPreviousLayerRef(ConnectionInfo zItem);
    void initializeConnections();

    const Framework::Array<ConnectionInfo>& getNextLayersConnections() const;
    int getNodeIndex() const;
    bool isMainQuest() const;
    bool isVirtual() const;
    bool isFinished() const;
    Connection* zOutgoingConnection() const;
    OrConnection* zIncommingConnection() const;

    friend QuestGraphItemElement;
};

class QuestGraphItemLayer : public Framework::ReferenceCounter
{
private:
    Framework::RCArray<QuestGraphItem> items;

public:
    QuestGraphItemLayer();

    bool tick(double tickVal);
    void render(Framework::Image& rObj);
    void doMouseEvent(Framework::MouseEvent& me);

    void addItem(QuestGraphItem* item);

    bool sortItems();
    int fixItemPositions(int x);
    void sortConnections();
    const Framework::RCArray<QuestGraphItem>& getItems() const;
    int getLeyerHeight(int& minY) const;
    int getLeyerHeight() const;
    void centerVertically(int pos);
    void resolveHorizontalConflicts(const QuestGraphItemLayer* zNextLayer);
    bool isHorizontalLineConflict(
        int y, Connection* zIgnoredIncommingConnection) const;
};

class QuestGraph : public Framework::DrawableBackground
{
private:
    Framework::Text id;
    Framework::RCArray<QuestGraphItemLayer> layers;
    QuestGraphItemLayer invalidNodes;
    Framework::Text collectionName;

public:
    QuestGraph();

    bool tick(double tickVal);
    void render(Framework::Image& rObj) override;
    void doMouseEvent(Framework::MouseEvent& me, bool userRet) override;

    void addVirtualConnectionNodes();
    void sortItems();
    void fixItemPositions();
    void centerVertically();
    void addItem(QuestGraphItem* item);

    friend QuestGraphElement;
};