
// Calc.java
// Andrew Davison, ad@coe.psu.ac.th, March 2023

/* A calculator using a grid with 5 columns.

   The first row is a display label
   that spans all the columns.

   The next four rows are buttons containing:
        "7", "8",  "9",  "*",   "+",
        "4", "5",  "6",  "/",   "-",
        "1", "2",  "3",  "1/x", SQROOT_SYM,
        "0", ".",  "C",  "<-",  "=",

   Then a close button.

   Evaluation of the input equation is carried out
   by mxParser (https://mathparser.org/). A simple
   example of how to use it is in EvalExpr.java

   Uses Lanterna 3
   https://github.com/mabe02/lanterna

   Usage (Windows):
     > compile.bat Calc.java
     > run.bat Calc

   Usage (Linux):
     > ./compile.sh Calc.java
     > ./run.sh Calc
*/

import java.io.*;
import java.util.*;

import com.googlecode.lanterna.*;
import com.googlecode.lanterna.gui2.*;
import com.googlecode.lanterna.gui2.dialogs.*;
import com.googlecode.lanterna.gui2.Button.*;
import com.googlecode.lanterna.screen.*;
import com.googlecode.lanterna.terminal.*;

import org.mariuszgromada.math.mxparser.*;


public class Calc
{
  private static final int DISPLAY_LEN = 30;

  private static final String SQROOT_SYM = "\u221A";

  private static final String[] OPS = 
          { "/", "*", "-", "+", "=", "1/x", SQROOT_SYM}; 


  private static Label display;
  private static String expr = "";
  private static boolean labelWasOp = false;


  public static void main(String[] args)
                            //    throws IOException
  {
    License.iConfirmNonCommercialUse("Andrew");  
            // required by mXparser
 
    // build screen, gui, window
    Screen screen = Utils.createScreen();
    MultiWindowTextGUI gui = 
               new MultiWindowTextGUI(screen);
    Window window = new BasicWindow("Toy Calculator");

    // a grid with 5 columns
    Panel p = new Panel(new GridLayout(5));

    // the display field
    display = new Label(padLeft("0", DISPLAY_LEN));
    display.setLayoutData(
       GridLayout.createHorizontallyFilledLayoutData(5));
    p.addComponent(display.withBorder(Borders.singleLine()));


    // buttons
    String[] labels = { "7", "8",  "9",  "*",   "+",
                        "4", "5",  "6",  "/",   "-",
                        "1", "2",  "3",  "1/x", SQROOT_SYM,
                        "0", ".",  "C",  "<-",  "=",
                      };
    addButtons(p, labels);

    // close button
    Button closeBut = new Button("Close", window::close);
    closeBut.setLayoutData(
        GridLayout.createHorizontallyEndAlignedLayoutData(5));
    closeBut.setRenderer( new BorderedButtonRenderer());
    p.addComponent(closeBut);

    // link panel, window, and gui
    window.setComponent(p);
    gui.addWindowAndWait(window);

    Utils.close(screen);
  } /* main */



  public static void addButtons(Panel p, String[] labels)
  // all the buttons call updateDisplay()
  {
    for(String label : labels) {
      Button b = new Button(label, () -> updateDisplay(label));
      b.setRenderer( new BorderedButtonRenderer() );
      p.addComponent(b);
    }
  }  // end of addButtons()


  private static void updateDisplay(String lbl)
  {
    String numStr = display.getText().trim();  // number string
/*  
    System.err.println("label = \"" + lbl + "\"");
    System.err.println("numStr = " + numStr);
    System.err.println("expr = \"" + expr + "\"\n");
*/
    if (lbl.equals("C"))  // cancel
      display.setText(padLeft("0", DISPLAY_LEN));
    else if (lbl.equals("<-")) {  // backspace
      if (numStr.length() == 1)
        numStr = "0";
      else
        numStr = numStr.substring(0, numStr.length() - 1);
      display.setText(padLeft(numStr, DISPLAY_LEN));
    }
    else if (Arrays.asList(OPS).contains(lbl)) {   // operators
      if (lbl.equals("=")) {
        expr = expr + numStr;  // add number to expr
        display.setText(padLeft(evalExpr(expr), DISPLAY_LEN));
        expr = ""; 
      }
      else if (lbl.equals("1/x")) {
        expr = expr + "(1/" + numStr + ")";
        display.setText(padLeft(evalExpr(expr), DISPLAY_LEN));
        expr = ""; 
      }
      else if (lbl.equals(SQROOT_SYM)) {
        expr = expr + "sqrt(" + numStr + ")";
        display.setText(padLeft(evalExpr(expr), DISPLAY_LEN));
        expr = ""; 
      }
      else  // binary ops (/, *, +, -)
        expr = expr + numStr + lbl;  // add number and operator to expr

      labelWasOp = true;
    }
    else  {  // label must be a digit
      if (numStr.equals("0") || labelWasOp) {   
        // default numerical value or new number after op
        
        numStr = (lbl.equals(".")) ? "0." : ""+lbl;  // overwrite "0"
        labelWasOp = false;
        display.setText(padLeft(numStr, DISPLAY_LEN));
      }
      else if (numStr.length() < DISPLAY_LEN) {
        numStr = numStr+lbl;  // build up number
        display.setText(padLeft(numStr, DISPLAY_LEN));
      }
    }
  }  // end of updateDisplay()


  public static String padLeft(String s, int n) 
  {  return String.format("%" + n + "s", s);  }



  private static String evalExpr(String expr)
  {
    // System.err.println("Evaluating: " + expr);
    Expression e = new Expression(expr); // calls mXparser
    return Double.toString(e.calculate());
  }  // end of evalExpr()


}  // end of Calc class
