/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * 
 * Author: pgess <v.melnychenko@xreate.org>
 * File: attachments.h
 * Date: 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 (next vacant id - 12);
        
//        Defined attachments:
//-----------------------------------------------------        
//        1 containers::Implementation
//        3 interpretation::InterpretationData
//        5 interpretation::FunctionInterpretationData
//        6 VariableVersion
//        7 IdentifierSymbol
//        8 versions::VersionImposedDependency
//        9 SymbolAlias        
//        11 TypeInferred
    };
    
    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(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(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(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(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_
