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

#ifndef _XREATE_ATTACHMENTS_H_
#define _XREATE_ATTACHMENTS_H_
#include <map>
#include <vector>
#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 - 8);
    };
    
    template<class Object>
    struct AttachmentsId{
        //static unsigned int getId(const Object& object);
    };

                    //TODO add specialization for pointers
    template<class Data>
    class AttachmentsStorage
    {
    public:
        void put(unsigned int id, Data data)
        {
            __data.emplace(id, data);
        }

        Data&  get(unsigned int id)
        {
            return __data.at(id);
        }
        
        Data get(unsigned int id, Data dataDefault)
        {
            if (! exists(id)){
                return dataDefault;
            }
            
            return __data.at(id);
        }

        bool exists(unsigned int id)
        {
            return __data.count(id);
        }

    private:
        std::map<unsigned int, Data> __data;
    };
    
    class Attachments{
    public:
        template<class Tag>
        using  Data = typename AttachmentsDict<Tag>::Data;

        template<class Tag, class Id>
        static Data<Tag>&  get(const Id& object)
        {
            unsigned int id = AttachmentsId<Id>::getId(object);
            AttachmentsStorage<Data<Tag>>* self = reinterpret_cast<AttachmentsStorage<Data<Tag>>*>(__storage[AttachmentsDict<Tag>::key]);

            return self->exists(id);
        }
                
        template<class Tag, class Id>
        static Data<Tag>&  get(const Id& object)
        {
            unsigned int id = AttachmentsId<Id>::getId(object);
            AttachmentsStorage<Data<Tag>>* self = reinterpret_cast<AttachmentsStorage<Data<Tag>>*>(__storage[AttachmentsDict<Tag>::key]);

            return self->get(id);
        }
        
        template<class Tag, class Id>
        static Data<Tag>  get(const Id& object, Data<Tag> dataDefault)
        {
            unsigned int id = AttachmentsId<Id>::getId(object);
            AttachmentsStorage<Data<Tag>>* self = reinterpret_cast<AttachmentsStorage<Data<Tag>>*>(__storage[AttachmentsDict<Tag>::key]);

            return self->get(id, dataDefault);
        }
        
        template<class Tag, class Id>
        static void put(const Id& object, Data<Tag> data)
        {
            unsigned int id = AttachmentsId<Id>::getId(object);
            AttachmentsStorage<Data<Tag>>* self = reinterpret_cast<AttachmentsStorage<Data<Tag>>*>(__storage[AttachmentsDict<Tag>::key]);

            self->put(id, std::move(data));
        }
        
        template<class Tag, class Id>
        static void put(const Id& object)
        {
            unsigned int id = AttachmentsId<Id>::getId(object);
            AttachmentsStorage<Data<Tag>>* self = reinterpret_cast<AttachmentsStorage<Data<Tag>>*>(__storage[AttachmentsDict<Tag>::key]);

            return self->exists(id);
        }
        
        template<class Tag>
        static void init(){
            unsigned int keyStorage = AttachmentsDict<Tag>::key;
            if (keyStorage+1 > __storage.size()){
                __storage.resize(keyStorage + 1, nullptr);
            }
            
            __storage[keyStorage] = new AttachmentsStorage<Data<Tag>>();
        }
        
    private:
        static std::vector<void*> __storage;
    };
}

#endif //_XREATE_ATTACHMENTS_H_