import java.util.*;  // to use Vector
import java.io.*;    // to use IO classes

/** A program that implements the SortOutput interface by providing
  the methods for reading Employee records from an input text
  file and saving into a Vector, for sorting the Vector, and for
  outputing the objects of the Vector to a text file; the program
  demonstrates the use of Vector objects and the methods, text file IO, 
  and interface 
  (Oct. 28, 2001)
*/
public class SortedEmployees implements SortOutput {
  private Vector vector = new Vector();
  private int numOfEmployees = 0;

  /** a constructor which reads the input text file as specified
    in the parameter, tokenizes the Employee records then creates
    a Boss or HourlyWorker object, then saves these object in
    the private Vector vector */
  public SortedEmployees(String inFileName) {

    String line;  // the next input line
    StringTokenizer tokens;  // the tokenized line
    String token;  // the next token of line being processed
    String first, last;  // an Employee's first and last names
    double wage;  // an Employee's wage
    double hours;  // an Employee's hours (if HourlyWorker)

    try {
        /* open a character-based input stream to read file using the
           local encoding; it genrates FileNotFoundException if the
           input file doesn't exist */
        BufferedReader in = new BufferedReader (
                 new FileReader(inFileName));

        // read one line per iteration until end of file
        while ((line = in.readLine()) != null) {
          // tokenize line and extract tokens
          tokens = new StringTokenizer(line);  
          token = tokens.nextToken();  

          /* add either a Boss or an HourlyWorker object to 
           the Vector vector */
          if (token.equals("Boss")) {  
            // found a Boss Employee record
            first = tokens.nextToken();  
            last = tokens.nextToken();  
            wage = Double.parseDouble(tokens.nextToken());  
            // add a Boss object to the Vector vector
            vector.add(new Boss(first, last, wage));  
          }
          else {  
            // assume an HourlyWorker Employee record
            first = tokens.nextToken();  
            last = tokens.nextToken();  
            wage = Double.parseDouble(tokens.nextToken());  
            hours = Double.parseDouble(tokens.nextToken());
            // add an HourlyWorker object to the Vector vector
            vector.add(
              new HourlyWorker(first, last, wage, hours));
          }
        }
        numOfEmployees = vector.size();
    } catch (FileNotFoundException e) {  // missing input file
        System.out.println("FileNotFoundException occurs");
        System.exit (-1);
    } catch (IOException e) {  // catch other types of IOException
        System.out.println("IOException occurs");
        System.exit (-1);
    } catch (NumberFormatException e) {  // for bad input records
        System.out.println("NumberFromatException occurs");
        System.exit (-1);
    } catch (NoSuchElementException e) {  // for bad input records
        System.out.println("NoSuchElementException occurs");
        System.exit (-1);
    }
  }  // end of constructor

  /** implement the (slow) Bubble sort algorithm moving
    Boss objects ahead HourlyWorker objects, and higher
    earnings ones ahead of the lower ones in each type */
  public void sort() {
    for (int i = numOfEmployees; --i >= 0;) 
      for (int j = 0; j < i; j++) 
        // swap objects if out of order
        if (compare(vector.get(j), vector.get(j+1)) < 0) {
          Object temp = vector.get(j);
          vector.setElementAt(vector.get(j+1), j);
          vector.setElementAt(temp, j+1);
        }
  }

  /** implements the sort() method of the interface which
    creates a text file as specified in parameter outFileNanme
    and outputs the Employees in vector in the format of the
    Employee's type (Boss or HourlyWorker) followed by first
    and last names, and earnings */
  public void output(String outFileName) {
    try {
        /* create a character-based output file; it generates
           IOException if failed (e.g., no write privilege) */
        PrintWriter out = new PrintWriter(
                          new BufferedWriter(
                          new FileWriter(outFileName)));

        /* output all Employee objects to output file, one Employee
         per line in the specified format */
        for (int j = 0; j < numOfEmployees; j++) {
          // if next object is a Boss
          if (vector.get(j) instanceof Boss) {
            Boss boss = (Boss)(vector.get(j));
            out.println(boss.toString() + ' ' + boss.earnings());
          }
          else { // assume an HourlyWorker object
            HourlyWorker worker = (HourlyWorker)(vector.get(j));
            out.println(worker.toString() + ' ' + worker.earnings());
          }
        }
        out.close();  // close output file
    } catch (IOException e) {  // catch IOException
        System.out.println("IOException occurs");
        System.exit (-1);
    }
  }  // end of output()

  /** a private static method which compares two Employee obects
    (Boss or HourlyWorker), returns 1, 0, or -1, depending on if
    obj1 is "higher" than obj2, where higher is defined so that
    Boss is higher than HourlyWorker, and the eranings decide
    the order within same type of employees */
  private static int compare(Object obj1, Object obj2) {
    // if obje1 is a Boss object
    if (obj1 instanceof Boss) {
      // if obj2 is also a Boss object
      if (obj2 instanceof Boss) {
        if (((Boss)obj1).earnings() > ((Boss)obj2).earnings())
          return 1;
        else if (((Boss)obj1).earnings() < ((Boss)obj2).earnings())
          return -1;
        else 
          return 0;
      }
      // obj2 is an HourlyWorker
      else 
        return 1;
    }
    // obj1 is an HourlyWorker object
    else { 
      // if obj2 is also an HourlyWorker object
      if (obj2 instanceof HourlyWorker) {
        if (((HourlyWorker)obj1).earnings() > ((HourlyWorker)obj2).earnings())
          return 1;
        else if (((HourlyWorker)obj1).earnings() < ((HourlyWorker)obj2).earnings())
          return -1;
        else 
          return 0;
      }
      // obj2 is a Boss object
      else 
        return -1;
    }
  }  // end of compare()
}

