#pragma once

#include "Array.h"

namespace Framework
{
    template<class T> class TreeIterator;

    template<class T> class Tree : public virtual ReferenceCounter
    {
    private:
        T* value;
        RCArray<Tree>* subtrees;
        Tree* zParent;

    public:
        Tree()
            : ReferenceCounter()
        {
            value = 0;
            zParent = 0;
            subtrees = new RCArray<Tree>();
        }

        Tree(T* value)
            : ReferenceCounter()
        {
            zParent = 0;
            this->value = value;
            subtrees = new RCArray<Tree>();
        }

        ~Tree()
        {
            if (value) value->release();
            subtrees->release();
        }

        TreeIterator<T> getIterator()
        {
            return TreeIterator(this);
        }

        void leeren()
        {
            if (value) value->release();
            value = 0;
            subtrees->leeren();
        }

        void addSubtree(Tree* t)
        {
            t->zParent = this;
            subtrees->add(t);
        }

        void setValue(T* v)
        {
            if (value) value->release();
            value = v;
        }

        T* getValue()
        {
            return value ? dynamic_cast<T*>(value->getThis()) : 0;
        }

        T* zValue()
        {
            return value;
        }

        Tree* getParent()
        {
            return zParent ? dynamic_cast<Tree*>(zParent->getThis()) : 0;
        }

        Tree* zParent()
        {
            return zParent;
        }

        friend TreeIterator;
    };

    template<class T> class TreeIterator
    {
    private:
        Tree* current;
        int nextSub;

    public:
        TreeIterator(Tree* tree)
        {
            current = tree;
            nextSub = 0;
        }

        TreeIterator(const TreeIterator& it)
        {
            current = it.current;
            nextSub = it.nextSub;
        }

        TreeIterator<T>& operator=(TreeIterator<T>& r)
        {
            current = r.current;
            nextSub = it.nextSub;
            return *this;
        }

        bool hasNext()
        {
            if (current->subtrees->getEintragAnzahl() >= nextSub) return 1;
            Tree<T>* c = current;
            while (true)
            {
                Tree<T>* p = c->zParent();
                if (!p)
                {
                    return 0;
                }
                for (auto i = p->subtrees->getIterator(); i; i++)
                {
                    if (i._ == c)
                    {
                        i++;
                        if (i)
                            return 1;
                        else
                        {
                            c = p;
                            break;
                        }
                    }
                }
            }
        }

        TreeIterator<T> next()
        {
            if (current->subtrees->getEintragAnzahl() >= nextSub)
                return TreeIterator(current->subtrees->z(nextSub++));
            Tree<T>* c = current;
            while (true)
            {
                Tree<T>* p = c->zParent();
                if (!p) return TreeIterator<T>(0);
                for (auto i = p->subtrees->getIterator(); i; i++)
                {
                    if (i._ == c)
                    {
                        i++;
                        if (i)
                            return TreeIterator<T>(i._);
                        else
                        {
                            c = p;
                            break;
                        }
                    }
                }
            }
        }

        operator bool()
        {
            return current != 0;
        }

        operator T*()
        {
            return current->zValue();
        }

        TreeIterator<T>& operator++() //! prefix
        {
            TreeIterator<T> temp(*this);
            *this = next();
            return temp;
        }

        TreeIterator<T>& operator++(int) //! postfix
        {
            *this = next();
            return *this;
        }

        T* operator->()
        {
            return current->zValue();
        }

        T val()
        {
            return current->zValue();
        }
    };
} // namespace Framework