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

#ifndef _XREATE_ATTACHMENTS_H_
#define _XREATE_ATTACHMENTS_H_
#include <unordered_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 - 9);
        //reserved attachments:
//        1 containers::Implementation
//        3 interpretation::InterpretationData
//        5 interpretation::FunctionInterpretationData
//        6 VariableVersion
//        7 Symbol
//        8 versions::VersionImposedDependency
    };
    
    template<class Object>
    struct AttachmentsId{
        //static unsigned int getId(const Object& object);
    };

    
template<class Data>
class IAttachmentsContainer{
    protected:
        virtual bool  __exists(const unsigned int object)=0;
        virtual Data& __get(const unsigned int object)=0;
        virtual void  __put(const unsigned int object, Data data)=0;

    public:
        template<class Id>
        bool  exists(const Id& object){
            unsigned int id = AttachmentsId<Id>::getId(object);

            return __exists(id);
        }

        template<class Id>
        Data& get(const Id& object){
            unsigned int id = AttachmentsId<Id>::getId(object);

            return __get(id);
        }

        template<class Id>
        Data  get(const Id& object, const Data& dataDefault){
            unsigned int id = AttachmentsId<Id>::getId(object);

            if (! __exists(id)){
                return dataDefault;
            }

            return __get(id);
        }

        template<class Id>
        void put(const Id& object, Data data){
            unsigned int id = AttachmentsId<Id>::getId(object);

                __put(id, data);
        }

        virtual ~IAttachmentsContainer(){};
};
    
template<class Data>
class AttachmentsContainerDefault: public IAttachmentsContainer<Data>{
private:
    std::unordered_map<unsigned int, Data> __data;

    virtual bool  __exists(const unsigned int id){
        return __data.count(id);
    }

    virtual Data& __get(const unsigned int id){
        return __data.at(id);
    }

    virtual void __put(const unsigned int id, Data data){
        auto result = __data.emplace(id, data);
        assert(result.second);
    }
    
public:    
    std::unordered_map<unsigned int, Data>& getRawStorage() {
        return __data;
    }
};


class Attachments{
    private:
        static std::vector<void*> __storage;
        
        template<class Tag>
        using  Data = typename AttachmentsDict<Tag>::Data;
        
    public:
        template<class Tag, class Id>
        static bool exists(const Id& object) {
            assert(AttachmentsDict<Tag>::key < __storage.size());
            assert(__storage.at(AttachmentsDict<Tag>::key));
            
            IAttachmentsContainer<Data<Tag>>* self = reinterpret_cast<IAttachmentsContainer<Data<Tag>>*>(__storage.at(AttachmentsDict<Tag>::key));
            return self->exists<Id>(object);
        }
        
        template<class Tag, class Id>
        static Data<Tag>& get(const Id& object){
            assert(AttachmentsDict<Tag>::key < __storage.size());
            assert(__storage.at(AttachmentsDict<Tag>::key));
            
            IAttachmentsContainer<Data<Tag>>* self = reinterpret_cast<IAttachmentsContainer<Data<Tag>>*>(__storage.at(AttachmentsDict<Tag>::key));
            return self->get<Id>(object);
        }
            
        template<class Tag, class Id>
        static Data<Tag>  get(const Id& object, const Data<Tag>& dataDefault){
            assert(AttachmentsDict<Tag>::key < __storage.size());
            assert(__storage.at(AttachmentsDict<Tag>::key));
            
            IAttachmentsContainer<Data<Tag>>* self = reinterpret_cast<IAttachmentsContainer<Data<Tag>>*>(__storage.at(AttachmentsDict<Tag>::key));
            return self->get<Id>(object, dataDefault);
        }
            
        template<class Tag, class Id>
        static void put(const Id& object, Data<Tag> data){
            assert(AttachmentsDict<Tag>::key < __storage.size());
            assert(__storage.at(AttachmentsDict<Tag>::key));
            
            IAttachmentsContainer<Data<Tag>>* self = reinterpret_cast<IAttachmentsContainer<Data<Tag>>*>(__storage.at(AttachmentsDict<Tag>::key));
            self->put<Id>(object, data);
        }
        
        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 AttachmentsContainerDefault<Data<Tag>>();
        }
        
        template<class Tag>
        static void init(IAttachmentsContainer<Data<Tag>>* container){
            unsigned int keyStorage = AttachmentsDict<Tag>::key;
            if (keyStorage+1 > __storage.size()){
                __storage.resize(keyStorage + 1, nullptr);
            }
            
            __storage[keyStorage] = container;
        }
};

}

#endif //_XREATE_ATTACHMENTS_H_