import junit.framework.TestCase;
import java.util.StringTokenizer;
/** 
 * Test class for Book Reference
 */
public class BookReferenceTester extends TestCase {

  /** test data - lists that can be used to test various methods */

  /** smaller booklist to be used to create test data */
  private String smallBList1 = 
    "Introduction to Psychology#Calculus";

  /** smaller booklist to be used to create test data */
  private String smallBList2 = 
    "Introduction to Unix Programming#Calculus";

  /** smaller booklist to be used to create test data */
  private String smallBList3 = 
    "An Introduction to Algorithms#Computability and Complexity";

  /** smaller booklist to be used to create test data */
  private String smallBList4 = 
    "C++ Programming#Computability and Complexity";

  /** smaller price list coordinated with small booklists used to create data */
  private String smallPList = 
    "Introduction to Psychology:54.00:108.00#Calculus:55.55:111.10#" + 
    "Introduction to Unix Programming:84.00:168.75#" +
    "C++ Programming:34.50:70.75#Computability and Complexity:40.00:80.00#" +
    "An Introduction to Algorithms:87.00:162.00";

  /** smaller book and price list used to create test data */
  private String smallPBList1 = 
    "Introduction to Psychology:54.00:108.00#Calculus:55.55:111.10";

  /** smaller book and price list used to create test data */
  private String smallPBList2 = 
    "Introduction to Unix Programming:84.00:168.75#" + 
    "Calculus:55.55:111.10";

  /** smaller book and price list used to create test data */
  private String smallPBList3 = 
    "An Introduction to Algorithms:87.00:162.00#" +
    "Computability and Complexity:40.00:80.00";

  /** smaller book and price list used to create test data */
  private String smallPBList4 = 
    "C++ Programming:34.50:70.75#Computability and Complexity:40.00:80.00";
  private String teenyPriceList = 
    "C++ Programming:34.50:70.75#Computability and Complexity:40.00:80.00#" +
    "An Introduction to Algorithms:87.00:162.00";
  
  
  /** Test the constructor that takes four booklists plus
   *  a price list.  To verify the booklists were correctly 
   *  added, you must also test getBookList.  To verify that the 
   *  price list was correctly added, you must test getWholesalePrice
   *  and getRetailPrice for the books on your book lists.
   */
  public void testFiveListConstructorGetBookListAndGetPriceList() {
    BookReference bookref = 
      new BookReference(smallBList1, smallBList2, smallBList3, smallBList4, teenyPriceList);
    assertTrue("lists should be equal (1)", equalLists(bookref.getBookList(1), smallBList1 ));
    assertTrue("lists should be equal (2)", equalLists(bookref.getBookList(2), smallBList2 ));
    assertTrue("lists should be equal (3)", equalLists(bookref.getBookList(3), smallBList3 ));
    assertTrue("lists should be equal (4)", equalLists(bookref.getBookList(4), smallBList4 ));
    assertEquals("price list should be equal", bookref.getPriceList(), teenyPriceList);
     
  }

  // check constructor two.
  public void testFourListConstructorGetBookListAndGetPriceList() {
    BookReference bookref = 
      new BookReference(smallPBList1, smallPBList2, smallPBList3, smallPBList4);
    assertTrue("lists should be equal (1)", equalLists(bookref.getBookList(1), smallBList1 ));
    assertTrue("lists should be equal (2)", equalLists(bookref.getBookList(2), smallBList2 ));
    assertTrue("lists should be equal (3)", equalLists(bookref.getBookList(3), smallBList3 ));
    assertTrue("lists should be equal (4)", equalLists(bookref.getBookList(4), smallBList4 ));
    assertTrue("lists should be equal (5)", equalLists(bookref.getPriceList(), smallPList ));
   }
  
  public void testGetWholesalePrice() {
    // check individual prices use 3 book price list, check price at beginning middle and end
    BookReference bookref = 
      new BookReference(smallBList1, smallBList2, smallBList3, smallBList4, teenyPriceList);
    checkWholesale(bookref,"C++ Programming",34.50);
    checkWholesale(bookref,"Computability and Complexity",40.00);
    checkWholesale(bookref, "An Introduction to Algorithms",87.00);
  }
  
  public void testGetRetailPrice() {
    // check individual prices use 3 book price list, check price at beginning middle and end
    BookReference bookref = 
      new BookReference(smallBList1, smallBList2, smallBList3, smallBList4, teenyPriceList);
    checkRetail(bookref, "C++ Programming",70.75);
    checkRetail(bookref, "Computability and Complexity",80.00);
    checkRetail(bookref, "An Introduction to Algorithms",162.00);
  }
  
  public void testBookRequired() {
    BookReference bookref = 
      new BookReference(smallBList1, smallBList2, smallBList3, smallBList4, smallPList);
    assertTrue("bookRequired should be true", bookref.bookRequired("Calculus"));
    assertFalse("bookRequired should be false", bookref.bookRequired("Calculus for High School"));
  }

  public void testBookFound(){
    assertTrue("Book in list should be true", BookReference.bookFound("Calculus",smallBList1));
    assertFalse("Book not in list, should be false", BookReference.bookFound("Calculus for High School",smallBList1));
  }

  public void testRemoveBooks() {
    assertEquals("removeBooks for disjoint lists incorrect","b4#b5#b6",BookReference.removeBooks("b1#b2#b3", "b4#b5#b6"));
    assertEquals("removeBooks for intersecting lists incorrect","b4#b6",BookReference.removeBooks("b1#b2#b3", "b4#b2#b6"));
    assertEquals("removeBooks for equivalent lists incorrect","",BookReference.removeBooks("b1#b2#b3", "b3#b2#b1"));
   }
  
  public void testListStructure() {
    BookReference bookref = 
      new BookReference(smallPBList1, smallPBList2, smallPBList3, smallPBList4);
    // look at beginning and end character of first and second book list and the price list.
    assertTrue(bookref.getBookList(1).charAt(0) != '#' 
                 && bookref.getBookList(1).charAt(bookref.getBookList(1).length() - 1) != '#');
    assertTrue(bookref.getBookList(2).charAt(0) != '#' 
                 && bookref.getBookList(2).charAt(bookref.getBookList(2).length() - 1) != '#');
    assertTrue(bookref.getPriceList().charAt(0) != '#' 
                 && bookref.getPriceList().charAt(bookref.getPriceList().length() - 1) != '#');  
  }
    
  
  /** 
   * helper to check retail price. 
   */
  private void checkRetail( BookReference bref, String book, double price) {
    assertEquals("Retail price should be " + price, price, bref.getRetailPrice(book), 0.009);
  }
  
  /** 
   * helper to check wholesale price. 
   */
  private void checkWholesale( BookReference bref, String book, double price) {
    assertEquals("Wholesale price should be "+ price, price, bref.getWholesalePrice(book), 0.009);
  }
  
  /** Helper to compare lists disregarding book order.
   *  Return true if every element in l1 is in l2, and
   *  every element in l2 is in l1.
   */
  private boolean equalLists(String l1, String l2) {
    return containsAllItems(l1, l2) && containsAllItems(l2, l1);
  }
  
  /** 
   * Basic code to check all items in l1 are contained in l2.
   */
  private boolean containsAllItems(String l1, String l2) {
    String b1;
    String b2;
    boolean result = true;
    StringTokenizer st1 = new StringTokenizer(l1,"#");
    while (st1.hasMoreTokens() && result){
      result = false;
      b1 = st1.nextToken();
      StringTokenizer st2 = new StringTokenizer(l2,"#"); 
      while (st2.hasMoreTokens() && !result){
        b2 = st2.nextToken();
        result = b1.equals(b2);
      }
    }
    return result;
  }
  
}

