#ifndef TreeElements_hpp
#define TreeElements_hpp

#include <string>
#include <vector>
#include <map>

#include "TreeInterfaces.hpp"

namespace apoc { namespace legacy {

class StructuralNode ;
class PropertyNode ;
class Node ;

struct NodeVisitor
{
    virtual bool preChildrenProcess(Node& aNode) = 0 ;
    virtual bool postChildrenProcess(Node& aNode) = 0 ;
} ;

class Node 
{
    public :
        explicit Node(const std::string& aName) ;
        virtual ~Node(void) {}

        virtual StructuralNode *asStructuralNode(void) { return NULL ; }
        virtual PropertyNode *asPropertyNode(void) { return NULL ; }
        
        const std::string& getName(void) const { return mName ; }
        int getAttributes(void) const { return mAttributes ; }
        void setAttributes(int aAttributes) { mAttributes = aAttributes ; }
        void modifyAttributes(int aModification) ;
        StructuralNode *getParent(void) const { return mParent ; }
        void setParent(StructuralNode *aParent) { mParent = aParent ; }

        bool isReadOnly(void) const { return checkAttribute(ReadOnlyFlag) ; }
        bool isFinalised(void) const { return checkAttribute(FinalisedFlag) ; }
        bool isUnfinalised(void) const 
        {
            return checkAttribute(UnfinalisedFlag) ; 
        }
        bool checkAttribute(TreeFlags aFlag) const
        {
            return (mAttributes & aFlag) != 0 ;
        }
        
        virtual Node *clone(bool aDeep) const = 0 ;
        virtual void visit(NodeVisitor& aVisitor) = 0 ;
    protected :
    private :
        StructuralNode *mParent ;
        std::string mName ;
        int mAttributes ;
} ;

class StructuralNode : public Node
{
    public :
        explicit StructuralNode(const std::string& aName) : Node(aName) {}
        virtual ~StructuralNode(void) ;

        virtual StructuralNode *asStructuralNode(void) { return this ; }

        typedef std::map<std::string, Node *> ChildrenMap ;

        const ChildrenMap& getChildren(void) const { return mChildren ; }
        void getChildren(std::vector<std::string>& aChildren) const ;
        Node *getChild(const std::string& aName) const ;
        void adoptChild(Node& aNode) ;
        void removeChild(const std::string& aName) ;

        virtual Node *clone(bool aDeep) const ;
        virtual void visit(NodeVisitor& aVisitor) ;
    protected :
    private :
        ChildrenMap mChildren ;
} ;

class PropertyNode : public Node
{
    public :
        PropertyNode(const std::string& aName, PropertyType aType) ;
        virtual ~PropertyNode(void) ;

        virtual PropertyNode *asPropertyNode(void) { return this ; }

        const PropertyType& getType(void) const { return mType ; }
        bool isList(void) const ;
        bool isLocalised(void) const { return checkAttribute(LocalisedFlag) ; }
        void getLocales(std::vector<std::string>& aLocales) const ;
        void modifyLocalised(bool aLocalised) ;

        const std::string& getStringValue(
                            const std::string& aLocale = kDefaultLocale) const ;
        const std::vector<std::string>& getListValue(
                            const std::string& aLocale = kDefaultLocale) const ;
        void setStringValue(const std::string& aValue, 
                            const std::string& aLocale = kDefaultLocale) ;
        void setListValue(const std::vector<std::string>& aValue, 
                          const std::string& aLocale = kDefaultLocale) ;
        void addListValues(const std::vector<std::string>& aValues, 
                           const std::string& aLocale = kDefaultLocale) ;

        virtual Node *clone(bool aDeep) const ;
        virtual void visit(NodeVisitor& aVisitor) ;
    protected :
    private :
        PropertyType mType ;
        union Value
        {
            std::string *asString ;
            std::vector<std::string> *asList ;
            std::map<std::string, Value *> *asLocalised ;
        } mValue ;

        Value *findBestValue(const std::string& aLocale) const ;
} ;

class Tree
{
    public :
        explicit Tree(Node *aRoot) : mRoot(aRoot) {}
        Tree(const Tree& aTree) ;
        ~Tree(void) { delete mRoot ; }

        Node *getRoot(void) const { return mRoot ; }
        void subtract(const Tree& aTree, ITreeHandler& aHandler) const ;
        void serialize(const std::string& aFileName) const;
        void unserialize(const std::string& aFileName);
    protected :
    private :
        Node *mRoot ;
} ;

} } // apoc.legacy

#endif // TreeElements_hpp

