#include "mathfunction.hpp"
#include <cstdio>
#include <cctype>
#include <cstring>
#include "unaryoperators.hpp"
#include "binaryoperators.hpp"

char *SubString(const char *s, int Pos, int Length)
{
 register int i;
 char *t;

 t = new char[Length + 1];
 for (i = 0; i < Length; i++)
 {
  t[i] = s[i+Pos];
 }

 t[Length] = '\0';
 return t;
}

TMathFunction::TMathFunction(const char *s)
{
 static TUnaryOperatorInfo UnaryOperatorTableInit[] =
 {
  {"sin", sin},
  {"cos", cos},
  {"tan", tan},
  {"arcsin", asin},
  {"arccos", acos},
  {"arctan", atan},
  {"sinh", sinh},
  {"cosh", cosh},
  {"tanh", tanh},
  {"exp", exp},
  {"ln", log},
  {"ceil", ceil},
  {"floor", floor},
  {"abs", fabs},
  {"round", round},
  {"fact", fact},
 };
 static TBinaryOperatorInfo BinaryOperatorTableInit[] =
 {
  {'^', 3, RIGHT_ASSOCIATIVE, pow},
  {'*', 2, LEFT_ASSOCIATIVE, mult},
  {'/', 2, LEFT_ASSOCIATIVE, divide},
  {'+', 1, LEFT_ASSOCIATIVE, add},
  {'-', 1, LEFT_ASSOCIATIVE, subtract},
 };

 UnaryOperatorTable = &(UnaryOperatorTableInit[0]);
 BinaryOperatorTable = &(BinaryOperatorTableInit[0]);

 First = new TMathFunctionNode;
 CreateBinaryTree(s, First);
}

TMathFunction::~TMathFunction()
{
}

double TMathFunction::operator()(double x, double y)
{
 return Traverse(First, x, y);
}

//This function does not support:
// - as a unary operator (e.g. sin(-x)) but this can be avoided by using e.g. sin(0-x)
// ! (the factorial symbol - e.g. sin(x!)) but this can be avoided by using e.g. sin(fact(x))
// | (the modulus symbol - e.g. sin(|x|) but this can be avoided by using e.g. sin(abs(x))
void TMathFunction::CreateBinaryTree(const char *s, TMathFunctionNode *CurrentNode)
{
 char *StringToPass;
 char *OperatorString;
 int i = 0;
 register int j;
 TBinaryOperatorInfo BinaryOperatorInfo = {' ', (NUMBINARYOPERATORS+1), RIGHT_ASSOCIATIVE, 0};
 int NumOpenBrackets = 0;
 int BinaryOperatorPos = 0; //safe to have this as the default, as expression cannot start with a binary operator.
 int FirstBracketPos = 0; //safe to have this as the default, as value will not be used if expression starts with a bracket.
 int StringLength = 0;

 while (s[i] != '\0')
 {
  if (s[i] == ' ') ; //Ignore spaces.
  else if (s[i] == '(')
  {
   NumOpenBrackets++;
   if (!FirstBracketPos) FirstBracketPos = i;
  }
  else if (s[i] == ')') NumOpenBrackets--;
  else if ((!isalnum(s[i])) && (NumOpenBrackets == 0) && (s[i] != '.'))
  {
   //s[i] is a binary operator.

   //Lookup binary operator in the table. It should be found, but if not the last operator in the table is used.
   for (j = 0; j < NUMBINARYOPERATORS; j++)
   {
    if (BinaryOperatorTable[j].Symbol == s[i]) break;
   }

   //If the thing has lower precedence then store it as the current used binary operator.
   //Also if the operations have the same precedence and are both left-associative then store it.
   //Note that "top-of-the-tree-ness" is opposite from both precedence and evaluation order.
   if ((BinaryOperatorTable[j].Precedence < BinaryOperatorInfo.Precedence) || ((BinaryOperatorTable[j].Precedence == BinaryOperatorInfo.Precedence) && ((BinaryOperatorTable[j].Associativity == LEFT_ASSOCIATIVE) || (BinaryOperatorInfo.Associativity == LEFT_ASSOCIATIVE))))
   {
    BinaryOperatorInfo = BinaryOperatorTable[j];
    BinaryOperatorPos = i;
   }
  }
  i++;
  StringLength++;
 }

 if (BinaryOperatorPos != 0)
 {
  //We found a binary operator at BracketLevel 0.
  CurrentNode->Type = 4;

  //Compute and pass the first argument.
  (CurrentNode->Value).OperatorWithArguments.Argument1 = new TMathFunctionNode;
  StringToPass = SubString(s, 0, BinaryOperatorPos);
  CreateBinaryTree(StringToPass, (CurrentNode->Value).OperatorWithArguments.Argument1);
  delete StringToPass;

  //Compute and pass the second argument.
  (CurrentNode->Value).OperatorWithArguments.Argument2 = new TMathFunctionNode;
  StringToPass = SubString(s, BinaryOperatorPos + 1, StringLength - BinaryOperatorPos - 1);
  CreateBinaryTree(StringToPass, (CurrentNode->Value).OperatorWithArguments.Argument2);
  delete StringToPass;

  //Pass the actual function pointer.
  (CurrentNode->Value).OperatorWithArguments.Operator.BinaryOperator = BinaryOperatorInfo.BinaryOperator;
 }
 else
 {
  //No binary operators at BracketLevel 0.

  if (s[0] == '(')
  {
   //String is bracketed completely; we can now discard this pair of brackets.
   StringToPass = SubString(s, 1, StringLength - 2);
   CreateBinaryTree(StringToPass, CurrentNode);
   delete StringToPass;
  }
  else if (isdigit(s[0]) || (s[0] == '.'))
  {
   //String is just a number.
   CurrentNode->Type = 1;
   sscanf(s, "%lf", &((CurrentNode->Value).Constant));
  }
  else if ((StringLength == 1) || (isalpha(s[0]) && (s[1] == ' ')))
  {
   //String is just a variable.
   CurrentNode->Type = 2;
   switch(s[0])
   {
    case 'x':
    (CurrentNode->Value).VariableID = 1;
    break;
    case 'y':
    (CurrentNode->Value).VariableID = 2;
    break;
    default: //Should never get here.
    break;
   }
  }
  else
  {
   //String is prefixed by a unary operator.
   CurrentNode->Type = 3;
   OperatorString = SubString(s, 0, FirstBracketPos);
   for (j = 0; j < NUMUNARYOPERATORS; j++)
   {
    if (!strcmp(OperatorString, UnaryOperatorTable[j].Name))
    {
     StringToPass = SubString(s, FirstBracketPos + 1, StringLength - FirstBracketPos - 2);
     (CurrentNode->Value).OperatorWithArguments.Argument1 = new TMathFunctionNode;
     CreateBinaryTree(StringToPass, (CurrentNode->Value).OperatorWithArguments.Argument1);
     (CurrentNode->Value).OperatorWithArguments.Argument2 = 0;
     (CurrentNode->Value).OperatorWithArguments.Operator.UnaryOperator = UnaryOperatorTable[j].UnaryOperator;
     break;
    }
   }
   delete StringToPass;
   delete OperatorString;
  }
 }

 return;
}

double TMathFunction::Traverse(TMathFunctionNode *CurrentNode, double x, double y)
{
 double Arg1, Arg2;
 switch(CurrentNode->Type)
 {
  case 1:
  //Node represents a constant.
  return (CurrentNode->Value).Constant;
  break;
  case 2:
  //Node represents a variable.
  switch((CurrentNode->Value).VariableID)
  {
   case 1:
   return x;
   break;
   case 2:
   return y;
   break;
   default: //Should not get here.
   return 0;
   break;
  }
  case 3:
  //Node represents a unary operator.
  Arg1 = Traverse((CurrentNode->Value).OperatorWithArguments.Argument1, x, y);
  return ((CurrentNode->Value).OperatorWithArguments.Operator.UnaryOperator)(Arg1);
  case 4:
  //Node represents a binary operator.
  Arg1 = Traverse((CurrentNode->Value).OperatorWithArguments.Argument1, x, y);
  Arg2 = Traverse((CurrentNode->Value).OperatorWithArguments.Argument2, x, y);
  return ((CurrentNode->Value).OperatorWithArguments.Operator.BinaryOperator)(Arg1, Arg2);
  default:
  //Should not get here.
  return 0;
 }
 return 0; //Should not get here.
}
