#include <vector>
#include <string>
#include <map>
#include <stack>
#include <iostream>
#include <fstream>
#include <sstream>

#include "parse.h"
#include "line.h"

std::map<std::string,int> Variables;
std::vector<Line> Lines;
int ExecutionCount = 0;

void Line::Execute()
{
    if(ExecutionCount > 1000)
    {
        std::cerr << "Chyba: cyklus ve skocich" << std::endl;
        exit(1);
    }
    ExecutionCount++;
    
    if(type == "set")
    {
        if(Variables.find(varName) != Variables.end())
        {
            Variables[varName] = expression->Eval();
        }
        else
        {
            Variables.insert(std::pair<std::string, int>(varName, expression->Eval()));
        }
    }
    else 
    {
        if(Variables.find(varName) != Variables.end() && Variables[varName] != 0)
        {        
            int res = expression->Eval();

            if(res == 0)
            {
                std::cerr << "Chyba: cyklus ve skocich" << std::endl;
                exit(1);
            }
            else 
            {
                if((lineNum + res) < 1 || (lineNum + res) > Lines.size())
                {
                    std::cerr << "Chyba: skok na neplatne misto" << std::endl;
                    exit(1);
                }

                Lines[(lineNum + res)-1].Execute();
            }
        }
    }

    if(Lines.size() > lineNum)
    {
        Lines[lineNum].Execute();
    }
}




class OpenBracket : public Node
{
    int Eval()
    {
        std::cerr << "Zavorky nelze vyhodnocovat" << std::endl;
        exit(1);
    }
};

class EndBracket : public Node
{
    int Eval()
    {
        std::cerr << "Zavorky nelze vyhodnocovat" << std::endl;
        exit(1);
    }
};

class Operator : public Node
{
public:
    Node* leftChild;
    Node* rightChild;

    void SetChilds(Node* left, Node* right)
    {
        leftChild = right;
        rightChild = left;
    }
};

class PlusOperator : public Operator
{
public:    
    int Eval()
    {
        return leftChild->Eval() + rightChild->Eval();
    }
};

class MinusOperator : public Operator
{
public:
    int Eval()
    {
        return leftChild->Eval() - rightChild->Eval();
    }
};

class DivideOperator : public Operator
{
public:
    int Eval()
    {
        if(rightChild->Eval() == 0)
        {
            std::cerr << "Chyba: deleni nulou" << std::endl;
            exit(1);
        }
        
        return leftChild->Eval() / rightChild->Eval();
    }
};

class TimesOperator : public Operator
{
public:
    int Eval()
    {
        return leftChild->Eval() * rightChild->Eval();
    }
};

class ValueNode : public Node
{
public:
    int value;

    void SetValue(int val)
    {
        value = val;
    }

    int Eval()
    {
        return value;
    }
};

class VariableNode : public Node
{
public:
    std::string name;

    int Eval()
    {
        if(Variables.find(name) != Variables.end())
        {
            return Variables[name];
        }
        else 
        {
            return 0;
        }
    }
};

Node* BuildTree(std::stack<Node*> &tokens)
{
    if(dynamic_cast<ValueNode*>(tokens.top()) || dynamic_cast<VariableNode*>(tokens.top()))
    {
        Node* node = tokens.top();
        tokens.pop();
        return node;
    }
    else 
    {
        Node* node = tokens.top();
        tokens.pop();
        
        Operator* op = dynamic_cast<Operator*>(node);

        op->SetChilds(BuildTree(tokens),BuildTree(tokens));

        return op;
    }
}

Node* ParseExpression(std::string expression)
{
    std::istringstream expr(expression);

    char c;
    std::string varName;
    std::string value;

    std::stack<Node*> tokens;

    while(expr.get(c))
    {        
        if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
        {
            varName.push_back(c);
        }
        else if( (c >= '0' && c <= '9') || ( c == '-' && value.length() == 0) )
        {
            value.push_back(c);
        }
        else if((c == '+') || (c == '-') || (c == '*') || (c == '/') || (c == ')'))
        {
            if(!varName.empty())
            {
                VariableNode* var = new VariableNode();
                var->name = varName;
                tokens.push(var);
                varName = "";
            }
            if(!value.empty())
            {
                ValueNode* var = new ValueNode();
                int num;
                std::istringstream issnum(value);
                issnum >> num;
                
                var->value = num;
                tokens.push(var);
                value = "";
            }

            if(c == '+')
            {
                tokens.push(new PlusOperator());
            }
            else if(c == '-')
            {
                tokens.push(new MinusOperator());
            }
            else if(c == '*')
            {
                tokens.push(new TimesOperator());
            }
            else if(c == '/')
            {
                tokens.push(new DivideOperator());
            }
            else if(c == ')')
            {
                tokens.push(new EndBracket());
            }
        }        
        else if(c == '(')
        {
            tokens.push(new OpenBracket());
        }
    }
    if(!varName.empty())
    {
        VariableNode* var = new VariableNode();
        var->name = varName;
        tokens.push(var);
        varName = "";
    }
    if(!value.empty())
    {
        ValueNode* var = new ValueNode();
        int num;
        std::istringstream issnum(value);
        issnum >> num;
                
        var->value = num;
        tokens.push(var);
        value = "";
    }

    std::stack<Node*> s1;
    std::stack<Node*> s2;

    while(!tokens.empty())
    {
        Node* token = tokens.top();
        tokens.pop();

        if(dynamic_cast<ValueNode*>(token))
        {
            ValueNode* node = dynamic_cast<ValueNode*>(token);
            s2.push(node);
        }
        else if(dynamic_cast<VariableNode*>(token))
        {
            VariableNode* node = dynamic_cast<VariableNode*>(token);
            s2.push(node);
        }
        else if(dynamic_cast<EndBracket*>(token))
        {
            EndBracket* node = dynamic_cast<EndBracket*>(token);
            s1.push(node);
        }
        else if(dynamic_cast<OpenBracket*>(token))
        {            
            if(s1.empty())
            {
                std::cerr << "Chyba: spatne uzavorkovani vyrazu" << std::endl;
                exit(1);
            }

            while(!dynamic_cast<EndBracket*>(s1.top()))
            {
                s2.push(s1.top());
                s1.pop();

                if(s1.empty())
                {
                    std::cerr << "Chyba: spatne uzavorkovani vyrazu" << std::endl;
                    exit(1);
                }
            }

            s1.pop();
        }
        else if(dynamic_cast<PlusOperator*>(token))
        {
            PlusOperator* node = dynamic_cast<PlusOperator*>(token);
            s1.push(node);
        }
        else if(dynamic_cast<MinusOperator*>(token))
        {
            MinusOperator* node = dynamic_cast<MinusOperator*>(token);
            s1.push(node);
        }
        else if(dynamic_cast<TimesOperator*>(token))
        {
            TimesOperator* node = dynamic_cast<TimesOperator*>(token);
            s1.push(node);
        }
        else if(dynamic_cast<DivideOperator*>(token))
        {
            DivideOperator* node = dynamic_cast<DivideOperator*>(token);
            s1.push(node);
        }
    }

    while(!s1.empty())
    {
        s2.push(s1.top());
        s1.pop();
    }

    return BuildTree(s2);
}

void ParseLine(std::string line)
{
    if(*(line.begin()) == '#')
    {
        line.erase(line.begin());

        std::ifstream fileInput(line);
        Parse(fileInput);
    }    
    else if(line.find('=') != line.npos)
    {
        std::string varName;        
        
        std::istringstream iss(line);
        std::getline(iss, varName, '=');
        
        while(varName.find(' ') != varName.npos)
        {
            varName.erase(varName.find(' '));
        }

        std::string expression;
        std::getline(iss, expression);

        Line line;
        line.varName = varName;
        line.expression = ParseExpression(expression);
        line.type = "set";
        line.lineNum = Lines.size()+1;

        Lines.push_back(line);
    }
    else if(line.find('?') != line.npos)
    {
        std::string varName;        
        
        std::istringstream iss(line);
        std::getline(iss, varName, '?');
        
        while(varName.find(' ') != varName.npos)
        {
            varName.erase(varName.find(' '));
        }

        std::string expression;
        std::getline(iss, expression);

        Line line;
        line.varName = varName;
        line.expression = ParseExpression(expression);
        line.type = "jump";
        line.lineNum = Lines.size()+1;

        Lines.push_back(line);
    }
}

void Parse(std::istream &input)
{
    while(input.good())
    {
        std::string line;
        std::getline(input, line);

        ParseLine(line);
    }
}

int main(int argc, char* argv)
{
    std::ifstream input("D:\\Temp\\input.txt");

    Parse(input);

    
    Lines.begin()->Execute();

    for(auto it = Variables.begin(); it != Variables.end(); it++)
    {
        std::cout << it->first << " = " << it->second << std::endl;
    }

    return 0;
}