#include "Lexesis/automata.h"
#include "Lexesis/inputparser.h"
#include "Lexesis/re.h"

#include <istream>
#include <string>
#include <iostream>
#include <memory>
#include <exception>

namespace lxs {

    InputParserException::InputParserException(std::string what): m_what(what) {} 
    const char* InputParserException::what() const throw() {
            return m_what.c_str();
        }

    DFA InputParser::parseInput(std::istream &is) {
        auto enfavect = parseLines(is);
        auto enfas = linesToEnfa(enfavect);
        auto enfa = merge(enfas);
        auto dfa = minimize(mssc(enfa));
        return dfa;
    }

    std::vector<std::pair<int, std::pair<std::string,std::string> > > InputParser::parseLines(std::istream &is) {
        std::string line;
        std::vector<std::pair<int, std::pair<std::string,std::string> > > result;
        unsigned int i=0;
        while(std::getline(is,line)) {
            i++;
            size_t start = line.find_first_not_of(" \t\v\f\r");
            if(line.length() == 0 || (start != std::string::npos && line.length() > start && line[start] == '#')) continue;
            std::size_t loc = line.find_first_of('=');
            if(loc == std::string::npos) throw InputParserException("Invalid syntax on line " + std::to_string(i) + ": no '=' found!");
            if(start == loc) throw InputParserException("Invalid syntax on line " + std::to_string(i) + ": no valid tokenname specified!");
            std::string tokenname = line.substr(start, loc);
            if(tokenname.length() == 0) throw InputParserException("Invalid syntax on line " + std::to_string(i) + ": no valid tokenname specified!");
            std::size_t end = tokenname.find_last_not_of(" \t\v\f\r");
            tokenname = tokenname.substr(0,end + 1);
            std::string regex = line.substr(loc+1);
            if(regex.length() == 0) throw InputParserException("Invalid syntax on line " + std::to_string(i) + ": no valid regex specified!");
            start = regex.find_first_not_of(" \t\v\f\r");
            if(start == std::string::npos) throw InputParserException("Invalid syntax on line " + std::to_string(i) + ": no valid regex specified!");
            regex = regex.substr(start);
            result.push_back(std::make_pair(i,std::make_pair(tokenname,regex)));
        }
        if(result.size() == 0) throw InputParserException("No valid rules found in the input file!");
        return result;
    }

    std::vector<ENFA> InputParser::linesToEnfa(std::vector<std::pair<int, std::pair<std::string,std::string> > > &input) {
        std::vector<ENFA> result;
        for(unsigned int i=0;i<input.size();i++) {
            try {
                std::shared_ptr<lxs::RE> re = lxs::parseRE(input[i].second.second);
                ENFA enfa;
                re->toENFA(enfa,0);
                enfa.numStates++;
                enfa.starting = 0;
                enfa.priority[(State) *enfa.accepting.begin()] = (Priority) i; 
                enfa.acceptingToken[(State) *enfa.accepting.begin()] = input[i].second.first;
                result.push_back(enfa);
            } catch(SyntaxError &err) {
                throw InputParserException("Error when parsing regex on line " + std::to_string(input[i].first) + ":\n\t" + err.what());
            }
        }

        return result;
    }
}
