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

#include <clasplayer.h>
#include "query/containers.h"

using namespace std;
using namespace xreate::containers;
using namespace xreate;

Implementation
Query::queryImplementation(xreate::Symbol const &s) {
    if (Attachments::exists<Implementation>(s))
    {
        return Attachments::get<Implementation>(s);
    }

    return  Implementation::create(s);
}


Query::Query(){
    Attachments::init<Implementation>();
}

void
Query::init(ClaspLayer* clasp)
{
    if (flagIsDataLoaded) return;

    map<Symbol, Symbol> prototypes;
    map<Symbol, string> roots;

        //read all proto data
    auto range = clasp->query(Config::get("containers.id.prototypes"));
    if (range)
    for(ClaspLayer::ModelIterator atom = range->first; atom != range->second; ++atom) {
        auto data =  ClaspLayer::parse<SymbolPacked, SymbolPacked>(atom->second);
        Symbol root = clasp->unpack(get<0> (data));
        Symbol prototype = clasp->unpack(get<1> (data));

        prototypes[root] = prototype;
    }

        // fill implementation data for a data sources:
    range = clasp->query(Config::get("containers.id.implementations"));
    if (range)
    for(ClaspLayer::ModelIterator atom = range->first; atom != range->second; ++atom)
    {
        auto data = ClaspLayer::parse<SymbolPacked, string>(atom->second);

        Symbol var  = clasp->unpack(get<0>(data));
        string implSerialized  = get<1>(data);

            //data source, has no prototypes:
        if (!prototypes.count(var))
        {
            Implementation impl = Implementation::create(var);
            Attachments::put<Implementation>(var, move(impl));
            continue;
        }

        roots.emplace(move(var), move(implSerialized));
    }

        //fill implementation data for a cluster roots
    for (const pair<Symbol, string> &  root: roots)
    {
        Symbol prototype = prototypes[root.first];

        while (prototypes.count(prototype))        {
            prototype = prototypes.at(prototype);
        }

        Attachments::put<Implementation>(root.first, Implementation(Attachments::get<Implementation>(prototype)));
    }

    // read cluster data and fill implementation data for cluster members
    range = clasp->query(Config::get("containers.id.clusters"));
    if (range)
    for(ClaspLayer::ModelIterator atom = range->first; atom != range->second; ++atom)
    {
        auto info = ClaspLayer::parse<SymbolPacked, SymbolPacked>(atom->second);

        Symbol root =  clasp->unpack(get<0>(info));
        Symbol child =  clasp->unpack(get<1>(info));

        if (!(child == root) && (Attachments::exists<Implementation>(root))) {
            Implementation rootImpl = Attachments::get<Implementation>(root);
            Attachments::put<Implementation>(child, move(rootImpl));
        }
    }

    flagIsDataLoaded = true;
}

//static ImplementationData* create(Symbol var, std::string implSerialized, const ImplementationData* implPrototype);

Implementation
Implementation::create(const Symbol &var)
{
    //TODO review implementation determination strategy
    Expression varDecl = CodeScope::getDeclaration(var);
    switch (varDecl.op)
    {
        case Operator::LIST_RANGE: {
            ImplementationRec<ON_THE_FLY> rec{var};
            return {ON_THE_FLY, rec};
        }

        case Operator::LIST: {
            return {SOLID, ImplementationRec<SOLID> {varDecl.getOperands().size()}};
        }

        default: break;
    };

    ImplementationLinkedList ill(var);
    if (ill){
    	return ill.getImplementationData();
    }

    assert(false && "Unable to determine proper implementation for the symbol");
}

Implementation
Implementation::create(const Symbol& var, const std::string& implSerialized)
{
    Expression varDecl = CodeScope::getDeclaration(var);
    if (implSerialized == Config::get("containers.impl.solid"))
    {
        return {SOLID, ImplementationRec<SOLID>{varDecl.operands.size()}};

    } else if (implSerialized == Config::get("containers.impl.onthefly")) {
        return {ON_THE_FLY, ImplementationRec<ON_THE_FLY>{var}};
    }

    assert(false && "unable to determine proper implementation for the symbol");
}


ImplementationLinkedList::ImplementationLinkedList(const Symbol& source)
        :    flagIsValid(false), s(source){

	const Expression& sourceExpr = CodeScope::getDeclaration(source);

	if (sourceExpr.tags.count(Config::get("containers.id.linkedlist"))){
        flagIsValid = true;

        Expression tagLinkedlist = sourceExpr.tags.at(Config::get("containers.id.linkedlist"));
		assert(tagLinkedlist.operands.size() == 2);
		fieldPointer = tagLinkedlist.operands.at(0).getValueString();
		terminator = tagLinkedlist.operands.at(1);
	}
}

ImplementationLinkedList:: operator bool () const{
    return flagIsValid;
}

Implementation
ImplementationLinkedList::getImplementationData() const {
	return {ON_THE_FLY, ImplementationRec<ON_THE_FLY>{s}};
}
