//---------------------------------------------------------------------------
//
//  Math Utils Parser 
//
//  Example 3 - using the parser DLL
//
//---------------------------------------------------------------------------

#include <cstring>
#include <iostream>
#include <cstdlib>  // for std::exit()

#include "muParserDLL.h"
#include "muParserError.h"

#define PARSER_CONST_PI  3.141592653589793238462643
#define PARSER_CONST_E   2.718281828459045235360287

#define PARSER_MAXVARS		10


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


//---------------------------------------------------------------------------
// Callbacks for postfix operators
double Mega(double a_fVal) { return a_fVal * 1.0e6; }
double Milli(double a_fVal) { return a_fVal / 1.0e3; }

//---------------------------------------------------------------------------
// Callbacks for infix operators
double Not(double v) { return v==0; }

//---------------------------------------------------------------------------
// Function callbacks
double Rnd(double v) { return v*std::rand() / (double)(RAND_MAX+1.0); }

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

double Sum(const double *a_afArg, int a_iArgc)
{ 
  double fRes=0;
  for (int i=0; i<a_iArgc; ++i) fRes += a_afArg[i];
  return fRes;
}

//---------------------------------------------------------------------------
// Binarty operator callbacks
double Add(double v1, double v2) { return v1+v2; }
double Mul(double v1, double v2) { return v1*v2; }

//---------------------------------------------------------------------------
// Factory function for creating new parser variables
// This could as well be a function performing database queries.
double* AddVariable(const char *a_szName)
{
  static double afValBuf[PARSER_MAXVARS];  // I don't want dynamic allocation here
  static int iVal = 0;                     // so i used this buffer

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

  afValBuf[iVal] = 0;
  if (iVal>=PARSER_MAXVARS-1) {
     printf("Variable buffer overflow.");
     return NULL;
  }

  return &afValBuf[iVal++];
}

//---------------------------------------------------------------------------
// Function to detect binary values in an expression string
bool IsBinVal(const char *a_szExpr, int &a_iPos, double &a_fVal)
{
  if (a_szExpr[0]!='#') 
    return false;

  unsigned iVal = 0, iBits = sizeof(iVal)*8;
  unsigned i=0; // BUGFIX of non-standard-conformity of VC++
  for (; (a_szExpr[i+1]=='0' || a_szExpr[i+1]=='1') && i<iBits; ++i)
    iVal |= (int)(a_szExpr[i+1]=='1') << ((iBits-1)-i);

  if (i==0) 
    return false;

  if (i==iBits) {
	  printf("Binary to integer conversion error (overflow).");
	  return false;
  }

  a_fVal = (unsigned)(iVal >> (iBits-i) );
  a_iPos += i+1;

  return true;
}

//---------------------------------------------------------------------------
void Intro()
{
  std::cout << "---------------------------------------\n";
  std::cout << "\n";
  std::cout << "  Math Parser sample application\n";
  std::cout << "  (DLL version)\n";
  std::cout << "\n";
  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 << "Please enter a formula:\n";
}

//---------------------------------------------------------------------------
// Callback function for parser errors
void OnError()
{
  std::cout << "\nError:\n";
  std::cout << "------\n";
  std::cout << "Message:  " << mupGetErrorMsg() << "\n";
  std::cout << "Token:    " << mupGetErrorToken() << "\n";
  std::cout << "Position: " << mupGetErrorPos() << "\n";
  std::cout << "Errc:     " << mupGetErrorCode() << "\n";
}

//---------------------------------------------------------------------------
void ListVar(parser_handle a_hParser)
{
  int iNumVar = mupGetVarNum(a_hParser);

  if (iNumVar==0)
  {
    std::cout << "No variables defined\n";
    return;
  }

  std::cout << "\nExpression variables:\n";
  std::cout <<   "---------------------\n";
  std::cout << "Number: " << iNumVar << "\n";
  
  for (int i=0; i<iNumVar; ++i)
  {
    const char *szName = 0;
    double *pVar = 0;
    mupGetVar(a_hParser, i, &szName, &pVar);
    std::cout << "Name: " << szName << "   Address: [0x" << pVar << "]\n";
  }
}

//---------------------------------------------------------------------------
void ListExprVar(parser_handle a_hParser)
{
  int iNumVar = mupGetExprVarNum(a_hParser);

  if (iNumVar==0)
  {
    std::cout << "Expression dos not contain variables\n";
    return;
  }

  std::cout << "\nExpression variables:\n";
  std::cout <<   "---------------------\n";
  std::cout << "Expression: " << mupGetExpr(a_hParser) << "\n";
  std::cout << "Number: " << iNumVar << "\n";
  
  for (int i=0; i<iNumVar; ++i)
  {
    const char *szName = 0;
    double *pVar = 0;
    mupGetExprVar(a_hParser, i, &szName, &pVar);
    std::cout << "Name: " << szName << "   Address: [0x" << pVar << "]\n";
  }
}

//---------------------------------------------------------------------------
void ListConst(parser_handle a_hParser)
{
  int iNumVar = mupGetConstNum(a_hParser);

  if (iNumVar==0)
  {
    std::cout << "No constants defined\n";
    return;
  }

  std::cout << "\nParser constants:\n";
  std::cout <<   "---------------------\n";
  std::cout << "Number: " << iNumVar << "\n";

  for (int i=0; i<iNumVar; ++i)
  {
    const char *szName = 0;
    double fVal = 0;
    mupGetConst(a_hParser, i, &szName, fVal);
    std::cout << "  " << szName << " = " << fVal << "\n";
  }
}

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

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

  return false;
}

//---------------------------------------------------------------------------
void Calc()
{
  char szLine[100];
  double fVal = 0,
         afVarVal[] = { 1, 2 }; // Values of the parser variables
  parser_handle hParser;

  hParser = mupInit();              // initialize the parser

  // Set an error handler [optional]
  // the only function that does not take a parser instance handle
  mupSetErrorHandler(OnError);
  
  // Set a variable factory
  mupSetVarFactory(hParser, AddVariable);

  // Define parser variables and bind them to C++ variables [optional]
  mupDefineConst(hParser, "const1", 1);  
  mupDefineConst(hParser, "const2", 2);
  mupDefineStrConst(hParser, "strBuf", "Hallo welt");

  // Define parser variables and bind them to C++ variables [optional]
  mupDefineVar(hParser, "a", &afVarVal[0]);  
  mupDefineVar(hParser, "b", &afVarVal[1]);

  // Define postfix operators [optional]
  mupDefinePostfixOprt(hParser, "M", Mega);
  mupDefinePostfixOprt(hParser, "m", Milli);

  // Define infix operator [optional]
  mupDefineInfixOprt(hParser, "!", Not);

  // Define functions [optional]
  mupDefineStrFun(hParser, "query", SampleQuery, false); // Add an unoptimizeable function 
  mupDefineFun1(hParser, "rnd", Rnd, false);             // Add an unoptimizeable function
  mupDefineFun1(hParser, "rnd2", Rnd); 
  mupDefineMultFun(hParser, "_sum", Sum);  // "sum" is already a default function

  // Define binary operators [optional]
  mupDefineOprt(hParser, "add", Add, 0);
  mupDefineOprt(hParser, "mul", Mul, 1);

  // Add support for detection of binary values
  mupAddValIdent(hParser, IsBinVal);

  while ( fgets(szLine, 99, stdin) )
  {
    szLine[strlen(szLine)-1] = 0; // overwrite the newline

    if (CheckKeywords(szLine, hParser)) 
      continue;

    mupSetExpr(hParser, szLine);

    fVal = mupEval(hParser);

/*  
    // Without an Error handler function 
    // you must use this for error treatment:
    if (mupError())
    {
      std::cout << "\nError:\n";
      std::cout << "------\n";
      std::cout << "Message:  " << mupGetErrorMsg() << "\n";
      std::cout << "Token:    " << mupGetErrorToken() << "\n";
      std::cout << "Position: " << mupGetErrorPos() << "\n";
      std::cout << "Errc:     " << mupGetErrorCode() << "\n";
      continue;
    }
*/
    if (!mupError())
      std::cout << fVal << "\n";

  } // while 

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

//---------------------------------------------------------------------------
int main(int, char*)
{
  Intro();
  Calc();
}
