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

#ifndef _XREATE_ATTACHMENTS_H_
#define _XREATE_ATTACHMENTS_H_
#include <map>
#include <assert.h>
#include <type_traits>

namespace xreate
{
        //Attachments dictionary
    template<class Tag>
    struct AttachmentsDict
    {
        // typedef void Data;
        // static const unsigned int key (current unreserved - 6);
    };
    
    template<class T>
    struct AttachmentsStorage
    {
        //static Attachments* get(const T&);
    };

    namespace detail {

        template<class Typ>
        typename std::enable_if<std::is_pointer<Typ>::value, void*>::type
        __wrap(const Typ& value){
            return value;
        }
        
        template<class Typ>
        typename std::enable_if<std::is_pointer<Typ>::value, Typ>::type
        __unwrap(void* value){
            return reinterpret_cast<Typ>(value);
        }
        
        template<class Typ>
        typename std::enable_if<! std::is_pointer<Typ>::value, void*>::type
        __wrap(const Typ& value){
            Typ* holder = new Typ(value);
            return holder;
        }
        
        template<class Typ>
        typename std::enable_if<! std::is_pointer<Typ>::value, Typ&>::type
        __unwrap(void* value){
            return *reinterpret_cast<Typ*>(value);
        }

        template<class Typ>
        typename std::enable_if<std::is_pointer<Typ>::value, void>::type
        __delete(void* value){
            delete reinterpret_cast<Typ>(value);
        }
        
        template<class Typ>
        typename std::enable_if<! std::is_pointer<Typ>::value, void>::type
        __delete(void* value){
            delete reinterpret_cast<Typ*>(value);
        }
    }
    
        //TODO copy whole data from symbol to symbol: copy(sTo, sFrom);
    class Attachments
    {
    public:
                //TODO add specialization for pointers
        template<class Tag>
        using  Data = typename AttachmentsDict<Tag>::Data;

        template<class Holder, class Tag>
        static void put(const Holder& holder, const Data<Tag>& data)
        {
            const unsigned int key = AttachmentsDict<Tag>::key;
            Attachments* self = AttachmentsStorage<Holder>::get(holder);

            void* dataWaste = self->put(key, detail::__wrap(data));
            detail::__delete<Data<Tag>>(dataWaste);
        }

        template<class Holder, class Tag>
        static Data<Tag>&  get(const Holder& holder)
        {
            const unsigned int key = AttachmentsDict<Tag>::key;
            Attachments* self = AttachmentsStorage<Holder>::get(holder);

            return detail::__unwrap<Data<Tag>>(self->get(key));
        }
        
        template<class Holder, class Tag>
        static Data<Tag>  get(const Holder& holder, Data<Tag>&& dataDefault)
        {
            if (! exists<Holder, Tag>(holder)){
                return dataDefault;
            }
            
            const unsigned int key = AttachmentsDict<Tag>::key;
            Attachments* self = AttachmentsStorage<Holder>::get(holder);

            return detail::__unwrap<Data<Tag>>(self->get(key));
        }

        template<class Holder, class Tag>
        static bool exists(const Holder& holder)
        {
            const unsigned int key = AttachmentsDict<Tag>::key;
            Attachments* self = AttachmentsStorage<Holder>::get(holder);
            return self->exists(key);
        }

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

        void* put(unsigned int key, void *data);
        void* get(unsigned int key);
        bool exists(unsigned int key);
    };
}

#endif //_XREATE_ATTACHMENTS_H_
