using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using uCalcSoftware;

namespace uCalcDemoCS
{
    public partial class Form1 : Form
    {
              
        public Graphics g;
        public Pen myPen = new Pen(Color.Black);
        public uCalc uc = new uCalc();

        static bool DisplayAgain = true;
        static void MyErrorHandler(IntPtr uCalcHandle)
		{
            var uc = new uCalc(uCalcHandle);
            if (DisplayAgain)
            {
                var Option = MessageBox.Show(
                  "uCalc Error (this message box is part of the demo)\n\n"
                + "Error Number: " + uc.ErrorNumber().ToString() + "  [" + Enum.GetName(typeof(ErrorNumberEnum), uc.ErrorNumber()) + "]\n"
                + "Bad expression: " + uc.ErrorExpression() + "\n"
                + "Error Handler message: " + uc.ErrorMessage() + "\n"
                + "Offending symbol: " + uc.ErrorSymbol() + "\n"
                + "Error Location: " + uc.ErrorLocation().ToString() + "\n\n"
                + "See MyErrorHandler(), in MyFunctions.vb, for error handler source code.\n\n"
                + "Show this message next time?", "uCalc Error (this message box is part of the demo)",MessageBoxButtons.YesNoCancel);
                if (Option == DialogResult.No) DisplayAgain = false;
            }
		}

        static String NumericFormat = "G";
        static void MyNumericFormat(IntPtr ExprPartPtr)
        {
            var ep = new uCalc.Callback(ExprPartPtr);
            var ResultAsString = ep.ArgStr(1);

            try {
                ep.ReturnStr(Convert.ToDouble(ResultAsString).ToString(NumericFormat));
            } catch {
                ep.ReturnStr(ResultAsString); // For special values like 1.#INF, -1.#IND, etc.
            }            
        }

        static void MyArea(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
            var MyLength = ep.Arg(1);
            var MyWidth = ep.Arg(2);

            if (MyLength < 0) ep.ErrorRaiseMessage("Length cannot be negative");
            if (MyWidth < 0) ep.ErrorRaiseMessage("Width cannot be negative");

            ep.Return(MyLength * MyWidth);
		}

        static void MyIIf_Numeric(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
            var Condition = ep.Arg(1);
            var TruePart = ep.ArgExpr(2);
            var FalsePart = ep.ArgExpr(3);

			if(Condition != 0)
                ep.Return(TruePart.Evaluate());
            else
                ep.Return(FalsePart.Evaluate());
		}

        static void MyIIf_String(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
            var Condition = ep.Arg(1);
            var TruePart = ep.ArgExpr(2);
            var FalsePart = ep.ArgExpr(3);

			if(Condition != 0)
                ep.ReturnStr(TruePart.EvaluateStr());
            else
                ep.ReturnStr(FalsePart.EvaluateStr());
		}

        static void MyMsgBox(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr); // Args 2 & 3 inverted because it's based on VB MsgBox syntax
            ep.Return((int)MessageBox.Show(ep.ArgStr(1), ep.ArgStr(3), (MessageBoxButtons)ep.Arg(2)));
		}

        static void MyAverage(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
			double Total = 0;
            
            for (int x = 1; x <= ep.ArgCount(); x++)
                Total = Total + ep.Arg(x);

            ep.Return(Total / ep.ArgCount());
		}

        static void MyLeft(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
            ep.ReturnStr(ep.ArgStr(1).Substring(0, (int)ep.Arg(2)));
		}

        static void StringRepeat(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
			string TotalString = "";

            for (int x = 1; x <= ep.Arg(2); x++)
                TotalString = TotalString + ep.ArgStr(1);

            ep.ReturnStr(TotalString);
		}

        static void ucSum(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
            double Total = 0;

            var Expression = ep.ArgExpr(1);
            var Start = ep.Arg(2);
            var Finish = ep.Arg(3);
            var sStep = ep.Arg(4);
            var VarHandle = ep.ArgObj(5);

            for (double x = Start; x <= Finish; x += sStep) 
			{
                VarHandle.Value(x);
                Total = Total + Expression.Evaluate();
				x = x + sStep;
			}

            ep.Return(Total);
		}

        static void ucSolve(IntPtr ExprPartPtr)
		{
            var ep = new uCalc.Callback(ExprPartPtr);
			int Iterations = 0;
			double fa, fb, Value = 0, tmp;

            var Expression = ep.ArgExpr(1);
            var a = ep.Arg(2);
            var b = ep.Arg(3);
            var Variable = ep.ArgObj(4);

			Variable.Value(a);
            fa = Expression.Evaluate();

            Variable.Value(b);
            fb = Expression.Evaluate();
   
			if(fb < fa) {tmp = a; a = b; b = tmp;}  // swap a, b
    
			while(Math.Abs(b - a) > 0.000000000000001)
			{
				Variable.Value((a + b) / 2);

                Value = Expression.Evaluate();
      
				if(Value == 0) {a = (a + b) / 2; break;}

				if(Value < 0) a = (a + b) / 2; else b = (a + b) / 2;

				Iterations = Iterations + 1;
				if(Iterations == 100) break;
			}

            if (Math.Abs(Value) > 0.0000000001) ep.ErrorRaiseMessage("Solution not found");
            ep.Return(a);
        }
        public Form1()
        {
            InitializeComponent();
        }

        // The definitions in Form1_Load are for "callback" routines.
        // It's important to use uc.PinAddr() on the callback functions even if it may sometimes
        // appear to work without it.  This prevents the garbage collector from removing the delegate.
        // The actual code body for these routines can be found elsewhere in this file.
        private void Form1_Load(object sender, EventArgs e)
        {
            g = PictureBox1.CreateGraphics();

            // The MyErrorHandler() function will be called whenever and wherever an error (such as "Syntax Error") is raised.	
            uc.AddErrorHandler(uc.PinAddr(MyErrorHandler));

            // Format lets you configure the formatting of results returned with EvalStr() or EvaluateStr()
            uc.Format(uc.PinAddr(MyNumericFormat), "Double");

            // MyArea() returns the product of multiplying the two arguments.
            // MyLeft() returns the left-most characters of a string.  The second argument represents the number of characters.
            // MsgBox() displays a message box.  The 1st argument is required.  The others are optional.
            uc.DefineFunction("MyArea(Length, Width)", uc.PinAddr(MyArea));
            uc.DefineFunction("MyLeft(Text As String, Count) As String", uc.PinAddr(MyLeft));
            uc.DefineFunction("MyMsgBox(Prompt As String, Buttons = 0, Title As String = 'uCalc')", uc.PinAddr(MyMsgBox));
            uc.ExpressionTransforms().FromTo("MsgBox ::= MyMsgBox");
         
            // The three consecutive dots "..." mean that MyAverage can take any number of arguments
            // (however, because "x" is specified, there must be at least one argument).
            uc.DefineFunction("MyAverage(x ...)", uc.PinAddr(MyAverage));

            // Two versions of MyIIF can co-exist peacefully with the same name because
            // they are defined with different argument types (numeric vs string).
            // By passing the last two arguments ByExpr, the callback can chose to evaluate only one of of them.  
            uc.DefineFunction("MyIIf(cond, ByExpr TruePart, ByExpr FalsePart)", uc.PinAddr(MyIIf_Numeric));
            uc.DefineFunction("MyIIf(cond, ByExpr TruePart As String, ByExpr FalsePart As String) As String", uc.PinAddr(MyIIf_String));

            // The following defines the "*" operator so that MyString * n returns MyString repeated n times.
            // For instance, "He " * 3, would return "He He He".  20 represents the precedence level.  It is
            // arbitrarily set at the same level as that of the numeric "*" multiplication operator.
            //uc.DefineOperator("{MyString As String} * {Number} As String", new uCalc.Item(uc.HandleOf("*")).Precedence(), GroupingEnum.GroupLeftToRight, uc.PinAddr(StringRepeat));
            uc.DefineOperator("{MyString As String} * {Number} As String", uc.ItemOf("*").Precedence(), GroupingEnum.GroupLeftToRight, uc.PinAddr(StringRepeat));

            // This definition is for a summation.  See the callback ucSum() routine for the
            // actual code, which runs a loop that adds up the total for the expression in
            // the first argument a number of times based on the second and third arguments.
            // For instance Sum(g^2+1, 1, 5, 1, g) returns 60 and Sum(x^2, 1, 10) returns 385.
            // The last two arguments are optional, so they default to 1 and x if omitted.
            //
            // The first argument is passed "ByExpr".  So instead of being evaluated before
            // being passed to the callback the way an ordinary argument would be, a handle
            // for the expression is passed so that the callback can evaluate it (in this
            // case numerous times).
            //
            // The last argument is passed "ByHandle".  This causes the callback to receive
            // a handle for the variable being passed, in such a way that it can be linked to
            // the summation counter, and integrated into the expression in the first argument.
            //
            // The actual function being defined is Sum_().  Then a syntax construct named
            // Sum() is defined in such a way that the last argument gets defined as a local
            // variable.  So if you evaluate Sum(x^2, 5, 10, 1, x), the local "x" in this
            // expression will not interfere with a pre-existing variable named x.  Also you
            // do not need to declare a variable ahead of time to use it as a counter for Sum().
            uc.DefineFunction("Sum_(ByExpr Expr, Start, Finish, Step, ByHandle Var)", uc.PinAddr(ucSum));
            uc.ExpressionTransforms().FromTo("Sum({Expr}, {Start}, {Finish} [, {Step=1} [, {Var=x}]])"
                    + "::= Local({Var}, Sum_({Expr}, {Start}, {Finish}, {Step}, {Var}))");

            // The following routine solves an equation.
            // The callback code is based on the Bisection Method algorithm.
            // The concept here is very similar to that of Sum().
            // Two syntax constructs are defined.  The second one rearranges the equation
            // if it includes an equal sign.
            // For instance Solve(x^2 = 9+x) becomes Solve(x^2 - (9+x))
            // Solve(x^2 + 1 = 26) returns 5.
            // Solve(x^2 + 1 = 26, -1000, 0) returns -5.
            uc.DefineFunction("Solve_(ByExpr Expr, a, b, ByHandle Var)", uc.PinAddr(ucSolve));
            uc.ExpressionTransforms().FromTo("Solve({Expr} [, {a=-100000000} [, {b=100000000} [, {Var=x}]]]) "
                            + "::= Local({Var}, Solve_({Expr}, {a}, {b}, {Var}))");
            uc.ExpressionTransforms().FromTo("Solve({Left} = {Right} [, {etc}]) ::= Solve({Left}-({Right}) {etc: , {etc}})");
        }

        private void btnEval_Click(object sender, EventArgs e)
        {
            // Eval() evaluates an expression and returns a numeric result.
            // EvalStr() can evaluate expressions of any data type (numeric, string, or other).
            // EvalStr() conveniently converts numeric results to strings for you.
            // EvalStr() can also return the text of an error message.

            txtResult.Text = uc.EvalStr(cmbExpression.Text);
        }

        private void btnDefine_Click(object sender, EventArgs e)
        {
            // Define() can define a variable, constant, function, operator,
            // syntax construct, pattern, data type, and more.

            uc.Define(cmbDefine.Text);
        }

        private void chkFPU_SelectedIndexChanged(object sender, EventArgs e)
        {
            var CheckedState = chkFPU.GetItemCheckState(chkFPU.SelectedIndex) == CheckState.Checked ? true : false;

            switch (chkFPU.SelectedIndex)
            {
                case 0: uc.RaiseErrorOnInvalid(CheckedState); break;
                case 1: uc.RaiseErrorOnDivideByZero(CheckedState); break;
                case 2: uc.RaiseErrorOnOverflow(CheckedState); break;
                case 3: uc.RaiseErrorOnUnderflow(CheckedState); break;
                case 4: uc.RaiseErrorOnInexact(CheckedState); break;
            }
        }

        private void btnSummation_Click(object sender, EventArgs e)
        {
            double SumTotal = 0, SumMax = Convert.ToDouble(txtSumMax.Text);
            Stopwatch MyStopWatch = Stopwatch.StartNew();           

            var VariableX = uc.DefineVariable("x");
            var Expression = uc.Parse(txtSumExpression.Text);
            
            for(double x = 1; x <= SumMax; x++)
            {
                VariableX.Value(x);
                SumTotal = SumTotal + Expression.Evaluate();                
            }
            MyStopWatch.Stop();

            txtSumResult.Text = SumTotal.ToString();
            lblElapsed.Text = "Elapsed Time:  " + Convert.ToString((double)MyStopWatch.ElapsedMilliseconds / 1000);

            Expression.Release();
            VariableX.Release();
        }

        public float OldX, OldY;
        private void CartesianLineTo(double x, double y)
        {
            float NewX, NewY;

            if (x == -10) { OldX = 0; OldY = 0; }

            NewX = (float)(PictureBox1.Size.Width * (x + 10) / 20);
            NewY = (float)(PictureBox1.Size.Height * (2 - y) / 4);

            g.DrawLine(myPen, OldX, OldY, NewX, NewY);

            OldX = NewX;
            OldY = NewY;
        }

        private void cmbNumericFormat_TextUpdate(object sender, EventArgs e)
        {
            NumericFormat = cmbNumericFormat.Text;
            if (NumericFormat == "Standard") NumericFormat = "N";
            if(NumericFormat == "Scientific") NumericFormat = "E";
            if(NumericFormat.ToUpper() != NumericFormat.ToLower()) NumericFormat = NumericFormat.Substring(0, 1);
        }

        private void cmbNumericFormat_SelectedIndexChanged(object sender, EventArgs e)
        {
            cmbNumericFormat_TextUpdate(sender, e);
        }

        private void btnPlot_Click(object sender, EventArgs e)
        {
            g.Clear(PictureBox1.BackColor);

            var Variable = uc.DefineVariable("x");
            var Equation = uc.Parse(txtPlotExpression.Text);           

            for(double x = -10; x <= 10; x += 0.125)
            {
                Variable.Value(x);                
                CartesianLineTo(x, Equation.Evaluate());                
            }

            Equation.Release();
            Variable.Release();
        }
    }
}
