#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   cout << "------ Basic examples -------" << endl;
   uc.Define("Function: f(x, y) = x + y");
   uc.Define("Operator: {x} %% {y} = x * y");
   uc.Define("Variable: MyVar = 123");

   cout << uc.Eval("f(5, 10)") << endl;
   cout << uc.Eval("5 %% 10") << endl;
   cout << uc.Eval("MyVar * 100") << endl;

   // End users can also call Define()
   cout << "------ End user definition -------" << endl;
   uc.Eval("Define('Function: ff(x) = x * 1000')");
   cout << uc.Eval("ff(987)") << endl;

   cout << "------ Boolean format -------" << endl;
   cout << uc.EvalStr("1 < 2") << endl;
   cout << uc.EvalStr("1 > 2") << endl;
   cout << uc.EvalStr("Int(1 < 2)") << endl;
   cout << uc.EvalStr("(True Or False) And (True And False)") << endl;
   uc.Define("Boolean: 55, TotallyTrue, CompletelyFalse");
   cout << uc.EvalStr("1 < 2") << endl;
   cout << uc.EvalStr("1 > 2") << endl;
   cout << uc.EvalStr("Int(1 < 2)") << endl;
   cout << uc.EvalStr("(TotallyTrue Or CompletelyFalse) And (TotallyTrue And CompletelyFalse)") << endl;

   // Format - configures the formatting for the output given by EvalStr
   cout << "------ Format -------" << endl;
   uc.Define("Format: Result = 'Answer: <' + Result + '>'");
   uc.Define("Format: String, val = 'String Value --> ' + val");
   cout << uc.EvalStr("1+2") << endl;
   cout << uc.EvalStr("'Hello ' + 'world!'") << endl;
   auto Additional = uc.Define("Format: ret = 'Additional format: ' + ret");
   cout << uc.EvalStr("1+2") << endl;
   uc.Define("Format: InsertAt: 0, result = 'The ' + result");
   cout << uc.EvalStr("1+2") << endl;
   Additional.Release();
   cout << uc.EvalStr("1+2") << endl;
   uc.FormatRemove();

   // Bootstrap - builds new def on top of existing one
   cout << "------ Bootstrap -------" << endl;
   cout << uc.EvalStr("Hex(123)") << endl; // uses "built-in" version of Hex()
   auto MyHex = uc.Define("Bootstrap ~~ Function: Hex(number As Int) As String = '0x' + UCase(Hex(number))");
   cout << uc.EvalStr("Hex(123)") << endl;
   MyHex.Release();
   cout << uc.EvalStr("Hex(123)") << endl;

   // Overwrite - useful for spreadsheet-like functionality
   cout << "------ Overwrite -------" << endl;
   uc.Define("Overwrite ~~ Func: SpreadsheetCell_A1() = 5");
   uc.Define("Overwrite ~~ Func: SpreadsheetCell_B2() = SpreadsheetCell_A1() * 10");
   uc.Define("Overwrite ~~ Func: SpreadsheetCell_C3() = SpreadsheetCell_A1() + SpreadsheetCell_B2()");
   cout << uc.Eval("SpreadsheetCell_A1()") << endl;
   cout << uc.Eval("SpreadsheetCell_B2()") << endl;
   cout << uc.Eval("SpreadsheetCell_C3()") << endl;
   // SpreadsheetCell_C3() will be affected by the definition changes of SpreadsheetCell_A1() and  SpreadsheetCell_B3()
   uc.Define("Overwrite ~~ Func: SpreadsheetCell_B2() = SpreadsheetCell_A1() * 100");
   uc.Define("Overwrite ~~ Func: SpreadsheetCell_A1() = 25");
   cout << "-------" << endl;
   // Note: Empty parenthesis are optional for functions with no parameters
   cout << uc.Eval("SpreadsheetCell_A1") << endl;
   cout << uc.Eval("SpreadsheetCell_B2") << endl;
   cout << uc.Eval("SpreadsheetCell_C3") << endl;

   // Lock
   cout << "------ Lock -------" << endl;
   uc.Define("Variable: xy = 555");
   uc.Define("Lock ~~ Variable: pi = 3.14"); // like using DefineConstant()
   cout << uc.EvalStr("xy") << endl;
   cout << uc.EvalStr("pi") << endl;
   cout << uc.EvalStr("xy = 222") << endl; // This variable is being changed
   cout << uc.EvalStr("pi = 3.14159") << endl; // This one is locked and cannot be changed
   cout << uc.EvalStr("xy") << endl; // Returns the new value of xy
   cout << uc.EvalStr("pi") << endl; // pi did not change; returns original value
   cout << "-------" << endl;
   uc.Define("Function: f1(x) = x + 100");
   uc.Define("Lock ~~ Function: f2(x) = x + 200"); // End-user can't change f2
   cout << uc.Eval("f1(5)") << endl;
   cout << uc.Eval("f2(5)") << endl;
   uc.Eval("Define('Function: f1(x) = x + 300')");
   uc.Eval("Define('Function: f2(x) = x + 400')"); // This re-definition is ignored
   cout << uc.Eval("f1(5)") << endl;
   cout << uc.Eval("f2(5)") << endl;

   // Tokens
   cout << "------ Tokens -------" << endl;
   cout << uc.EvalStr("5 + 4 // This comment causes an error") << endl;
   cout << uc.EvalStr("5 + /* comment not recognized yet */ 10") << endl;
   uc.Define("TokenType: Whitespace ~~ Token: //.*"); // // C-style to end-of-line comment
   uc.Define("TokenType: Whitespace ~~ Token: /[*].*?[*]/"); // /* C-style enclosed comment */
   cout << uc.EvalStr("5 + 4 // This comment will be ignored") << endl;
   cout << uc.EvalStr("5 + /* comment ignored */ 10") << endl;

   // Precedence
   cout << "------ Precedence -------" << endl;
   uc.Define("Precedence: 1    ~~ Operator: {a As Int32} OpA {b As Int32} = a + b");
   uc.Define("Precedence: 1000 ~~ Operator: {a As Int32} OpB {b As Int32} = a + b");
   cout << uc.Eval("5 OpA 4 * 10") << endl;
   cout << uc.Eval("5 OpB 4 * 10") << endl;

   // Associativity
   cout << "------ Associativity -------" << endl;
   uc.Define("Associativity: LeftToRight ~~ Operator: {x} OpX {y} = x / y");
   uc.Define("Associativity: RightToLeft ~~ Operator: {x} OpY {y} = x / y");
   cout << uc.Eval("3 OpX 4 OpX 5") << endl;
   cout << uc.Eval("3 OpY 4 OpY 5") << endl;
}