/*
 * 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>
 * Created on June 15, 2018, 5:32 PM
 *
 */

#include "compilation/interpretation-instructions.h"
#include "analysis/typeinference.h"
#include "transcendlayer.h"
#include "targets.h"

using namespace std;

namespace xreate{
namespace interpretation{

Expression
IntrinsicQueryInstruction::representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT) {
    TranscendLayer* transcend = static_cast<TargetInterpretation*> (__scope->function->man)->pass->man->transcend;

    switch(schemaT->__operator) {
    case TypeOperator::NONE:
    {
        switch(schemaT->__value) {
        case TypePrimitive::I8:
        case TypePrimitive::I32:
        case TypePrimitive::I64:
        case TypePrimitive::Num:
        case TypePrimitive::Int:
        {
            return Expression(Atom<Number_t>(atom.num()));
        }

        case TypePrimitive::String:
        {
            return Expression(Atom<String_t>(atom.string().c_str()));
        }

        case TypePrimitive::Invalid:
        case TypePrimitive::Bool:
        case TypePrimitive::Float:
        {
            assert(false);
            return Expression();
        }
        }
        break;
    }

    case TypeOperator::SLAVE:
    {
        ExpandedType contentT = typeinference::dereferenceSlaveType(schemaT, transcend);
        return representTransExpression(atom, contentT);
    }

    case TypeOperator::VARIANT:
    {
        map<string, int> dictVariants;
        for(size_t variantId = 0; variantId < schemaT->fields.size(); ++variantId) {
            dictVariants.emplace(schemaT->fields.at(variantId), variantId);
        }

        string predicateName = atom.name().c_str();
        assert(dictVariants.count(predicateName));

        size_t predicateId = dictVariants.at(predicateName);
        Expression result(Operator::VARIANT,{});
        result.op = Operator::VARIANT;
        result.setValueDouble(predicateId);

        if(!schemaT->__operands.size()) return result;
        ExpandedType contentT = schemaT->__operands.at(0).__operator == TypeOperator::SLAVE
                ? typeinference::dereferenceSlaveType(ExpandedType(schemaT->__operands.at(0)), transcend)
                : ExpandedType(schemaT->__operands.at(0));

        //edge case, content's type is LIST_NAMED:
        if (contentT->__operator == TypeOperator::LIST_NAMED) {
            result.operands.push_back(representTransExpression(atom, contentT));

        } else {
            assert(atom.args().size);
            result.operands.push_back(representTransExpression(atom.args()[0], contentT));
        }

        return result;
    }

    case TypeOperator::LIST_NAMED:
    {
        const Gringo::SymSpan& operandsRaw = atom.args();
        size_t opCount = operandsRaw.size;
        assert(opCount == schemaT->__operands.size());

        size_t operandId = 0;
        std::vector<Expression> operands;
        operands.reserve(opCount);
        for(const TypeAnnotation operandT : schemaT->__operands) {
            operands.push_back(representTransExpression(operandsRaw[operandId], ExpandedType(operandT)));
            ++operandId;
        }

        Expression result(Operator::LIST_NAMED,{});
        result.operands = operands;
        result.type = schemaT;
        return result;
    }

    case TypeOperator::LIST:
    case TypeOperator::CALL:
    case TypeOperator::CUSTOM:
    case TypeOperator::ACCESS:
    case TypeOperator::LINK:
    {
        assert(false);
        return Expression();
    }
    }

    assert(false);
    return Expression();
}

Expression
IntrinsicQueryInstruction::process(const Expression& expression) {
    AST* ast = static_cast<TargetInterpretation*> (__scope->function->man)->ast;
    TranscendLayer* transcend = static_cast<TargetInterpretation*> (__scope->function->man)->pass->man->transcend;
    InterpretationFunction* function = static_cast<InterpretationFunction*> (__scope->function);

    ExpandedType targetT = ast->expandType(expression.type);
    assert(expression.operands.size() == 1);
    assert(expression.operands.front().__state == Expression::STRING);
    assert(targetT->__operator == TypeOperator::LIST);

    std::string namePredicate = expression.operands.front().getValueString();
    StaticModel model = (static_cast<TargetInterpretation*> (function->man))->pass->man->transcend->query(namePredicate);

    Expression result(Operator::LIST,{});
    result.operands.reserve(model.size());

    ExpandedType elementT = targetT->__operands.at(0).__operator == TypeOperator::SLAVE
            ? typeinference::dereferenceSlaveType(ExpandedType(targetT->__operands.at(0)), transcend)
            : ExpandedType(targetT->__operands.at(0));

    if(model.size()) {
        if (elementT->__operator == TypeOperator::LIST_NAMED) {
            //edge case, content's type is LIST_NAMED:
            for(const auto& row : model) {
                result.operands.push_back(representTransExpression(row.second, elementT));
            }
        } else {
            for (const auto& row : model) {
                assert(row.second.args().size);
                result.operands.push_back(representTransExpression(row.second.args()[0], elementT));
            }
        }
    }

    return result;
}
}
} //end of xreate::interpretation