//
// Created by pgess on 3/15/15.
//

#ifndef _XREATE_ATTACHMENTS_H_
#define _XREATE_ATTACHMENTS_H_
#include <map>

namespace xreate
{
        //Attachemnt Tags:
    struct IsDeclVisited{};

        //Atachments dictionary
    template<class Tag>
    struct AttachmentsDictionary
    {
        // typedef void Data;
    };

    template<>
    struct AttachmentsDictionary<IsDeclVisited>
    {
        typedef bool Data;
        static const unsigned int key = 0;
    };



    class Symbol;
    typedef unsigned int VID;

    class AttachmentsImpl
    {
        friend class SymbolAttachments;

    };

        //TODO copy function to copy whole data from symbol to symbol: copy(sTo, sFrom);
    class SymbolAttachments
    {
    public:
                //TODO add specialization for pointers
        template<class Tag>
        static void put(const Symbol& symbol, typename AttachmentsDictionary<Tag>::Data && data)
        {
            typedef typename AttachmentsDictionary<Tag>::Data Typ;
            const unsigned int key = AttachmentsDictionary<Tag>::key;
            Typ* ptr = new Typ(data);

            put(symbol, key, ptr);
        }

        /*
        template<class Tag>
        using Tag2 = std::enable_if<std::is_pointer<Tag>, Tag>::
        static void put(const Symbol& symbol, typename AttachmentsDictionary<Tag>::Data && data)
        {
            typedef typename AttachmentsDictionary<Tag>::Data Typ;
            Typ* ptr = new Typ(data);

            AttachmentsImpl::put(symbol, ptr);
        }
        */

        template<class Tag>
        static typename AttachmentsDictionary<Tag>::Data& get(const Symbol& symbol, typename AttachmentsDictionary<Tag>::Data&& valueDefault)
        {
            typedef typename AttachmentsDictionary<Tag>::Data Typ;
            const unsigned int key = AttachmentsDictionary<Tag>::key;

            Typ* def = new Typ(valueDefault);
            Typ* result = static_cast<Typ*> (get(symbol, key, def));

            if (result != def) delete def;
            return *result;
        }

        template<class Tag>
        static typename AttachmentsDictionary<Tag>::Data& get(const Symbol& symbol)
        {
            typedef typename AttachmentsDictionary<Tag>::Data Typ;
            const unsigned int key = AttachmentsDictionary<Tag>::key;

            Typ* result = static_cast<Typ*> (get(symbol, key, nullptr));

            assert(result != nullptr); // data not found for the symbol
            return *result;
        }

        template<class Tag>
        static bool exists(const Symbol& symbol)
        {
            const unsigned int key = AttachmentsDictionary<Tag>::key;
            return exists(symbol, key);
        }

    private:
        typedef std::map<VID, void*> Attachment;
        std::map<unsigned int, Attachment> __data;

        static void put(const Symbol& symbol, unsigned int key, void* data);
        static void* get(const Symbol& symbol, unsigned int key,  void *def);
        static bool exists(const Symbol& symbol, unsigned int key);

    };
}

#endif //_XREATE_ATTACHMENTS_H_
