

namespace ParserInCSharp
{
    public class MathFunction
    {
        public string Term { private set; get; }
        public MathFunction childLeft { private set; get; }
        public MathFunction childRight { private set; get; }
        public OpTyp Typ { private set; get; }
        public ErrorTyp Error { private set; get; }
        private string GetFullTerm(string term)
        {
            string termTmp = term;
            string subTerm;
            int subTermValue;
            bool toInsert;
            bool ende;
            string[] zk;
            int pos;
            if (termTmp != "")
            {
                // Prüfung, ob vor den gennannten Zeichen ein "*" eingefügt werden muss
                zk = new string[] { "x", "(", "sin", "cos", "tan", "sqrt", "lg", "e", "Pi" };
                foreach (string z in zk)
                {
                    pos = 1;
                    do
                    {
                        if (((pos = termTmp.IndexOf(z, pos)) > -1) && (pos > 0))
                        {
                            ende = false; // weitere Suche
                            subTerm = termTmp.Substring(pos - 1, 1);  //  Zeichen davor
                            try    // prüfen, ob das vorangehende Zeichen eine Zahl ist 
                            {
                                subTermValue = int.Parse(subTerm);
                                toInsert = true;
                            }
                            catch
                            {
                                toInsert = false;
                            }

                            if ((toInsert == false) && ((subTerm == "x") || (subTerm == ")") || (subTerm == "e"))) // prüfen, ob das vorangehende Zeichen "x" ist 
                            {
                                toInsert = true;
                            }
                            if (toInsert == true)
                            {
                                termTmp = termTmp.Insert(pos, "*");
                                pos = pos + 2;
                            }
                            else
                            {
                                pos++;
                            }
                        }
                        else
                            ende = true; // Suche beenden
                    }
                    while (ende == false);
                }
                // Prüfung, ob nach den gennannten Zeichen ein "*" eingefügt werden muss
                zk = new string[] { ")" };
                foreach (string z in zk)
                {
                    pos = 0;
                    do
                    {
                        if (((pos = termTmp.IndexOf(z, pos)) > -1) && (pos + 1 < termTmp.Length))
                        {
                            ende = false; // weitere Suche
                            subTerm = termTmp.Substring(pos + 1, 1);  // Zeichen danach
                            try   // prüfen, ob das nachgehende Zeichen eine Zahl ist 
                            {
                                subTermValue = int.Parse(subTerm);
                                toInsert = true;
                            }
                            catch
                            {
                                toInsert = false;
                            }
                            if ((toInsert == false) && (subTerm == "x")) // prüfen, ob das nachgehende Zeichen "x" ist 
                            {
                                toInsert = true;
                            }
                            if (toInsert == true)
                            {
                                termTmp = termTmp.Insert(pos + 1, "*");
                                pos = pos + 2;
                            }
                            else
                            {
                                pos++;
                            }
                        }
                        else
                            ende = true; // Suche beenden
                    }
                    while (ende == false);
                }
            }
            return termTmp;
        }
        private int GetIndex(string term, string searchChar)
        {
            int index = -1;
            // Search for operators, e.g.: +, -, *, /, ^          
            int niveauOfBrackets = 0;
            for (int i = 0; i < term.Length; i++)
            {
                if (term[i].ToString() == "(")
                    niveauOfBrackets++;
                else if (term[i].ToString() == ")")
                    niveauOfBrackets--;
                if ((term[i].ToString() == searchChar) && (niveauOfBrackets == 0))
                    index = i;
            }
            return index;
        }
        private bool DeleteBrackets(ref string term)
        {
            if ((term.StartsWith("(")) && (term.EndsWith(")")))
            {
                term = term.Substring(1, term.Length - 2);
                return true;
            }
            else
                return false;
        }
        private OpTyp ExploreTerm(ref string term, out int Index)
        {
            OpTyp typ = new OpTyp();
            int index = -1;
            bool ergebnis;
            do
            {
                if ((GetIndex(term, "+") == -1) && (GetIndex(term, "-") == -1) && (GetIndex(term, "*") == -1)
                    && (GetIndex(term, "/") == -1) && (GetIndex(term, "^") == -1))
                    ergebnis = DeleteBrackets(ref term);
                else
                    ergebnis = false;
            }
            while (ergebnis == true);
            if ((term.StartsWith("-sin(")) ||
                (term.StartsWith("-cos(")) ||
                (term.StartsWith("-tan(")) ||
                (term.StartsWith("-sqrt(")) ||
                (term.StartsWith("-lg(")) ||
                (term.StartsWith("-ln(")) ||
                (term.StartsWith("-lb(")) ||
                (term.StartsWith("-x")) ||
                (term.StartsWith("-("))
                )
            {
                string subTerm = term.Substring(1);
                term = "-1*" + subTerm;
            }
            if (term == "x") typ = OpTyp.Var;
            if (term == "pi") typ = OpTyp.Pi;
            else
            {
                // Sinus, Kosinus, Tangens, Quadratwurzel, Logarithmus
                if (term.StartsWith("sin(")) typ = OpTyp.Sin;
                else if (term.StartsWith("cos(")) typ = OpTyp.Cos;
                else if (term.StartsWith("tan(")) typ = OpTyp.Tan;
                else if (term.StartsWith("sqrt(")) typ = OpTyp.Sqr;
                else if (term.StartsWith("lg(")) typ = OpTyp.Lg;
                // Fakultät
                if (GetIndex(term, "!") > -1)
                {
                    index = GetIndex(term, "!");
                    typ = OpTyp.Fak;
                }
                // Potenzrechnung
                if (GetIndex(term, "^") > -1)
                {
                    index = GetIndex(term, "^");
                    typ = OpTyp.Pow;
                }
                // Punktrechnung
                if (GetIndex(term, "/") > -1)
                {
                    index = GetIndex(term, "/");
                    typ = OpTyp.Div;
                }
                if (GetIndex(term, "*") > -1)
                {
                    index = GetIndex(term, "*");
                    typ = OpTyp.Mul;
                }
                // Strichrechnung
                if (GetIndex(term, "-") > 0)
                {
                    index = GetIndex(term, "-");
                    typ = OpTyp.Sub;
                }
                if (GetIndex(term, "+") > -1)
                {
                    index = GetIndex(term, "+");
                    typ = OpTyp.Add;
                }
            }
            //  Prüfen, ob die Umwandlung in eine Zahl möglich ist
            if (typ == OpTyp.No)
            {
                try
                {
                    double.Parse(term);
                    typ = OpTyp.Num;
                }
                catch
                {
                    Error = ErrorTyp.Resolve;
                }
            }
            Index = index;
            return typ;
        }
        private bool TestTerm(string term)
        {
            bool result = false;
            int number = 0;
            for (int i = 0; i < term.Length; i++)
            {
                if (term[i].ToString() == "(")
                    number++;
                else if (term[i].ToString() == ")")
                    number--;
            }
            if (number == 0)
                result = true;
            return result;
        }
        public MathFunction(string term)
        {
            Term = term;
            term = GetFullTerm(term);
            if (TestTerm(term) == true)
            {
                term = term.ToLower();
                int index = -1;
                Error = ErrorTyp.No;
                Typ = ExploreTerm(ref term, out index);
                if ((Typ == OpTyp.Div) || (Typ == OpTyp.Sub) || (Typ == OpTyp.Mul)
                    || (Typ == OpTyp.Add) || (Typ == OpTyp.Pow))
                {
                    string termPart1 = term.Substring(0, index);
                    string termPart2 = term.Substring(index + 1, (term.Length - index - 1));
                    childLeft = new MathFunction(termPart1);
                    childRight = new MathFunction(termPart2);
                }
                else if ((Typ == OpTyp.Sin) || (Typ == OpTyp.Cos)
                    || (Typ == OpTyp.Tan) || (Typ == OpTyp.Sqr) || (Typ == OpTyp.Lg))
                {
                    string teilTerm;
                    int startPosition = term.IndexOf("(");
                    teilTerm = term.Substring(startPosition + 1, term.Length - (startPosition + 2));
                    childLeft = new MathFunction(teilTerm);
                }
                else if (Typ == OpTyp.Fak)
                {
                    string teilTerm;
                    teilTerm = term.Substring(0, term.Length - 1);
                    childLeft = new MathFunction(teilTerm);
                }
            }
            else
                Error = ErrorTyp.Resolve;
        }
    }
}
