// Michael Tartaglia
// 23 April 2002
//    This GUI based program allows the user to enter and
//	pop numbers into an array, sort them into ascending
//	or descending order, print out the highest and lowest
//	integers, display the average of all the digits, and
//      display the textual equivalent of the inputted numbers
//    NOTE: This is the latest version. Two bugs have been
//          fixed from the previous version (one which
//          disallowed the continuation of number entering
//          after the arrays were cleared), and the "sum"
//          feature has been added.
//


import java.awt.Graphics;
import java.applet.Applet;
import java.awt.Font;
import java.awt.*;
import java.awt.event.*;

public class NumbersFun extends Applet
implements ActionListener {
 private static final int MAX_ARRAY = 32, MAX_WIDTH = 355;
 private int index = 0,               // array index
             lowest = 0, highest = 0, // lowest/highest ints
	          yInit = 115, xInit = 20, // x,y coordinates
             yInc = 16, xInc = 38,    // x,y coord. increments
             rand;                    // for rand # generator
 private long sum = 0;                // contains numbers' sum
 private double average;              // # average

 // various arrays for numbers, sorted numbers, text versions
 //    of various numbers (0-19, 10-90 in tens, 100, 1000,
 //    1000000)
 private int nums[] = new int[MAX_ARRAY];
 private int sortedNums[] = new int[MAX_ARRAY];
 private String[] ones = {"zero","one","two","three",
	"four","five","six","seven","eight","nine","ten",
	"eleven","twelve","thirteen","fourteen","fifteen",
	"sixteen","seventeen","eighteen","nineteen"};
 private String[] tens = {"ten","twenty","thirty",
	"forty","fifty","sixty","seventy","eighty","ninety"};
 private String[] tenPow = {"","thousand"};

 private String ERROR = "";           // error string
 private TextField enteredNum;        // input
 private TextArea textbox;            // text numbers
 private Button enterKey,             // enter # button
                popNum,               // pop number
                randomKey,            // enter random #
                rel,                  // reload screen
                CLEAR;                // clear all data
 private CheckboxGroup sortOpt;       // sort button group
 private Checkbox sort,               // desire sort box
                  sortDOWN, sortUP,   // sort direction
                  info;               // hi, low, avg option
 private Choice selectText;           // selection box


 public void init() {
   setBackground(Color.white);

   setLayout(new FlowLayout(FlowLayout.CENTER,1,1));

	// PLACE INSTRUCTIONS
   Panel instruct = new Panel();
   instruct.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
   Label l = new Label("Enter # and hit add!");
   l.setFont(new Font("Arial", Font.ITALIC + Font.BOLD, 12));
   instruct.add(l);
   add(instruct);

	// PLACE FORM ELEMENTS
   Panel insertNum = new Panel();
   insertNum.setBackground(new Color(0,0,128));
   insertNum.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
   enteredNum = new TextField(2);
   enteredNum.setText("");
   enteredNum.setEditable(true);
   enteredNum.addActionListener(this);
   enteredNum.setBackground(new Color(255,255,255));
   insertNum.add(enteredNum);
   enterKey = new Button("Add");
   enterKey.setBackground(new Color(225,225,225));
   enterKey.setEnabled(true);
   enterKey.addActionListener(this);
   insertNum.add(enterKey);
   randomKey = new Button("Add Random #");
   randomKey.setBackground(new Color(0,235,0));
   randomKey.setEnabled(true);
   randomKey.addActionListener(this);
   insertNum.add(randomKey);
   popNum = new Button("Pop");
   popNum.setBackground(new Color(235,235,0));
   popNum.setEnabled(false);
   popNum.addActionListener(this);
   insertNum.add(popNum);
   add(insertNum);

	// PLACE "CLEAR EVERYTHING" PANEL
   Panel CLEARALL = new Panel();
   CLEARALL.setBackground(Color.red);
   CLEARALL.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
   CLEAR = new Button("Clear");
   CLEAR.setEnabled(false);
   CLEAR.addActionListener(this);
   CLEARALL.add(CLEAR);
   add(CLEARALL);

	// PLACE SORT FEATURE PANEL
   Panel chooseSort = new Panel();
   chooseSort.setBackground(new Color(225,225,225));
   chooseSort.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
   sort = new Checkbox("Sort? ");
   sortOpt = new CheckboxGroup();
   sortUP = new Checkbox("up", sortOpt, true);
   sortDOWN = new Checkbox("down", sortOpt, false);
   chooseSort.add(sort);
   chooseSort.add(sortUP);
   chooseSort.add(sortDOWN);
   add(chooseSort);

	// PLACE STATISTICS PANEL
   Panel stats = new Panel();
   stats.setBackground(new Color(225,225,225));
   stats.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
   info = new Checkbox("Show # info?");
   stats.add(info);
   add(stats);
   
	// PLACE RELOAD PANEL
   Panel RELOAD = new Panel();
   RELOAD.setBackground(new Color(185,185,185));
   RELOAD.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
   rel = new Button("Refresh");
   rel.setEnabled(true);
   rel.addActionListener(this);
   RELOAD.add(rel);
   add(RELOAD);

	// PLACE TEXT AREA PANELS
   Panel text = new Panel();
   text.setBackground(new Color(225,225,225));
   text.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
   textbox = new TextArea("",1,25,2);
   textbox.setEditable(false);
   textbox.setBackground(new Color(255,255,255));
   text.add(textbox);
   selectText = new Choice();
   selectText.addItem("Show number as text?");
   selectText.addItem("All numbers to text.");
   selectText.addItem("Last number to text.");
   selectText.select(0);
   text.add(selectText);
   add(text);
 } // END init();



 public void actionPerformed(ActionEvent E)
 throws IllegalArgumentException {
   /* INPUT: action event
      OUTPUT: none
      FUNCTION: checks which button has been pressed
                and associates functions with that
                action; also does error checking,
                errors saved into strings to be 
                printed in the applet */
   if (E.getSource() == randomKey) {		// RANDOM #
      int x = (int)Math.round(Math.random()*1000);
      addNumber(Math.abs(x));
   }

   else if (E.getSource() == enterKey
            || E.getSource() == enteredNum) {	// ADD USER #
      int x;
      try {
         x = Integer.parseInt(enteredNum.getText());
         if (x > 99999 || x < -9999)
            throw new IllegalArgumentException();
         addNumber(x);
      } catch(NumberFormatException s) {
         ERROR = "Your input isn\'t an integer!  Try another.";
      } catch(IllegalArgumentException ia) {
         ERROR = "Enter number between -9999 and 99999.";
      }
   }

   else if (E.getSource() == popNum) {         // POPPING #
      popNumber();
   }

   else if (E.getSource() == rel) {            // RELOADING
      if (!info.getState() && !sort.getState()
         && selectText.getSelectedIndex() == 0)
         ERROR = "Warning: No task selected!";
      else if (info.getState() && index < 2)
         ERROR = "Too few numbers to display worthwhile information!";
      else if (sort.getState()) {
         if (index == 0) ERROR = "No numbers to sort! Enter something!";
         else if (index == 1) ERROR = "Too few numbers to sort! Enter more!";}
      else if (selectText.getSelectedIndex() != 0 && index == 0)
         ERROR = "No numbers available to convert to text!";
  }

  else if (E.getSource() == CLEAR) {           // CLEARING
      for (int i = 0; i < index; ++i) {
         nums[i] = 0; sortedNums[i] = 0;
      }
      index = 0;
      CLEAR.setEnabled(false);		// DISABLE CLEAR BUTTON
      enterKey.setEnabled(true);		// ENABLE ENTER KEY
      randomKey.setEnabled(true);	// ENABLE RANDOM KEY
      enteredNum.setText("");			// CLEAR ENTERED NUMBERS FIELD
      enteredNum.setEditable(true);	// LET FIELD BE EDITABLE
      textbox.setText("");				// CLEAR TEXTBOX
      popNum.setEnabled(false);		// DISABLE "POP" BUTTON
      info.setState(false);			// DISABLE INFO OPTION
      sort.setState(false);			// DISABLE SORT OPTION
      selectText.select(0);			// RESET SELECTION
   }
   repaint();
 } // END actionPerformed()



 public void addNumber(int x) {
   /* INPUT: integer to add
      OUTPUT: none
      FUNCTION: adds element to sorted and
                input arrays - one array keeps
                original order, the other is
                modifiable for sorting */
   if (index+1 >= MAX_ARRAY) {
      ERROR = "Warning: Array is now filled!";
      enteredNum.setEditable(false);
      enterKey.setEnabled(false);
      randomKey.setEnabled(false);
   }

   String inbox = "";
   nums[index] = x;
   sortedNums[index] = x;
   ++index;

   if (index < MAX_ARRAY)
      // put number back in entry box only if popped
      inbox = (nums[index] == 0 ? "" : ""+nums[index]);

   sum += (long)x;

   enteredNum.setText(inbox);
   CLEAR.setEnabled(true);
   popNum.setEnabled(true);
 } // END addNumber()



 public void popNumber() {
   /* INPUT: none
      OUTPUT: none
      FUNCTION: gives user the ability to take
                out of the array only the last
                number entered; the number is 
                taken off of both arrays; allows
                for editing numbers after entry */
   int numToPop = nums[index-1];
   for (int i = 0; i < index; ++i) {
      if (sortedNums[i] == numToPop) {
         for (int j = i+1; j < index; ++j)
            sortedNums[i] = sortedNums[j];
         break;
      }
   }
   index--;
   enteredNum.setText(""+nums[index]);
   sum -= (long)nums[index];
   if (index == 0) popNum.setEnabled(false);
   if (index < MAX_ARRAY) {
      randomKey.setEnabled(true);
      enteredNum.setEditable(true);
      enterKey.setEnabled(true);
   }
 }



 public void sortNums() {
   /* INPUT: none
      OUTPUT: none
      FUNCTION: sorts numbers in ascending
                or descending order using
                selection sorting */
   int temp;     // holding the variable
   for (int i = 0; i < index; ++i) {
      for (int j = 0; j < index; ++j) {
         if (sortUP.getState()) {
            if (sortedNums[i] < sortedNums[j]) {
               temp = sortedNums[i];
               sortedNums[i] = sortedNums[j];
               sortedNums[j] = temp;
         }  }
         else if (sortDOWN.getState()) {
            if (sortedNums[i] > sortedNums[j]) {
               temp = sortedNums[i];
               sortedNums[i] = sortedNums[j];
               sortedNums[j] = temp;
   }  }  }  }
 } // END sortNums()



 public void paint(Graphics g) {
   /* INPUT: graphics modue
      OUTPUT: none
      FUNCTION: repaints text onto screen */
   int y = yInit,    // where writing starts on y
       x = xInit;    // where writing starts on x
   if (selectText.getSelectedIndex() != 0 && index > 0)
       printText();								// PRINT ENGLISH NUMBERS, IF DESIRED
   else textbox.setText("");
   if (ERROR != "") {							// PRINT ERROR MESSAGE, IF IT EXISTS
      resetFont(g, 2);
      g.drawString(ERROR, x, y);
   }
   y += yInc;
   resetFont(g, 0);
   g.drawString("Your numbers: ", x, y);
   y += yInc;
   y = printArray(g, nums, x, y);
   if (sort.getState() && index >= 2) {	// PRINT SORTED NUMBERS, IF USER DESIRES
      sortNums();
      y += yInc;
      resetFont(g, 0);
      g.drawString("Your numbers, sorted: ", x, y);
      y += yInc;
      y = printArray(g, sortedNums, x, y);
   }
   if (info.getState() && index >= 2) {	// PRINT NUMBER INFO, IF USER DESIRES
      y = displayHiAndLow(g, x, y);
   }
   ERROR = ""; // clear error now
 } // END paint()



 public int displayHiAndLow(Graphics g, int x, int y) {
   /* INPUT: graphics module g, (x,y) coordinate
      OUTPUT: final y position which would become
              the initial y position for next painted
              function
      FUNCTION: calculates the higest, lowest and
                average of all the numbers entered */
   lowest = nums[0];   // set low for comparisons
   highest = nums[0];  //  "  high  "       "
   average = 0;        // reset average
   for (int i = 0; i < index; i++) {
      if (lowest > nums[i]) lowest = nums[i];
      if (highest < nums[i]) highest = nums[i];
      average += nums[i]; 
   }
   average /= index;
   y += yInc;
   resetFont(g, 0); g.drawString("Lowest: ", x, y);
   resetFont(g, 1); g.drawString(""+lowest, x+150, y);
   y += yInc;
   resetFont(g, 0); g.drawString("Highest: ", x, y);
   resetFont(g, 1); g.drawString(""+highest, x+150, y);
   y += yInc;
   resetFont(g, 0); g.drawString("Average: ", x, y);
   resetFont(g, 1); g.drawString(""+average, x+150, y);
   y += yInc;
   resetFont(g, 0); g.drawString("Sum: ", x, y);
   resetFont(g, 1); g.drawString(""+sum, x+150, y);
   y += yInc;
   resetFont(g, 0); g.drawString("Entered Numbers: ", x, y);
   resetFont(g, 1); g.drawString(""+index, x+150, y);
   return y;
 } // END displayHiAndLow()



 public int printArray(Graphics g, int[] a, int x, int y) {
   /* INPUT: graphics module, array to print, (x,y) coordinate
      OUTPUT: final y position
      FUNCTION: neatly prints out array's elements onto
                the screen */
   resetFont(g, 1);
   int xOrig = x;     // original x position for returns 
   for (int i = 0; i < index; ++i) {
      if (x+xInc > MAX_WIDTH) {
         x = xOrig;
         y += yInc;
      }
      x += xInc;
      g.drawString(String.valueOf(a[i]), x, y);
   }
   return y;	// needs to return a y offset
 } // END printArray()



 public void printText() {
   /* INPUT: none
      OUTPUT: none
      FUNCTION: gets array's elements in text form
                and puts it into text box in GUI */
   String txt = "You entered: ";
   if (selectText.getSelectedIndex() == 1)
      for (int i = 0; i < index; ++i) 
         txt += toText(nums[i], i);
   else if (selectText.getSelectedIndex() == 2)
      txt += toText(nums[index-1], 0);
   textbox.setText(txt+".");
 } // END printText()



 private String toText(int num, int j) {
   /* INPUT: number to text, number position in array
      OUTPUT: number as text string (not int-like string)
      FUNCTION: helps printText by converting
                individual numbers into their written
                equivalent */
   String t = "",      // string to return
          space,       // word spacing
          numString = num+"";  // input number
   int numCopy = num,  // input number as int
       position = 0;   // current group of thousands
   int nSegs[] = new int[(int)Math.ceil(numString.length()/3)+1];
   if (j != 0) t = ", ";
   if (num < 0) {
      t += "negative ";
      num = Math.abs(num);
   }
   for (int i = 0; num > 0; i++, num = (int)Math.floor(num/1000))
      nSegs[i] = num % 1000;
   position = nSegs.length-1;
   if (numCopy == 0) t += ones[0];
   else while (position >= 0) { // get print-out
      space = (position == 0 ? "" : " ");
      t +=  toTextHundreds(nSegs[position]);
      if (nSegs[position] != 0) t += space + tenPow[position];
      --position;
   }
   return t;
 } // END toText()



 private String toTextHundreds(int num2) {
   /* INPUT: three-digit number to text
      OUTPUT: number as text string (not int-like string)
      FUNCTION: helps toText by converting numbers into
                text, by groups of three digits - one
                group at a time is passed from toText */
   int numCopy2 = num2;    // copy of entry
   String t2 = "";         // string to return
   int n[] = new int[3];   // digits
   for (int i = 0; i < 3; ++i) {
      n[i] = numCopy2%10;
      numCopy2 = (int)Math.floor(numCopy2/10);
   }
   if (num2 == 0) return t2;
   else if (num2 < 20 && num2 > 0) t2 += ones[num2];
   else if (num2 < 100 && num2%10 == 0) t2 += tens[(num2/10)-1];
   else if (num2 >= 100 && num2%100 == 0) t2 += ones[n[2]] + " hundred";
   else if (num2 < 100) t2 += tens[n[1]-1] + "-" + ones[n[0]];
   else {
      t2 += ones[n[2]] + " hundred ";
      if ((n[1]*10 + n[0]) < 20) t2 += ones[(n[1]*10 + n[0])];
      else if (n[0] == 0 && n[1] != 0) t2 += tens[n[1]-1];
      else t2 += tens[n[1]-1] + "-" + ones[n[0]];
   }
   return t2;
 } // END toTextHundreds()



 public void resetFont(Graphics g, int i) {
   /* INPUT: graphics module, "type" integer
      OUTPUT: nothing
      FUNCTION: allows for simplified changing
                of text appearance, minimizing
                code redundancy */
   if (i == 0) {	// Arial 12, Bold, Black: Headers
      g.setColor(Color.black);
      g.setFont(new Font("Arial", Font.BOLD, 12));
   }
   else if (i == 1) {	// Arial 12, Plain, grey: #s
      g.setColor(new Color(120,120,120));
      g.setFont(new Font("Arial", Font.PLAIN, 12));
   }
   else if (i == 2) {	// Arial 12, Bold, red: Errors
      g.setColor(new Color(230,0,0));
      g.setFont(new Font("Arial", Font.BOLD, 12));
   }
 }

}
