#include "expman.h"
#include <cctype>

const string OPEN = "([{";
const string CLOSE = ")]}";

bool is_open(char ch) {
    return OPEN.find(ch) != string::npos;
}

bool is_close(char ch) {
    return CLOSE.find(ch) != string::npos;
}

int precedence(string ch) {
    int status;  
    if(ch == "(") {
        status = -1;
    }
    if(ch == ")") {
        status = -1;
    }
    if(ch == "+" or ch == "-")
        status = 2;
    if(ch == "*" or ch == "/")
        status = 3;
    return status;
}

void process_operator(string ch, stack<string> & operator_stack, string & postfix) {
    cout << ch << " this is ch" << endl;
    if(operator_stack.empty()) {
        operator_stack.push(ch);
        return;
    }
    if(ch == ")") {
        bool validity = true;
        while(validity) {
            postfix += operator_stack.top();
            postfix += " ";
            operator_stack.pop();
            if(operator_stack.top() == "(") {
                operator_stack.pop();
                validity = false;
            }
        }
    }
    else {
        if(precedence(ch) > precedence(operator_stack.top())) {
            operator_stack.push(ch);
        }
        else {
            while(!operator_stack.empty() and (precedence(ch) <= precedence(operator_stack.top()))) {
                postfix += operator_stack.top();
                postfix += " ";
                operator_stack.pop();
            }
            operator_stack.push(ch);
        }
    }
}

bool is_operator(string token) {
    if(token.size() != 1) {
        return false;
    }
    if(token == "+" or
       token == "-" or
       token == "/" or
       token == "*" or
       token == "^" or
       token == "(" or
       token == ")") {
        return true;
    }
    return false;
}

bool is_int(string token) {
    if(token == "") {
        return false;
    }
    for(unsigned int i = 0; i < token.size(); i++) {
        if(!isdigit(token[i])) {
            return false;
        }
    }
    return true;
}

bool is_valid_token(string token) {
    if(!is_int(token) and !is_operator(token) and !is_paren(token)) {
        return false;
    } 
    return true;
}

bool is_paren(string token) {
    if(token == "(" or 
       token == "{" or 
       token == "[" or
       token == ")" or
       token == "}" or
       token == "]") {
        return true;
    }
    return false;
}

bool is_valid_expression(string expression) {
    stack<string> temp = parse_expression(expression);
    while(!temp.empty()) {
        if(not is_valid_token(temp.top())) {
            return false;
        }
        temp.pop();
    }
    return true;
}

bool expman::isBalanced(string expression) {
    stack<char> s;
    bool balanced = true;
    string::const_iterator iter = expression.begin();
    while(balanced && (iter != expression.end())) {
        char next_ch = *iter;
        if(is_open(next_ch)) {
            s.push(next_ch);
        }
        else if(is_close(next_ch)) {
            if(s.empty()) {
                balanced = false;
            }
            else {
                char top_ch = s.top();
                s.pop();
                balanced = OPEN.find(top_ch) == CLOSE.find(next_ch);
            }
        }
        ++iter;
    }
    return balanced && s.empty();
}

string expman::postfixToInfix(string postfixExpression) {
    cout << postfixExpression << endl;
    if(!isBalanced(postfixExpression))
        return "invalid";
    if(!is_valid_expression(postfixExpression))
        return "invalid";
    return "h";
}

string expman::infixToPostfix(string infixExpression) {
    cout << infixExpression << endl;
    if(!isBalanced(infixExpression))
        return "invalid";
    if(!is_valid_expression(infixExpression))
        return "invalid";
    stack<string> operator_stack;
    stack<string> expression = parse_expression(infixExpression);
    expression = reverse_stack(expression);
    string postfix;
    while(!expression.empty()) {
        if(is_int(expression.top())) {
            postfix += expression.top();
            postfix += " ";
            expression.pop();
        }
        else if(is_operator(expression.top())) {
            process_operator(expression.top(), operator_stack, postfix);
            expression.pop();
        }
        else if(is_paren(expression.top())) {
            expression.pop();
        }
    }
    while(!operator_stack.empty()) {
        postfix += operator_stack.top();
        postfix += " ";
        operator_stack.pop();
    }
    cout << postfix << " final postfix" << endl;
    return postfix;
}

string expman::postfixEvaluate(string postfixExpression) {
    cout << postfixExpression.size() << " size of string" << endl;
    if(!isBalanced(postfixExpression)) {
        cout << "not balanced" << endl;
        return "invalid";
    }
    if(!is_valid_expression(postfixExpression)) {
        cout << "invalide character" << endl;
        return "invalid";
    }
    stack<string> expression = parse_expression(postfixExpression);
    if(expression.size() == 1) {
        cout << "this is here" << endl;
        return postfixExpression;
    }
    stack<string> numbers;
    expression = reverse_stack(expression);
    while(!expression.empty()) {
        if(is_int(expression.top())) {
            numbers.push(expression.top());
            expression.pop();
        }
        else if(is_operator(expression.top())) {
            if(numbers.empty()) {
                return "invalid";
            }
            string r = numbers.top();
            string l = numbers.top();
            numbers.pop();
            numbers.pop();
            int left = atoi(l.c_str());
            int right = atoi(r.c_str());
            int result;
            if(expression.top() == "+") {
                result = left + right;
            }
            else if(expression.top() == "-") {
                result = left - right;
            }
            else if(expression.top() == "*") {
                result = left * right;
            }
            else if(expression.top()== "/") {
                if(left == 0) {
                    return "invalid";
                }
                else {
                    result = right / left;
                }
            }
            expression.pop();
            stringstream ss;
            ss << result;
            numbers.push(ss.str());
        }
    }
    return numbers.top();
}

stack<string> reverse_stack(stack<string> s) {
    stack<string> r;
    while(!s.empty()) {
        r.push(s.top());
        s.pop();
    }
    return r;
}

stack<string> parse_expression(string expression) {
    stack<string> results;
    string s;
    for(unsigned int i = 0; i < expression.length(); i++) {
        char c = expression[i];
        if(c != ' ') {
            s += c;
        }
        else {
            if(s != "") {
                results.push(s);
                s.clear();
            }
        }
    }
    if(s != "") {
        results.push(s);
    }
    return results;
}
