using uCalcSoftware;

var uc = new uCalc();
Console.WriteLine("------ Basic examples -------");
uc.Define("Function: f(x, y) = x + y");
uc.Define("Operator: {x} %% {y} = x * y");
uc.Define("Variable: MyVar = 123");

Console.WriteLine(uc.Eval("f(5, 10)"));
Console.WriteLine(uc.Eval("5 %% 10"));
Console.WriteLine(uc.Eval("MyVar * 100"));

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

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

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

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

// Overwrite - useful for spreadsheet-like functionality
Console.WriteLine("------ Overwrite -------");
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()");
Console.WriteLine(uc.Eval("SpreadsheetCell_A1()"));
Console.WriteLine(uc.Eval("SpreadsheetCell_B2()"));
Console.WriteLine(uc.Eval("SpreadsheetCell_C3()"));
// 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");
Console.WriteLine("-------");
// Note: Empty parenthesis are optional for functions with no parameters
Console.WriteLine(uc.Eval("SpreadsheetCell_A1"));
Console.WriteLine(uc.Eval("SpreadsheetCell_B2"));
Console.WriteLine(uc.Eval("SpreadsheetCell_C3"));

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

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

// Precedence
Console.WriteLine("------ Precedence -------");
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");
Console.WriteLine(uc.Eval("5 OpA 4 * 10"));
Console.WriteLine(uc.Eval("5 OpB 4 * 10"));

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