/* 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/.
 *
 * modules.cpp
 *
 * Author: pgess <v.melnychenko@xreate.org>
 * Created on July 22, 2017, 5:13 PM
 */

/**
 * \file    modules.h
 * \brief   Modules support
 */

#include "modules.h"
#include "modules/Parser.h"
#include "analysis/utils.h"
#include <clingo/clingocontrol.hh>
#include <boost/format.hpp>
#include <regex>
#include <boost/filesystem.hpp>
#include <fstream>

using namespace std;
namespace fs = boost::filesystem;

namespace xreate { namespace modules{
void
ModuleRecord::addRequest(const Expression& request){
    __requests.push_back(request);
}

void
ModuleRecord::addControllerPath(const std::string& path){
    __controllers.push_back(path);
}

void
ModuleRecord::addDiscoveryPath(const std::string& path){
    __discoveryPaths.push_back(path);
}

void
ModuleRecord::addProperty(const Expression& prop){
    __properties.push_back(prop);
}

void
ModulesSolver::loadControllers(const ModuleRecord& module){
    for (const std::string& pathController: module.__controllers){
        std::fstream fileContent(pathController);
        __program << fileContent.rdbuf();
    }
}

void
ModulesSolver::extractProperties(const ModuleRecord& module){
//    const std::string atomProperty = "bind_module";
//    boost::format formatProperty(atomProperty + "(\"%1%\", %2%).");
//
//    for (const Expression& property: module.__properties){
//        std::list<std::string> reprProp = xreate::analysis::compile(property);
//        assert(reprProp.size()== 1);
//
//        __program  << (formatProperty % module.__path % reprProp.front())
//                << std::endl;
//    }
}

void
ModulesSolver::discoverModules(const ModuleRecord& moduleClient){
    std::regex extXreate("\\.xreate$", std::regex::basic);

    for(const std::string& path: moduleClient.__discoveryPaths){
        for(fs::directory_entry e: fs::recursive_directory_iterator(path)) {
            if (fs::is_regular_file(e.status())){
                if (!std::regex_search(e.path().string(), extXreate)) continue;

                FILE* script = fopen(e.path().c_str(), "r");
                Scanner scanner(script);
                Parser parser(&scanner);
                parser.Parse();
                assert(!parser.errors->count && "Discovery errors");
                parser.module.__path = e.path().c_str();

                extractProperties(parser.module);
                fclose(script);
            }
        }
    }
}

void
ModulesSolver::extractRequirements(const ModuleRecord& module){
//    const std::string atomQuery = "modules_require";
//    boost::format formatProperty(atomQuery + "(\"%1%\", %2%).");
//
//    for (const Expression& query: module.__requests){
//        std::list<std::string> reprQuery = xreate::analysis::compile(query);
//        assert(reprQuery.size()== 1);
//
//        __program  << (formatProperty % module.__path % reprQuery.front())
//                << std::endl;
//    }
}

void
ModulesSolver::add(const std::string& base){
    __program << base;
}

void
ModulesSolver::init(const std::string& programBase, const ModuleRecord& module){
    add(programBase);
    extractRequirements(module);
    extractProperties(module);
    loadControllers(module);
    discoverModules(module);

    std::cout << __program.str() << std::endl;
}

std::list<std::string>
ModulesSolver::run(const ModuleRecord& module){
    const std::string predicateResolution = "modules_resolution";

    std::map<Expression, string> dictResolution;
    std::vector<char const *> args{"clingo", nullptr};
    DefaultGringoModule moduleDefault;
    Gringo::Scripts scriptsDefault(moduleDefault);
    ClingoLib ctl(scriptsDefault, 0, args.data(), {}, 0);

    ctl.add("base", {}, __program.str());
    ctl.ground({{"base", {}}}, nullptr);
    ctl.solve([&predicateResolution, this, &dictResolution, &module](Gringo::Model const &model) {
        for (Gringo::Symbol atom : model.atoms(clingo_show_type_atoms)) {
            std::cout << atom << std::endl;
            if (std::strcmp(atom.name().c_str(), predicateResolution.c_str())!=0) continue;

            if (atom.arity() == 2){
                auto resolution = TranscendLayer::parse<Expression, std::string>(atom);
                dictResolution.emplace(get<0>(resolution), get<1>(resolution));
                continue;
            }

            if (atom.arity() == 3){
                auto resolution = TranscendLayer::parse<Expression, std::string, std::string>(atom);
                if(get<2>(resolution) != module.__path) continue;

                dictResolution.emplace(get<0>(resolution), get<1>(resolution));
                continue;
            }
            assert(false && "Wrong resolution format");
        }
        return true;
    }, {});

    std::list<std::string> result;
    for(const Expression& request: module.__requests){
        assert(dictResolution.count(request) && "Can't find requested module");
        result.push_back(dictResolution.at(request));
    }

    return result;
}

}} //namespace xreate::modules
