/* 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>
 *
 * containers.cpp
 * Created on 3/14/15.
 */

/**
 * \file    query/containers.h
 * \brief   Represents reasoner's solution on [Container implementations](/w/concepts/containers)
 */

#include <transcendlayer.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(TranscendLayer* transcend)
{
    if (flagDataIsLoaded) return;

    //Fill implementation data for a data sources:
    auto range = transcend->query(Config::get("containers.id.implementations"));
    if (range.size())
    for(auto atom: range)
    {
        auto data = TranscendLayer::parse<SymbolPacked, Gringo::Symbol>(atom.second);

        Symbol var  = transcend->unpack(get<0>(data));
        string implStr  = get<1>(data).name().c_str();

        if (implStr == Config::get("containers.impl.solid"))
        {
            auto size = TranscendLayer::parse<int>(get<1>(data));
            Attachments::put<Implementation>(var, {SOLID, ImplementationRec<SOLID>{get<0>(size)}});

        } else if (implStr == Config::get("containers.impl.onthefly")) {
            Attachments::put<Implementation>(var, {ON_THE_FLY, ImplementationRec<ON_THE_FLY>{var}});

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

    flagDataIsLoaded = true;
}

Implementation
Implementation::create(const Symbol &var)
{
    //TODO review implementation determination strategy
    Expression varDecl = CodeScope::getDefinition(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");
    return Implementation();
}

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

	const Expression& sourceExpr = CodeScope::getDefinition(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}};
}
