// ListTestAutomated.java
//
// Tests ShortSequenceLinkedList.java
// Uses, in this folder:
//    ShortSequenceLinkedList.java
//    ShortSequenceLinkedListIterator.java


import java.util.StringTokenizer;


public class ListTestAutomated
{
   final static String lineBreak
                         = System.getProperty(
                                     "line.separator");

   final static String GETNUMBERAT = "short getNumberAt(int index)";
   final static String APPEND = "void append(short data)";
   final static String INSERT = "void insert(int index, short data)";
   final static String INDEXOF = "int indexOf(short data)";
   final static String REMOVEELEMENTAT
                         = "short removeElementAt(int index)";
   final static String EQUALS = "boolean equals(Object other)";
   final static String HASHCODE = "int hashCode()";
   final static String BEGINITERATING
                         = "ShortSequenceLinkedListIterator beginIterating()";

   final static char TEST = '#';
   final static char ENSURE_EXISTS = '|';
   final static char ENSURE_NULL = '-';

   private static String dataFileName;
   private static TextFileInput in;

   private static ShortSequenceLinkedList list1;
   private static String descriptorList1;
   private static ShortSequenceLinkedList list2;
   private static String descriptorList2;

   public static void main(String[] args)
   {
      if ( args.length == 0 )
      {
         System.out.print("One command-line argument needed ");
         System.out.println("to specify data file.");
         System.out.print("A sample data file is ");
         System.out.println("ListTestAutomated-data1.txt");
         System.exit(0);
      }  // if

      System.out.println(introMessage());
      System.out.println("Linked list tester");

      list1 = list2 = null;
      descriptorList1 = "List 1";
      descriptorList2 =  "List 2";

      runTestsRepeatedly(args[0]);
   }  // method main

   private static void runTestsRepeatedly(String filename)
   {
      in = new TextFileInput(filename);
      dataFileName = filename;

      while (true)
      {
         String line = in.readLine();
         if ( line == null )
            return;
         runTests(line);
      }  // while
   }  // method runTestsRepeatedly

   private static void runTests(String preliminaryCommands)
   {
      ShortSequenceLinkedList[] lists = {list1, list2};
      String[] descriptors = { descriptorList1, descriptorList2 };

      parsePreliminaryCommands(preliminaryCommands,
                               lists,
                               descriptors);

      for ( int i = 0; i < 2; i++ )
      {
         // For each list, the preliminary command can be TEST
         // only if the list is non-null:
         assert ( !(lists[i] == null
                       && preliminaryCommands.charAt(i) == TEST) )
                : ("lists[" + i + "] = " + lists[i]
                    + " and the corresponding command is \'"
                    + preliminaryCommands.charAt(i) + "\'");

         // Now determine which method to test, and test it:
         if ( preliminaryCommands.charAt(i) == TEST )
         {
            System.out.println("Now testing "
                                + descriptors[i] + ":");
            testOneList( lists[i], descriptors[i],
                         lists[1-i], descriptors[1-i] );
         }  // if
      }  // for i

      list1 = lists[0];
      list2 = lists[1];

   }  // method runTests

   private static void parsePreliminaryCommands(
                                       String commands,
                                       ShortSequenceLinkedList[] lists,
                                       String[] descriptors)
   {
      if ( commands.length() != 2 )
         printParseError(commands
                         + " <--- expected to be two characters,"
                         + " each \'-\', \'#\', or \'?\'.");

      System.out.println("Executing preliminary commands on line "
                         + in.getLineCount() + ":  " + commands);

      for ( int i = 0; i < 2; i++ )
      {
         switch ( commands.charAt(i) )
         {
            case ENSURE_NULL:
               if ( lists[i] != null )
               {
                  System.out.println("Set to null: "
                                     + descriptors[i]);
                  lists[i] = null;
               }  // if
               else
                  System.out.println(descriptors[i]
                                     + " still null, unchanged.");

               break;
            case ENSURE_EXISTS: case TEST:
               if ( lists[i] == null )
               {
                  System.out.println("Instantiate new "
                                     + descriptors[i]);
                  lists[i] = new ShortSequenceLinkedList();
               }  // if
               else
                  System.out.println(descriptors[i]
                                     + " still exists, unchanged.");
               break;
            default:

               printParseError(commands
                               + " <--- expected to be two characters,"
                               + " each \'" + ENSURE_NULL + "\', \'"
                               + ENSURE_EXISTS + "\', or \'" 
                               + TEST + "\'.");
         }  // switch

         // Each list should now be null if and only if
         // its corresponding preliminary command is ENSURE_NULL:
         assert ( ! ( lists[i] == null
                       ^ commands.charAt(i) == ENSURE_NULL ) )
                : ("lists[" + i + "] = " + lists[i]
                    + " and the corresponding command is \'"
                    + commands.charAt(i) + "\'");
      }  // for i

      String[] texts = { listInfo(lists[0], descriptors[0]),
                         listInfo(lists[1], descriptors[1]) };

      displayResults(texts);
   }  // parsePreliminaryOptions

   private static void testOneList(
                            ShortSequenceLinkedList listToTest,
                            String descriptorListToTest,
                            ShortSequenceLinkedList listOther,
                            String descriptorListOther)
   {
      // listOther may be null, but listToTest may not be null.
      assert ( listToTest != null ) : "listToTest is null.";

      String line = in.readLine();
      String methodToTest = askWhichMethod(line);

      assert ( methodToTest != null )
             : "methodToTest == null";

      String message = null;
      if ( line.length() >= 2 )
         message = line.substring(2);

      String result;

      if ( methodToTest.equals(GETNUMBERAT) )
         result = testGetNumberAt(listToTest, message);
      else if ( methodToTest.equals(APPEND) )
         result = testAppend(listToTest, message);
      else if ( methodToTest.equals(INSERT) )
         result = testInsert(listToTest, message);
      else if ( methodToTest.equals(INDEXOF) )
         result = testIndexOf(listToTest, message);
      else if ( methodToTest.equals(REMOVEELEMENTAT) )
         result = testRemoveElementAt(listToTest,
                                      message);
      else if ( methodToTest.equals(EQUALS) )
         result = testEquals(listToTest, descriptorListToTest,
                             listOther, descriptorListOther,
                             message);
      else if ( methodToTest.equals(HASHCODE) )
         result = testHashCode(listToTest, message);
      else if ( methodToTest.equals(BEGINITERATING) )
         result = testBeginIterating(listToTest, message);
      else
         throw new IllegalStateException("methodToTest="
                                         + methodToTest);

      String[] texts = new String[]{ (descriptorListToTest
                                               + ":  " + result
                                               + lineBreak),
                                      listInfo(listToTest,
                                               descriptorListToTest),
                                      listInfo(listOther,
                                               descriptorListOther) };
      displayResults(texts);
   }  // method testOneList

   private static String askWhichMethod(String line)
   {
      if ( line.length() == 0 )
         printParseError("Line empty.  Should "
                         + "specify a method to test.");

      switch ( line.charAt(0) )
      {
         case 'N':  case 'n':  return GETNUMBERAT;
         case 'A':  case 'a':  return APPEND;
         case 'I':  case 'i':  return INSERT;
         case 'J':  case 'j':  return INDEXOF;
         case 'R':  case 'r':  return REMOVEELEMENTAT;
         case 'E':  case 'e':  return EQUALS;
         case 'H':  case 'h':  return HASHCODE;
         case 'B':  case 'b':  return BEGINITERATING;
         default:
            printParseError(line.charAt(0)
                            + " <--- unrecognized option."
                            + "  Should specify a method to test.");

            // Needed to compile, in place of a final return statement:
            throw new AssertionError(
                     "We should never reach this point because the "
                     + "preceding method call should exit the program.");
      }  // switch
   }  // method askWhichMethod

   private static String testGetNumberAt(ShortSequenceLinkedList list,
                                         String message)
   {
      assert ( list != null ) : "list is null.";

      String line = in.readLine();
      if ( line == null )
         printParseError("index missing for test of " + GETNUMBERAT);
      System.out.println("Testing with index = " + line 
                         + ":  " + GETNUMBERAT);
      if ( message != null )
         System.out.println(message);

      try
      {
         int index =  Integer.parseInt(line);
         short data = list.getNumberAt(index);
         return ("Tested: " + GETNUMBERAT
                            + lineBreak
                            + "The element at index " + index
                            + " is " + data + ".");
      }  // try
      catch ( NumberFormatException nfe )
      {
         printParseError("Not an integer: " + line);
         throw new AssertionError(
                     "We should never reach this point because the "
                     + "preceding method call should exit the program.");
      }  // catch
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + GETNUMBERAT
                             + lineBreak
                             + re);
      }  // catch
   }  // method testGetNumberAt

   private static String testAppend(ShortSequenceLinkedList list,
                                    String message)
   {
      assert ( list != null ) : "list is null.";

      String line = in.readLine();
      if ( line == null )
         printParseError("data missing for test of " + APPEND);
      System.out.println("Testing with data = " + line 
                         + ":  " + APPEND);
      if ( message != null )
         System.out.println(message);

      try
      {
          short data =  Short.parseShort(line);
          list.append(data);
          return ("Tested: " + APPEND
                             + lineBreak
                             + "Appended " + data + ".");
      }  // try
      catch ( NumberFormatException nfe )
      {
         printParseError("Not an integer: " + line);
         throw new AssertionError(
                     "We should never reach this point because the "
                     + "preceding method call should exit the program.");
      }  // catch
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + APPEND
                             + lineBreak
                             + re);
      }  // catch
   }  // method testAppend

   private static String testInsert(ShortSequenceLinkedList list,
                                    String message)
   {
      assert ( list != null ) : "list is null.";

      String line1 = in.readLine();
      if ( line1 == null )
         printParseError("index missing for test of " + INSERT);
      String line2 = in.readLine();
      if ( line2 == null )
         printParseError("index missing for test of " + INSERT);
      System.out.println("Testing with index = " + line1
                         + " and data = " + line2
                         + ":  " + INSERT);
      if ( message != null )
         System.out.println(message);

      try
      {
          int index =  Integer.parseInt(line1);
          short data =  Short.parseShort(line2);
          list.insert(index, data);
          return ("Tested: " + INSERT
                             + lineBreak
                             + "Inserted " + data + " at index "
                             + index + ".");
      }  // try
      catch ( NumberFormatException nfe )
      {
         printParseError("Not an integer: " + nfe.getMessage());
         throw new AssertionError(
                     "We should never reach this point because the "
                     + "preceding method call should exit the program.");
      }  // catch
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + INSERT
                             + lineBreak
                             + re);
      }  // catch
   }  // method testInsert

   private static String testIndexOf(ShortSequenceLinkedList list,
                                     String message)
   {
      assert ( list != null ) : "list is null.";

      String line = in.readLine();
      if ( line == null )
         printParseError("data missing for test of " + INDEXOF);
      System.out.println("Testing with data = " + line 
                         + ":  " + INDEXOF);
      if ( message != null )
         System.out.println(message);

      try
      {
          short data =  Short.parseShort(line);

          int index = list.indexOf(data);
          return ("Tested: " + INDEXOF
                             + lineBreak
                             + "Index of " + data + " is "
                             + index + ".");
      }  // try
      catch ( NumberFormatException nfe )
      {
         printParseError("Not an integer: " + line);
         throw new AssertionError(
                     "We should never reach this point because the "
                     + "preceding method call should exit the program.");
      }  // catch
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + INDEXOF
                             + lineBreak
                             + re);
      }  // catch
   }  // method testIndexOf

   private static String testRemoveElementAt(
                                  ShortSequenceLinkedList list,
                                  String message)
   {
      assert ( list != null ) : "list is null.";

      String line = in.readLine();
      if ( line == null )
         printParseError("index missing for test of " + REMOVEELEMENTAT);
      System.out.println("Testing with index = " + line 
                         + ":  " + REMOVEELEMENTAT);
      if ( message != null )
         System.out.println(message);

      try
      {
          int index =  Integer.parseInt(line);
          short data = list.removeElementAt(index);
          return ("Tested: " + REMOVEELEMENTAT
                             + lineBreak
                             + "Removed " + data + " at index "
                             + index + ".");
      }  // try
      catch ( NumberFormatException nfe )
      {
         printParseError("Not an integer: " + line);
         throw new AssertionError(
                     "We should never reach this point because the "
                     + "preceding method call should exit the program.");
      }  // catch
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + REMOVEELEMENTAT
                             + lineBreak
                             + re);
      }  // catch
   }  // method testInsert

   private static String testEquals(ShortSequenceLinkedList listToTest,
                             String descriptorListToTest,
                             ShortSequenceLinkedList listOther,
                             String descriptorListOther,
                             String message)
   {
      assert ( listToTest != null ) : "listToTest is null.";

      if ( message != null )
         System.out.println(message);

      try
      {
          boolean equal = listToTest.equals(listOther);
          String text = "Tested: " + EQUALS
                             + lineBreak
                             + "Testing        "
                             + descriptorListToTest
                             + " (hash code "
                             + listToTest.hashCode() + ")"
                             + lineBreak
                             + "with parameter "
                             + descriptorListOther + " ";
          text += ( ( listOther == null )
                  ? ( "(null)." )
                  : ( "(hash code " + listOther.hashCode() + ")." ) );

          text += lineBreak + "The two lists "
                            + (equal ? "ARE" : "are NOT")
                            + " equal.";

          return text;
      }  // try
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + EQUALS
                             + lineBreak
                             + re);
      }  // catch
   }  // method testEquals

   private static String testHashCode(ShortSequenceLinkedList list,
                                      String message)
   {
      assert ( list != null ) : "list is null.";

      if ( message != null )
         System.out.println(message);

      try
      {
          int hashCode = list.hashCode();
          return ("Tested: " + HASHCODE
                             + lineBreak
                             + "Hash code is " + hashCode + ".");
      }  // try
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + HASHCODE
                             + lineBreak
                             + re);
      }  // catch
   }  // method testHashCode

   private static String testBeginIterating(ShortSequenceLinkedList list,
                                            String message)
   {
      assert ( list != null ) : "list is null.";

      if ( message != null )
         System.out.println(message);

      try
      {
          ShortSequenceLinkedListIterator iterator
                                           = list.beginIterating();
          String text = "Tested: " + BEGINITERATING
                                   + lineBreak
                                   + "Below are the list's"
                                   + " contents, if any:"
                                   + lineBreak;

          while ( iterator.hasNext() )
              text = text + Short.toString(iterator.next())
                          + lineBreak;

          text = text + "End of iteration.";

          return text;
      }  // try
      catch ( RuntimeException re )
      {
         re.printStackTrace();
         return("Exception while testing:  "
                             + BEGINITERATING
                             + lineBreak
                             + re);
      }  // catch
   }  // method testBeginIterating

   private static String introMessage()
   {
      String intro = lineBreak
                     + "  This program allows you to test two linked "
                     + lineBreak
                     + "  lists repeatedly.  Each time, you will have"
                     + lineBreak
                     + "  the option of creating or destroying either"
                     + lineBreak
                     + "  or both lists if desired, and then calling"
                     + lineBreak
                     + "  a method on each list (if non-null).  (When"
                     + lineBreak
                     + "  you test the equals method, the parameter "
                     + lineBreak
                     + "  will be a reference to the other list.)"
                     + lineBreak;

      return intro;
   }  // method introMessage

   private static String listInfo(ShortSequenceLinkedList list,
                                  String listReferenceID)
   {
      String text = ( list == null )
                       ? (listReferenceID + " does not exist."
                               + " (The reference to "
                               + listReferenceID + " is null.)")
                       : (listReferenceID + ":   [" + list
                               + "]    length=" + list.getLength());
      return text;
   }  // method writeListsInfo

   private static void displayResults(String[] text)
   {
      System.out.println();
      for ( int i = 0; i < text.length; i++ )
         System.out.println(text[i]);
      ConsoleInput.pause();
      // System.out.println();
   }  // method displayResults

   private static void printParseError(String gripe)
   {
      System.out.println("Error in data file " + dataFileName
                         + " on line " + in.getLineCount() + ":");
      System.out.println(gripe);
      System.exit(0);
   }  // method printParseError
}  // class ListTestAutomated