using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Calculator2D
{
    public static float x;

    public float getNum(string s, ref int index)
    {
        bool end = false;
        string num = "";

        while(!end && index < s.Length)
        {
            if ((s[index] >= '0' && s[index] <= '9') || s[index] == '.')
            {
                num += s[index];
                index++;
            }
            else
                break;
        }
        index--;
        return float.Parse(num);
    }
    //'('-0, '+-'-1,'*/'-2,'sct^l'-3
    private bool calculateStack(int prior, ref Stack<float> numstack, ref Stack<char> opstack)
    {
        while(opstack.Count > 0)
        {
            char operater = opstack.Peek();
            float num1;
            float num2;
            switch (operater)
            {
                case '+':
                    if (prior == 1 || prior == 2)
                        return true;
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    num2 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(num2 + num1);
                    break;
                case '-':
                    if (prior == 1 || prior == 2)
                        return true;
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    num2 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(num2 - num1);
                    break;
                case '*':
                    if (prior == 2)
                        return true;
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    num2 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(num2 * num1);
                    break;
                case '/':
                    if (prior == 2)
                        return true;
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    if (num1 == 0)
                        return false;
                    num2 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(num2 / num1);
                    break;
                case '(':
                    if(prior == 0)
                        opstack.Pop();
                    return true;
                case 's':
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(Mathf.Sin(num1));
                    break;
                case 'c':
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(Mathf.Cos(num1));
                    break;
                case 't':
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(Mathf.Tan(num1));
                    break;
                case '^':
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    num2 = numstack.Peek();
                    numstack.Pop();
                    numstack.Push(Mathf.Pow(num2, num1));
                    break;
                case 'l':
                    opstack.Pop();
                    num1 = numstack.Peek();
                    numstack.Pop();
                    if(num1 <= 0)
                        return false;
                    num2 = numstack.Peek();
                    numstack.Pop();
                    if(num2 <=0 || num2 == 1)
                        return false;
                    numstack.Push(Mathf.Log(num1, num2));
                    break;
                default:
                    break;
            }
        }
        return true;
    }

    public bool calculate2D(string s, float xIn, out float yOut)
    {
        x = xIn;
        yOut = 0;
        Stack<char> opstack = new Stack<char>();
        Stack<float> numstack = new Stack<float>();
        int length = s.Length;
        int index;
        char peek;

        for (index = 0; index < length; ++index)
        {
            if(s[index] == '+' || s[index] == '-')
            {
                if(index == 0)
                {
                    s = "0" + s;
                    length++;
                }
                else
                {
                    if(s[index - 1] == '(')
                    {
                        s.Insert(index - 1, "0");
                        length++;
                    }
                }
            }
        }

        index = 0;

        while (index < length)
        {
            Debug.Log(s[index]);
            switch (s[index])
            {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    numstack.Push(getNum(s, ref index));
                    break;
                case 'x':
                    numstack.Push(x);
                    break;
                case '(':
                    opstack.Push('(');
                    break;
                case ')':
                    if(!calculateStack(0, ref numstack, ref opstack))
                        return false;
                    break;
                case '+':
                    if(opstack.Count != 0)
                    {
                        peek = opstack.Peek();
                        if (peek != '+' && peek != '-')
                        {
                            if (!calculateStack(1, ref numstack, ref opstack))
                                return false;
                        }
                    }
                    opstack.Push('+');
                    break;
                case '-':
                    if (opstack.Count != 0)
                    {
                        peek = opstack.Peek();
                        if (peek != '+' && peek != '-')
                        {
                            if (!calculateStack(1, ref numstack, ref opstack))
                                return false;
                        }
                    }
                    opstack.Push('-');
                    break;
                case '*':
                    if (opstack.Count != 0)
                    {
                        peek = opstack.Peek();
                        if (peek != '+' && peek != '-' && peek != '*' && peek != '/')
                        {
                            if (!calculateStack(2, ref numstack, ref opstack))
                                return false;
                        }
                    }
                    opstack.Push('*');
                    break;
                case '/':
                    if (opstack.Count != 0)
                    {
                        peek = opstack.Peek();
                        if (peek != '+' && peek != '-' && peek != '*' && peek != '/')
                        {
                            if (!calculateStack(2, ref numstack, ref opstack))
                                return false;
                        }
                    }
                    opstack.Push('/');
                    break;
                case 's':
                    if (s[index + 1] == 'i' && s[index + 2] == 'n' && s[index + 3] == '(')
                    {
                        opstack.Push('s');
                        opstack.Push('(');
                        index += 3;
                    }
                    break;
                case 'c':
                    if (s[index + 1] == 'o' && s[index + 2] == 's' && s[index + 3] == '(')
                    {
                        opstack.Push('c');
                        opstack.Push('(');
                        index += 3;
                    }
                    break;
                case 't':
                    if (s[index + 1] == 'a' && s[index + 2] == 'n' && s[index + 3] == '(')
                    {
                        opstack.Push('t');
                        opstack.Push('(');
                        index += 3;
                    }
                    break;
                case '^':
                    opstack.Push('^');
                    break;
                case 'l':
                    if (s[index + 1] == 'o' && s[index + 2] == 'g' && s[index + 3] == '(')
                    {
                        opstack.Push('l');
                        opstack.Push('(');
                        index += 3;
                    }
                    break;
                default:
                    break;
            }
            index++;
        }
        while(opstack.Count > 0)
        {
            if (!calculateStack(3, ref numstack, ref opstack))
                return false;
        }
        yOut = numstack.Peek();
        return true;
    }

}
