//---------------------------------------------------------------------------
//
//  Math Utils Parser 
//				
//  Example 1 - using the parser as a static library
//
//---------------------------------------------------------------------------

#include "muParserTest.h"

/** \brief This macro will enable mathematical constants like M_PI. */
#define _USE_MATH_DEFINES		

#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>

#include "muParser.h"

#if defined( USINGDLL ) && defined( _WIN32 )
#error This sample can be used only with STATIC builds of muParser (on win32)
#endif


using namespace std;
using namespace mu;

// Operator callback functions
value_type Mega(value_type a_fVal) { return a_fVal * 1e6; }
value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; }
value_type Rnd(value_type v) { return v*std::rand()/(value_type)(RAND_MAX+1.0); }
value_type Not(value_type v) { return v==0; }
value_type Add(value_type v1, value_type v2) { return v1+v2; }
value_type Mul(value_type v1, value_type v2) { return v1*v2; }

mu::value_type SampleQuery(const char *szMsg) 
{
  if (szMsg) std::cout << szMsg << std::endl;
  return 999;
};

//---------------------------------------------------------------------------
// Factory function for creating new parser variables
// This could as well be a function performing database queries.
double* AddVariable(const char *a_szName)
{
  // I don't want dynamic allocation here, so i used this static buffer
  // If you want dynamic allocation you must allocate all variables dynamically
  // in order to delete them later on. Or you find other ways to keep track of 
  // variables that have been created implicitely.
  static double afValBuf[100];  
  static int iVal = 0;          

  cout << "Generating new variable \"" 
       << a_szName << "\" (slots left: " 
       << 99-iVal << ")" << endl;

  afValBuf[iVal] = 0;
  if (iVal>=99)
    throw mu::ParserError("Variable buffer overflow.");

  return &afValBuf[iVal++];
}

//---------------------------------------------------------------------------
void SelfTest()
{
  std::cout << "---------------------------------------\n";
  std::cout << "\n";
  std::cout << "  Math Parser sample application\n";
  std::cout << "\n";
  std::cout << "---------------------------------------\n";

  mu::Test::ParserTester pt;
  pt.Run();

  std::cout << "---------------------------------------\n";
  std::cout << "Commands:\n";
  std::cout << "  list var     - list parser variables\n";
  std::cout << "  list exprvar - list expression variables\n";
  std::cout << "  list const   - list all numeric parser constants\n";
  std::cout << "  exit         - exits the parser\n";
  std::cout << "Constants:\n";
  std::cout << "  \"_e\"   2.718281828459045235360287\n";
  std::cout << "  \"_pi\"  3.141592653589793238462643\n";
  std::cout << "---------------------------------------\n";
  std::cout << "Enter a formula or a command:\n";
}

//---------------------------------------------------------------------------
void ListVar(const mu::ParserBase &parser)
{
  // Query the used variables (must be done after calc)
  mu::varmap_type variables = parser.GetVar();
  if (!variables.size())
    return;

  cout << "\nParser variables:\n";
  cout <<   "-----------------\n";
  cout << "Number: " << (int)variables.size() << "\n";
  varmap_type::const_iterator item = variables.begin();
  for (; item!=variables.end(); ++item)
    cout << "Name: " << item->first << "   Address: [0x" << item->second << "]\n";
}

//---------------------------------------------------------------------------
void ListConst(const mu::ParserBase &parser)
{
  cout << "\nParser constants:\n";
  cout <<   "-----------------\n";

  mu::valmap_type cmap = parser.GetConst();
  if (!cmap.size())
  {
    cout << "Expression does not contain constants\n";
  }
  else
  {
    valmap_type::const_iterator item = cmap.begin();
    for (; item!=cmap.end(); ++item)
      cout << "  " << item->first << " =  " << item->second << "\n";
  }
}

//---------------------------------------------------------------------------
void ListExprVar(const mu::ParserBase &parser)
{
  std::string sExpr = parser.GetExpr();
  if (sExpr.length()==0)
  {
    cout << "Expression string is empty\n";
    return;
  }

  // Query the used variables (must be done after calc)
  cout << "\nExpression variables:\n";
  cout <<   "---------------------\n";
  cout << "Expression: " << parser.GetExpr() << "\n";

  varmap_type variables = parser.GetUsedVar();
  if (!variables.size())
  {
    cout << "Expression does not contain variables\n";
  }
  else
  {
    cout << "Number: " << (int)variables.size() << "\n";
    mu::varmap_type::const_iterator item = variables.begin();
    for (; item!=variables.end(); ++item)
      cout << "Name: " << item->first << "   Address: [0x" << item->second << "]\n";
  }
}

//---------------------------------------------------------------------------
/** \brief Check for external keywords.
*/
bool CheckKeywords(const char *a_szLine, mu::ParserBase &a_Parser)
{
  std::string sLine(a_szLine);

  if (sLine=="quit")
  {
    exit(0);
  }
  else if (sLine=="list var")
  {
    ListVar(a_Parser);
    return true;
  }
  else if (sLine=="list const")
  {
    ListConst(a_Parser);
    return true;
  }
  else if (sLine=="list exprvar")
  {
    ListExprVar(a_Parser);
    return true;
  }
  else if (sLine=="list const")
  {
    ListConst(a_Parser);
    return true;
  }

  return false;
}

//---------------------------------------------------------------------------
void Calc()
{
  char line[100];
  mu::Parser  parser;

  // Add some variables
  value_type  vVarVal[] = { 1, 2 }; // Values of the parser variables
  parser.DefineVar("a", &vVarVal[0]);  // Assign Variable names and bind them to the C++ variables
  parser.DefineVar("b", &vVarVal[1]);
  parser.DefineStrConst("strBuf", "hello world");

  // Add user defined unary operators
  parser.DefinePostfixOprt("M", Mega);
  parser.DefinePostfixOprt("m", Milli);
  parser.DefineInfixOprt("!", Not);
  parser.DefineFun("query", SampleQuery, false);
  parser.DefineFun("rnd", Rnd, false); // Add an unoptimizeable function
  parser.DefineOprt("add", Add, 0);
  parser.DefineOprt("mul", Mul, 1);

  // Define the variable factory
  parser.SetVarFactory(AddVariable);

  parser.EnableOptimizer(false);     // set optimizer state (true is the default)

  while(true)
  {
    try
    {
      while ( fgets(line, 99, stdin) )
      {
	      line[std::strlen(line)-1] = 0; // overwrite nl
        if (CheckKeywords(line, parser)) 
          continue;

        parser.SetExpr(line);
        cout << parser.Eval() << "\n";
        cout << parser.Eval() << "\n";
      }
    }
    catch(mu::Parser::exception_type &e)
    {
      cout << "\nError:\n";
      cout << "------\n";
      cout << "Message:  " << e.GetMsg() << "\n";
      cout << "Formula:  " << e.GetExpr() << "\n";
      cout << "Token:    " << e.GetToken() << "\n";
      cout << "Position: " << (int)e.GetPos() << "\n";
      cout << "Errc:     " << e.GetCode() << "\n";
    }
  } // while running
}

//---------------------------------------------------------------------------
int main(int, char*)
{
  using namespace mu;

  SelfTest();
  
  try
  {
    Calc();
  }
  catch(Parser::exception_type &e)
  {
    // Only erros raised during the initialization will end up here
    // formula related errors are treated in Calc()
    cout << "Initialization error:  " << e.GetMsg() << endl;
    while(1);
  }

  return 0;
}
