#include <iostream>
#include <stack>
#include <string>
#include <vector>
#include <cmath>
#include <cctype>
#include <stdexcept>
#include <map>
#include <sstream>

using namespace std;

// Define the types of tokens that can be part of the expression
enum TokenType {
    Number, Operator, Function, LeftParen, RightParen
};

// Token structure to hold the details of each token in the expression
struct Token {
    TokenType type;
    double value;
    string name;
    int precedence;
    bool rightAssociative;

    // Constructor for operators (+, -, *, /, ^)
    Token(char c) : type(Operator), value(0), name(1, c), precedence(0), rightAssociative(false) {
        if (name == "+" || name == "-") {
            precedence = 1;
        } else if (name == "*" || name == "/") {
            precedence = 2;
        } else if (name == "^") {
            precedence = 3;
            rightAssociative = true;
        }
    }

    // Constructor for numbers
    Token(double val) : type(Number), value(val), name(""), precedence(0), rightAssociative(false) {}

    // Constructor for functions (sin, cos, etc.) and parentheses
    Token(TokenType t, const string& n = "") : type(t), value(0), name(n), precedence(0), rightAssociative(false) {}
};

// Tokenize the input string into a list of tokens
vector<Token> tokenize(const string& expression) {
    vector<Token> tokens;
    map<string, TokenType> functions;  // Map to hold known functions
    functions["sin"] = Function;
    functions["cos"] = Function;
    functions["tan"] = Function;
    functions["exp"] = Function;
    functions["log"] = Function;
    functions["sqrt"] = Function;

    // Loop through each character in the input string
    for (size_t i = 0; i < expression.length(); i++) {
        char c = expression[i];
        if (isspace(c)) continue;  // Skip whitespace

        // Check if the character is part of a number (digit or decimal point)
        if (isdigit(c) || (c == '.')) {
            string number;
            // Continue to append to string while we have digits or decimal points
            while (i < expression.length() && (isdigit(expression[i]) || expression[i] == '.')) {
                number += expression[i++];
            }
            --i;  // Decrement to avoid skipping a character after the loop
            istringstream iss(number);  // Convert string to double
            double num;
            iss >> num;
            tokens.push_back(Token(num));  // Create a number token
        } else if (isalpha(c)) {  // Check if the character starts a function name
            string func;
            while (i < expression.length() && isalpha(expression[i])) {
                func += expression[i++];
            }
            --i;
            if (functions.find(func) != functions.end()) {
                tokens.push_back(Token(Function, func));  // Create a function token
            }
        } else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '^') {
            tokens.push_back(Token(c));  // Create an operator token
        } else if (c == '(') {
            tokens.push_back(Token(LeftParen));  // Left parenthesis token
        } else if (c == ')') {
            tokens.push_back(Token(RightParen));  // Right parenthesis token
        }
    }
    return tokens;
}

// Convert the list of tokens into Reverse Polish Notation (RPN) using the Shunting-yard algorithm
vector<Token> toRPN(const vector<Token>& tokens) {
    stack<Token> stack;
    vector<Token> output;
    vector<Token>::const_iterator it;
    for (it = tokens.begin(); it != tokens.end(); ++it) {
        const Token& token = *it;
        switch (token.type) {
            case Number:
                output.push_back(token);  // Push numbers directly to the output
                break;
            case Operator:
            case Function:
                // Pop from the stack to the output until we see an operator with a lower precedence or a left parenthesis
                while (!stack.empty() && ((stack.top().type == Operator && token.precedence <= stack.top().precedence && !token.rightAssociative) ||
                                         (stack.top().type == Function))) {
                    output.push_back(stack.top());
                    stack.pop();
                }
                stack.push(token);  // Push current operator or function onto the stack
                break;
            case LeftParen:
                stack.push(token);  // Push left parenthesis onto the stack
                break;
            case RightParen:
                // Pop from stack to output until left parenthesis is encountered
                while (!stack.empty() && stack.top().type != LeftParen) {
                    output.push_back(stack.top());
                    stack.pop();
                }
                stack.pop();  // Pop the left parenthesis
                if (!stack.empty() && stack.top().type == Function) {
                    output.push_back(stack.top());  // If there's a function token, add it to the output
                    stack.pop();
                }
                break;
        }
    }

    // Pop all remaining tokens in the stack to the output
    while (!stack.empty()) {
        output.push_back(stack.top());
        stack.pop();
    }
    return output;
}

// Evaluate the expression given in Reverse Polish Notation
double evaluateRPN(const vector<Token>& rpn) {
    stack<double> stack;
    for (size_t i = 0; i < rpn.size(); ++i) {
        const Token& token = rpn[i];
        if (token.type == Number) {
            stack.push(token.value);  // Push numbers onto the stack
        } else if (token.type == Operator) {
            double b = stack.top(); stack.pop();
            double a = stack.top(); stack.pop();
            // Perform the operation and push the result back onto the stack
            if (token.name == "+") stack.push(a + b);
            else if (token.name == "-") stack.push(a - b);
            else if (token.name == "*") stack.push(a * b);
            else if (token.name == "/") stack.push(a / b);
            else if (token.name == "^") stack.push(pow(a, b));
        } else if (token.type == Function) {
            double a = stack.top(); stack.pop();
            // Evaluate the function and push the result onto the stack
            if (token.name == "sin") stack.push(sin(a));
            else if (token.name == "cos") stack.push(cos(a));
            else if (token.name == "tan") stack.push(tan(a));
            else if (token.name == "exp") stack.push(exp(a));
            else if (token.name == "log") stack.push(log(a));
            else if (token.name == "sqrt") stack.push(sqrt(a));
        }
    }
    return stack.top();  // The top of the stack now holds the result
}

// Main function to read the expression from command line argument and calculate the result
int main(int argc, char *argv[]) {
    if (argc > 1) {
        string input = argv[1]; //Take user input from php code
        cout << "Your Input: " << input << endl;
        try {
            vector<Token> tokens = tokenize(input);  // Tokenize the input
            vector<Token> rpn = toRPN(tokens);  // Convert to RPN
            double result = evaluateRPN(rpn);  // Evaluate the RPN
            cout << "Result: " << result << endl;
        } catch (const exception& e) {
            cerr << "Error: " << e.what() << endl;
            return 1;
        }
    } else {
        cout << "No input received. Please try again." << endl;
    }
    return 0;
}
