#pragma once

#include "Array.h"
#include "Text.h"

namespace Framework
{
    enum class AbstractType
    {
        NULL_,
        BOOLEAN,
        NUMBER,
        STRING,
        ARRAY,
        OBJECT
    };

    class AbstractBool;
    class AbstractNumber;
    class AbstractString;
    class AbstractArray;
    class AbstractObject;

    class AbstractElement : public virtual ReferenceCounter
    {
    public:
        virtual AbstractType getType() const = 0;
        virtual const AbstractBool* asAbstractBool() const = 0;
        virtual const AbstractNumber* asAbstractNumber() const = 0;
        virtual const AbstractString* asAbstractString() const = 0;
        virtual const AbstractArray* asAbstractArray() const = 0;
        virtual const AbstractObject* asAbstractObject() const = 0;
        virtual Text toString() const = 0;
    };

    class AbstractBool : public virtual AbstractElement
    {
    public:
        virtual bool getBool() const = 0;
    };

    class AbstractNumber : public virtual AbstractElement
    {
    public:
        virtual double getNumber() const = 0;
    };

    class AbstractString : public virtual AbstractElement
    {
    public:
        virtual Text getString() const = 0;
    };

    class AbstractArray : public virtual AbstractElement
    {
    public:
        virtual AbstractElement* zAbstractValue(int i) const = 0;
        virtual int getLength() const = 0;
    };

    class AbstractObject : public virtual AbstractElement
    {
    public:
        virtual AbstractElement* zAbstractValue(Text field) const = 0;
        virtual int getFieldCount() const = 0;
        virtual Text getFieldKey(int i) const = 0;
        virtual AbstractElement* zAbstractValue(int i) const = 0;
        virtual bool hasValue(Text field) const = 0;
    };

    class ElementPathBuilder;

    class ElementPath : public virtual ReferenceCounter
    {
    private:
        RCArray<Text>* path;
        int index;

        __declspec(dllexport) ElementPath(RCArray<Text>* path);

    public:
        __declspec(dllexport) ~ElementPath();

        __declspec(dllexport) bool isValid() const;
        __declspec(dllexport) bool hasNext() const;
        __declspec(dllexport) void next();
        __declspec(dllexport) bool isObjectAttribute() const;
        __declspec(dllexport) bool isArrayElement() const;
        __declspec(dllexport) int getArrayIndex() const;
        __declspec(dllexport) Text getObjectAttribute() const;
        __declspec(dllexport) Text toString() const;
        __declspec(dllexport) int getCurrentPathElementIndex() const;
        __declspec(dllexport) void setCurrentPathElementIndex(int index);

        friend ElementPathBuilder;
    };

    class ElementPathBuilder
    {
    private:
        RCArray<Text>* path;

    public:
        __declspec(dllexport) ElementPathBuilder();
        __declspec(dllexport) ElementPathBuilder(
            const ElementPathBuilder& other);
        __declspec(dllexport) ~ElementPathBuilder();
        __declspec(dllexport) ElementPathBuilder& appendArrayElement(int index);
        __declspec(dllexport) ElementPathBuilder& appendObjectElement(Text key);
        __declspec(dllexport) ElementPathBuilder& prependArrayElement(
            int index);
        __declspec(dllexport) ElementPathBuilder& prependObjectElement(
            Text key);
        __declspec(dllexport) ElementPath* build();
        __declspec(dllexport) ElementPathBuilder& operator=(
            const ElementPathBuilder& other);
    };
} // namespace Framework