/* 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/.
 * 
 * File:   PassManagerModular.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on June 22, 2017, 5:32 PM
 */

#ifndef PASSMANAGERMODULAR_H
#define PASSMANAGERMODULAR_H

#include "ast.h"
#include "modules.h"
#include "modules/Parser.h"
#include "main/Parser.h"

namespace xreate{namespace modules {
    
template<class Parent>
class XreateManagerDecoratorModules: public Parent{
public:
    XreateManagerDecoratorModules(): __registry(new ModulesRegistry()){}
    
    void prepareCode(std::string&& code) override {
        Scanner scannerModules(reinterpret_cast<const unsigned char*>(code.c_str()), code.size());
        std::list<std::string> listIncludedFiles;
        parseModulesGrammar(scannerModules, listIncludedFiles);
        
        grammar::main::Scanner scannerMain(reinterpret_cast<const unsigned char*>(code.c_str()), code.size());
        parseMainGrammar(scannerMain, listIncludedFiles);
    }
    
    void prepareCode(FILE* code) override {
        Scanner scannerModules(code);
        std::list<std::string> listIncludedFiles;
        parseModulesGrammar(scannerModules, listIncludedFiles);
        
        grammar::main::Scanner scannerMain(code);
        parseMainGrammar(scannerMain, listIncludedFiles);
    }
    
private:
    ModulesRegistry* __registry;

    void parseModulesGrammar(Scanner& scanner, std::list<std::string>& listIncludedFiles){
        ModulesSolver solver(__registry);

        Parser parser(&scanner);
        parser.Parse();
        parser.module.__path = "";

        solver.init("", parser.module);

        std::list<std::string> modulesExternal = solver.run(parser.module);
        std::string programBase = solver.__program.str();

        for (const std::string module: modulesExternal){
            parseModulesGrammar(module, programBase, listIncludedFiles);
        }
    }
    
    void parseModulesGrammar(const std::string& path, std::string base, std::list<std::string>& listIncludedFiles){
        FILE* input = fopen(path.c_str(), "r");
        assert(input != nullptr);
        Scanner scanner(input);
        Parser parser(&scanner);
        parser.Parse();
        parser.module.__path = path;
        fclose(input);

        ModulesSolver solver(__registry);
        solver.init(base, parser.module);

        std::list<std::string>&& modulesExternal = solver.run(parser.module);
        std::string programBase = solver.__program.str();

        for (const std::string module: modulesExternal){
            parseModulesGrammar(module, programBase, listIncludedFiles);
        }

        listIncludedFiles.push_back(path);
    }
    
    void parseMainGrammar(grammar::main::Scanner& scanner, std::list<std::string>& listIncludedFiles){
        details::incomplete::AST* ast = new AST;

        grammar::main::Parser parser(&scanner);
        parser.root = ast;
        parser.Parse();
        assert(!parser.errors->count && "Parser errors");

        for (auto file: listIncludedFiles){
            FILE* fileContent = fopen(file.c_str(), "r");
            grammar::main::Scanner scanner(fileContent);
            grammar::main::Parser parser(&scanner);
            parser.root = ast;
            parser.Parse();
            fclose(fileContent);
            
            assert(!parser.errors->count && "Parser errors");
        }

        PassManager::prepare(ast->finalize());
    }
};

}} //end namespace xreate::modules


#endif /* PASSMANAGERMODULAR_H */

