/* 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/.
 *
 * aux.cpp
 *
 * Author: pgess <v.melnychenko@xreate.org>
 */

/**
 *  \file aux.h
 *  \brief Data representation in ASP format ready for use by reasoner
 */

#include "aux.h"
#include <boost/format.hpp>

namespace xreate { namespace analysis {

using namespace std;

list<string>
multiplyLists(list<list<string>> &&lists) {
    typedef list<string> StringList;
    assert(lists.size());
    StringList result(*lists.begin());
    lists.pop_front();

    boost::format concat("%s, %s");
    for (StringList &list: lists) {
        StringList::const_iterator end = result.end();
        for (StringList::iterator expr1I = result.begin(); expr1I != end; ++expr1I) {
            if (list.size() == 0) continue;

            StringList::const_iterator expr2I = list.begin();
            for (int expr2No = 0, size = list.size() - 1; expr2No < size; ++expr2No, ++expr1I)
                result.push_back(str(concat %(*expr1I) %(*expr2I)));

            *expr1I = str(concat %(*expr1I) %(*expr2I));
        }
    }

    return result;
}

std::list<std::string>
compile(const Expression &e){
    list<string> result;

    switch (e.op) {
        case Operator::CALL: {
            assert(e.__state == Expression::COMPOUND);

            if(!e.operands.size()){
                result.push_back(e.getValueString());
                break;
            }

            std::list<list<string>> operands;
            std::transform(e.operands.begin(), e.operands.end(), std::inserter(operands, operands.begin()),
                    [](const Expression &e) {
                        return compile(e);
                    });

            list<string> &&operands_ = multiplyLists(std::move(operands));
            result.push_back(boost::str(boost::format("%1%(%2%)") % (e.getValueString()) % (boost::algorithm::join(operands_, ", "))));
            break;
        }
        case Operator::NEG: {
            assert(e.operands.size() == 1);

            const Expression &op = e.operands.at(0);
            list<string> &&rawOp = compile(op);

            assert(rawOp.size() == 1);
            result.push_back((boost::format("not %1%")%(rawOp.front())).str());
            break;
        };

        case Operator::NONE: {
            switch (e.__state) {
                case Expression::IDENT:
                    result.push_back(e.getValueString());
                    break;

                case Expression::NUMBER:
                    result.push_back(to_string(e.getValueDouble()));
                    break;

                default:
                    assert(true);
            }
            break;
        }

        default: break;
    }

//TODO Null ad hoc ClaspLayer implementation
//        if (e.isNone()){
//             result.push_back(e.__valueS);
//        }

    assert(result.size());
    return result;
}

std::list<std::string>
compileNeg(const Expression &e){
    list<string> result;
    switch (e.op) {
        case Operator::IMPL: {
            assert(e.__state == Expression::COMPOUND);
            assert(e.operands.size() == 2);
            list<string> operands1 = compile(e.operands.at(0));
            list<string> operands2 = compile(e.operands.at(1));

            boost::format formatNeg("%1%, not %2%");
            for (const auto &op1: operands1)
                for (const auto &op2: operands2) {
                    result.push_back(boost::str(formatNeg %(op1) % (op2)));
                }
            break;
        }
        case Operator::NEG: {
            assert(e.operands.size() == 1);

            const Expression &op = e.operands.at(0);
            list<string> &&rawOp = compile(op);

            assert(rawOp.size() == 1);
            result.push_back(rawOp.front());
            break;
        };

        default:
            assert(true);
    }

    return result;
}

boost::format
formatSymbol(const SymbolPacked& s){
    boost::format formatSymbNamed("(%1%, %2%, %3%)");
    boost::format formatSymbAnonymous("anonym(%1%, %2%)");

    if (!s.categoryTransient){
        return formatSymbNamed % s.identifier % s.version % s.scope;
    } else {
        return formatSymbAnonymous % s.identifier % s.scope;
    }
}

}}
