import java.io.PrintStream;
import java.util.StringTokenizer;

/** 
 * A Bookstore that sells texts to 
 * university students.
 */
public class BookStore {
  
  /** The cost multiplier, which is multiplied by wholesale price of 
   *  a book to determine what the cost of that book is. */
  private double costMultiplier;
  
  /** The sale multiplier, which is multiplied by the retail price of
   *  a book to determine the sale price of that book */
  private double saleMultiplier;
  
  /** The total sales amount. */
  private double sales;
  
  /** The total costs for the store inventory. */
  private double costs;
  
  /** 
   * Books on hand are kept in a contiguous string.
   * The name of the book and the quantity are stored 
   * for each title in the store.  The name and quantity 
   * are separated by a colon (":"), and each book is
   * separated by a pound-sign ("#").  
   * For example:
   * "Programming Using Java:30#Systems Analysis with UML:45# ..." 
   */ 
  private String booksOnHand;
  
  /**
   * Book reference used to access book lists and price lists
   * for local university.
   */
  private BookReference bookRef;
  
  /** Constant used to separate books, and units of
   *  book information in the book and price lists.
   */
  private static final String BOOK_DELIMITER = "#";
  
  /** Constant used to separate individual fields 
   * within  a unit of book information.
   */
  private static final String QTY_DELIMITER = ":";

  /** 
   * Create a new BookStore object with the 
   * booklists.  There is no initial inventory.
   * @param b The BookReference for the book and price lists for 
   * the curriculum at the local university.
   * @param cm The cost multiplier. 
   * @param sm The sale multiplier. 
   */
  public BookStore(BookReference b, double cm, double sm) {
    this.bookRef = b;
    this.costMultiplier = cm;
    this.saleMultiplier = sm;
    this.sales = 0.0;
    this.costs = 0.0;
    this.booksOnHand = "";
  }

  /** 
   * Create a new BookStore object with the 
   * booklists and initial inventory.
   * @param b the book and price lists
   * for the curriculum at the local university.
   * @param cm The cost multiplier. 
   * @param sm The sale multiplier. 
   * @param i The initial inventory list of books 
   * separated by the BOOK_DELIMITER.
   */
  public BookStore(BookReference b, double cm, double sm, String i) {
    this.bookRef = b;
    this.costMultiplier = cm;
    this.saleMultiplier = sm;
    this.sales = 0.0;
    this.booksOnHand = "";
    
    // now add inventory one book at a time 
    StringTokenizer books = new StringTokenizer(i, BOOK_DELIMITER);
    while (books.hasMoreTokens()) {
      this.buyBook(books.nextToken());
    }
  }
  
  /** 
   *  Purchase a book for the bookstore.
   *  Only add the book if it is on a
   *  booklist for this year.
   *  Return 0.0 if the book is not 
   *  purchased.
   *  Add the book to in
   *  Update the costs to reflect the 
   *  cost of the new inventory.
   *  @param b The book to be added.
   *  @return The purchase price of the book
   */
  public double buyBook(String b) {
    if (this.bookRef.bookRequired(b)) {
      this.addInventory(b);
      return this.addCosts(b);
    }
    return 0.0;
  }
  /** 
   * Add a book into the inventory of 
   * books on hand.
   * @param b The book to be added to the inventory.
   */
  public void addInventory(String b) {
    String newInventory = "";
    if (this.booksOnHand.equals("")) { //first book in the store
      newInventory = b + ":1";
    } else { // insert the book into the current inventory list
      StringTokenizer books = new StringTokenizer(this.booksOnHand, 
						  BOOK_DELIMITER);
      boolean found = false;
      while (books.hasMoreTokens()) {
        if (!newInventory.equals("")) {
          newInventory +=  BOOK_DELIMITER;
        }
        String bookUnit = (String) books.nextToken();
        if (bookUnit.substring(0, bookUnit.indexOf(QTY_DELIMITER)).equals(b)) { 
          // found book, now add to inventory
          int qty = 
            Integer.parseInt
            (bookUnit.substring(bookUnit.indexOf(QTY_DELIMITER) + 1)) + 1;
          newInventory += 
            bookUnit.substring(0, bookUnit.indexOf(QTY_DELIMITER) + 1) + qty;
          found = true;
        } else { // otherwise just leave book quantity as is
	         // and add it back to the string.
          newInventory += bookUnit;
        }
      }
      if (!found) { // add it to the end of the list
        newInventory += BOOK_DELIMITER + b + ":1";
      }
    } // end of insert into current inventory list
    this.booksOnHand = newInventory;  // update the instance variable
  }
  
 /** 
   * Remove book b from the books on hand.
   * @param b The book to be removed.
   */
  public void reduceInventory(String b) {
    String newInventory = "";
    StringTokenizer books = 
      new StringTokenizer(this.booksOnHand, BOOK_DELIMITER);
    
    while (books.hasMoreTokens()) {
      String bookUnit = (String) books.nextToken();
      if (bookUnit.substring(0, bookUnit.indexOf(QTY_DELIMITER)).equals(b)) { 
        // found book, now add to inventory
        int qty = 
          Integer.parseInt(bookUnit.substring(bookUnit.indexOf(QTY_DELIMITER)
                                                + 1)) - 1;
        // only put the book back on the list if the quantity is greater than 0
        if (qty > 0) { 
          if (!newInventory.equals("")) {
            newInventory +=  BOOK_DELIMITER;
          }
          newInventory +=  
            bookUnit.substring(0, bookUnit.indexOf(QTY_DELIMITER) + 1 ) + qty;
            
        } 
        // otherwise just leave book quantity as is 
        // and add it back to the string.
      } else { 
        if (!newInventory.equals("")) {
          newInventory +=  BOOK_DELIMITER;
        }
        newInventory += bookUnit;
      }
    }
       
    this.booksOnHand = newInventory;  // update the instance variable
  }


  /** 
   * Get the wholesale value of the book b,
   * calculate the cost for the bookstore, and
   * add the cost to the overall store costs.
   * @param b The book for which the cost is added.
   * @return The purchase price of the book.
   */
  public double addCosts(String b) {
    double cost = this.bookRef.getWholesalePrice(b) * costMultiplier;
    this.costs += cost;
    return cost;
  }
    
  /** 
   * Sell a book from the books on hand
   * update the sales to reflect the 
   * sale price of the book and decrement the 
   * quantity on hand.  If the book is not
   * available, the sale price is 0.0.
   * @param b The book to be sold.
   * @return The sale price of the book.
   */ 
  public double sellBook(String b) {
    if ( bookAvailable(b) ) {
      this.reduceInventory(b);
      return this.addSales(b);
    }
    return 0.0;
  }
  
  /**
   * Determine if the book is available in 
   * the store inventory.  If so, return true,
   * otherwise, return false.
   * @param b The book to be found in inventory.
   * @return Whether the book is in inventory.

   */
  public boolean bookAvailable(String b) {
    StringTokenizer books = 
      new StringTokenizer(this.booksOnHand, BOOK_DELIMITER);
    
    while (books.hasMoreTokens()) {
      String bookUnit = (String) books.nextToken();
      if (bookUnit.substring(0, bookUnit.indexOf(QTY_DELIMITER)).equals(b)) {
        return true; // found book, return true
      }
    }
 
    // if we get here, the book was not found
    return false;
  }
  
   /** 
   * Get the wholesale value of the book b,
   * calculate the sale for the bookstore, and
   * add the amount to the overall store sales.
   * The sale amount is equal to the retail price 
   * times the price multiplier.
   * @param b The book for which the sale is added.
   * @return The purchase price of the book.
   */
  public double addSales(String b) {
    double saleAmount = this.bookRef.getRetailPrice(b) * saleMultiplier;
    this.sales += saleAmount;
    return saleAmount;
 
 }
  
  /** 
   * Perform inventory of books in store.
   * Return a list of books showing the quantity, book
   * title, wholesale price, retail price,  and the
   * extended wholesale and retail price (created by
   * multiplying the quantity times the price).
   * Total the extended price to show the amount
   * of money invested in inventory (total extended 
   * wholesale) and the expected revenue)
   * @return The inventory list including cost and sale price.
   */
  public String inventoryBooks() {
    String inventory = "";
    double saleAmt = 0.0;
    double costAmt = 0.0;
    String bookUnit;
    int totQty = 0;
    int qty;
    double cost;
    String book;
    double salePrice;
    StringTokenizer books = new StringTokenizer(booksOnHand, BOOK_DELIMITER);
    while (books.hasMoreTokens()) {
      bookUnit = books.nextToken();
      book = bookUnit.substring(0, bookUnit.indexOf(QTY_DELIMITER));
      qty =  Integer.parseInt(bookUnit.substring(
			      bookUnit.indexOf(QTY_DELIMITER) + 1));
      cost = this.bookRef.getWholesalePrice(book) * costMultiplier;
      salePrice = this.bookRef.getRetailPrice(book) * saleMultiplier;
      inventory += book + ", " + qty +  ", " +
                   cost + ", " + (cost * qty)  + ", " + 
                   salePrice + ", " + (salePrice * qty)  + "\n";
      saleAmt += (salePrice * qty);
      costAmt += (cost * qty);
      totQty += qty;
    }
    inventory += "Total: number of books: " + totQty + ", cost: " + costAmt
              + ", sale price: " + saleAmt + "\n";
    return inventory;
  }
  
   /** Set the cost multiplier to the new percentage.
    *  @param pct The new cost multiplier.
    */
   public void setCostMultiplier(double pct) {
     this.costMultiplier = pct;
   }

   /** Get the total costs for this bookstore
    *  @return The total costs for the bookstore.
    */
   public double getCosts() {
     return this.costs;
   }

   /** Get the total sales for this bookstore
    *  @return The total sales for the bookstore.
    */
   public double getSales() {
     return this.sales;
   }
  
   /**
    * Get the inventory for this bookstore.
    * @return The bookstore inventory.
    */
   public String getInventory() {
     return booksOnHand;
   }
}

