///////////////////////////////////////////////////////////////////////////////
//                   ALL STUDENTS COMPLETE THESE SECTIONS
// Title:            Interactive Customer Database Tester
// Files:            InteractiveDBTester.java
//									 CustomerDatabase.java
// Semester:         CS367-002 Summer 2014
//
// Author:           James Maciejewski
// Email:            james.m.maciejewski@gmail.com
// CS Login:         maciejewski2
// Lecturer's Name:  Beck Hasti
//
//////////////////////////// 80 columns wide //////////////////////////////////

import java.util.*;
import java.io.*;
import java.lang.Math;

/**
 * InteractiveDBTester implements an interactive interface for testing
 * the CustomerDatabase class.
 *
 * @author James Maciejewski
 */public class InteractiveDBTeseter {

	 /**
	  * The main method.  Loads a specified input file into a CustomerDatabase
	  * object and provides an interactive command-line interface for 
	  * modifying and reporting on the customer database.
	  * 
	  * @param args The input file to load into a CustomerDatabase object
	  */
	 public static void main(String[] args) {

		 // Check that exactly 1 argument as passed from the commend line
		 if (args.length != 1){
			 System.out.println("Please provde input file as command-line argument");
			 return;
		 }

		 //The customer database
		 CustomerDatabase customerDB = new CustomerDatabase(); 
		 // input file to load into customerDB
		 String inputFileName = args[0];  
		 // file object for passed in file
		 File inputFile = new File( inputFileName ); 

		 // Check that file exists before attempting to open
		 if (! inputFile.exists() ) {
			 System.out.println("Error: Cannot access input file.");
			 return;
		 }

		 try {
			 Scanner inFile = new Scanner( inputFile );
			 String parsingDelims = "[,]+";

			 while ( inFile.hasNext() ) {
				 String line = inFile.nextLine();
				 String[] lineAry = line.split(parsingDelims);

				 customerDB.addCustomer(lineAry[0]);
				 for (int i = 1; i < lineAry.length; i++) {
					 customerDB.addProduct(lineAry[0], lineAry[i]);
				 }
			 }	

			 inFile.close();

		 } catch ( FileNotFoundException exc ) {
			 System.out.println( "Error: Cannot access input file.");
			 return;
		 }

		 Scanner stdin = new Scanner(System.in);  // for reading console input
		 printOptions();
		 boolean done = false;
		 while (!done) {
			 System.out.print("Enter option ( dfhisqr ): ");
			 String input = stdin.nextLine();
			 input = input.toLowerCase();  // convert input to lower case

			 // only do something if the user enters at least one character
			 if (input.length() > 0) {
				 char choice = input.charAt(0);  // strip off option character
				 String remainder = "";  // used to hold the remainder of input
				 if (input.length() > 1) {
					 // trim off any leading or trailing spaces
					 remainder = input.substring(1).trim(); 
				 }

				 switch (choice) {

				 case 'd': // d - discontinue product from database
					 discontinueProduct(customerDB, remainder);
					 break;

				 case 'f': // f - find customer in database
					 findCustomer(customerDB, remainder);
					 break;

				 case 'h': // h - get help 
					 printOptions();
					 break;

				 case 'i': // i - display some info about counts in the database
					 displayDBInfo(customerDB);
					 break;

				 case 's': // s - search for a product in the database
					 searchForProduct(customerDB, remainder);
					 break;

				 case 'q': // q - quit the program
					 done = true;
					 System.out.println("quit");
					 break;

				 case 'r': // r - remove a customer from the database
					 removeCustomer(customerDB, remainder);
					 break;

				 default:  // ignore any unknown commands
					 break;
				 }
			 }
		 }

		 stdin.close();
	 }

	 /**
	  * Remove specified customer from the customer database.  If customer
	  * is not found, inform the user.
	  *
	  * @param customerDB The CustomerDatabase to remove the customer from
	  * @param customer The customer to remove from the database
	  */
	 private static void removeCustomer(CustomerDatabase customerDB, 
			 String customer) {

		 try {
			 // try to remove the customer
			 if ( customerDB.removeCustomer( customer ) ) {
				 System.out.println("customer removed");
			 } else { 
				 // if the customer couldn't be removed, they weren't in the DB
				 System.out.println("customer not found");
			 }
		 } catch ( IllegalArgumentException exc ) {
			 // Null is not a customer
			 System.out.println("customer not found");
		 }

	 }

	 /**
	  * Find all customers in the database have the specified product in their
	  * wishlist and display their names.  If no customer has the product, 
	  * inform the user.
	  *
	  * @param customerDB The CustomerDatabase to search for the product
	  * @param product The product to search for in the database
	  */
	 private static void searchForProduct( CustomerDatabase customerDB, 
			 String product) {

		 try {

			 // list of all customers with the specified product
			 List<String> customerList = customerDB.getCustomers( product );
			 // Comma delimited list of customers in customerList
			 String customerListString = stringListToString( customerList );

			 // if customer list is null, no customer has the product
			 if ( customerList == null) {
				 System.out.println("product not found");

			 } else {
				 // otherwise print out the customers with the product
				 System.out.print(product + ":");
				 System.out.println( customerListString );

			 }
		 } catch (IllegalArgumentException exc ) {

			 // null is not a product
			 System.out.println("product not found");

		 }

	 }

	 /**
	  * Discontinue the specified product, that is, remove the product
	  * from all customers' wishlists.  If no customer has the product, 
	  * inform the user.
	  *
	  * @param customerDB The CustomerDatabase to discontinue the product from
	  * @param product The product to discontinue
	  */
	 private static void discontinueProduct(CustomerDatabase customerDB, 
			 String product) {

		 try {

			 // try to remove the product from the database
			 if ( customerDB.removeProduct( product ) ) {
				 System.out.println( "product discontinued" );

			 } else { // if couldn't remove product, it wasn't there
				 System.out.println( "product not found" );

			 }
		 } catch (IllegalArgumentException exc) {

			 // null is not a product in the database
			 System.out.println( "product not found" );

		 }

	 }

	 /**
	  * Find the specified customer in the database and print their wishlist.
	  * If the customer is not in the database inform the user 
	  *
	  * @param customerDB The CustomerDatabase to search for name
	  * @param name The name of the customer to find
	  */
	 private static void findCustomer(CustomerDatabase customerDB, String name) {

		 // message to return to user
		 String message = "customer not found";

		 try {

			 // check if name is in the database
			 if ( customerDB.containsCustomer(name) ) {

				 // iterate over customerDB until we find name
				 Iterator<Customer> customerIter = customerDB.iterator();
				 while ( customerIter.hasNext() ) {

					 Customer customer = customerIter.next();

					 // when we find name build message for user
					 if ( customer.getUsername().equals(name) ) {
						 message = name + ":";

						 // get the wishlist
						 List<String> customerWishlist = customer.getWishlist();
						 // put the wishlist in a comma-delimited string
						 String customerWishlistString 
						 = stringListToString( customerWishlist );
						 // add the wishlist to the user message
						 message = message + customerWishlistString;

						 // we found name, so no need to keep iterating
						 break;
					 }
				 }
			 } else {
				 // this shouldn't happen, but if we don't find name tell the user				 
				 message = "customer not found";
			 }
		 } catch (IllegalArgumentException exc) {} // null isn't a customer in the DB

		 System.out.println( message );
	 }

	 /**
	  * Display information about the current state of the database
	  *
	  * @param customerDB The CustomerDatabase to displayinfo for
	  */
	 private static void displayDBInfo ( CustomerDatabase customerDB ) {

		 int numCustomers = 0;  // number of customer in the databse
		 int numUniqueProducts = 0; // number of unique products in the database
		 int numProducts =0; // total number of products in users' wishlists

		 int mostNumProdsPerCust = 0; // max products per customer
		 int leastNumProdsPerCust = 0; // min number of products per customer
		 int avgNumProdsPerCust = 0; // average number of products per customer

		 int mostNumCustsPerProd = 0; // max number of customers  per product
		 int leastNumCustsPerProd = 0; // min number of customers per product
		 int avgNumCustsPerProd = 0; // average number of customer per product

		 // list of unique products in the database
		 List<String> productList = new ArrayList<String>();
		 // customer counts for each unique product in the database
		 List<Integer> productCountList = new ArrayList<Integer>();

		 // calculations are different for the first 
		 //product and customer when iterating
		 boolean isFirstCustomer = true;
		 boolean isFirstProduct = true;

		 // iterate over all customer to get customer count,
		 // total product count, and product per customer counts
		 Iterator<Customer> customerIter = customerDB.iterator();
		 while ( customerIter.hasNext() ) {

			 Customer customer = customerIter.next();
			 numCustomers++;

			 // Wishlist for each customer inthe database
			 List<String> customerWishlist = customer.getWishlist();

			 // get products per customer counts as we loop through
			 int customerWishlistSize = customerWishlist.size();
			 if ( isFirstCustomer ) {
				 leastNumProdsPerCust = customerWishlistSize;
				 mostNumProdsPerCust = customerWishlistSize;
				 isFirstCustomer = false;
			 } else {	
				 leastNumProdsPerCust = 
						 Math.min(customerWishlistSize, leastNumProdsPerCust);
				 mostNumProdsPerCust = 
						 Math.max(customerWishlistSize, mostNumProdsPerCust);
			 }

			 // loop over each customer wishlist to get total products
			 // and build list of unique products and their counts
			 Iterator<String> wishlistIter = customerWishlist.iterator();
			 while ( wishlistIter.hasNext() ) {
				 String product = wishlistIter.next();
				 numProducts++;
				 if ( productList.contains(product) ) {
					 int productIndex = productList.indexOf(product);
					 int productCount = productCountList.remove(productIndex);
					 productCount++;
					 productCountList.add(productIndex, productCount);
				 } else {
					 productList.add(product);
					 productCountList.add(1);
				 }
			 }
		 }

		 // get average number of products per customer
		 avgNumProdsPerCust = Math.round( numProducts / (float)(numCustomers) );


		 // iterate over unique products to get customers per product counts
		 Iterator<Integer> productCountIter = productCountList.iterator();
		 while ( productCountIter.hasNext() ) {
			 int productCount = productCountIter.next();
			 numUniqueProducts++;

			 if ( isFirstProduct ) {
				 mostNumCustsPerProd = productCount;
				 leastNumCustsPerProd = productCount;
				 isFirstProduct = false;
			 } else {
				 mostNumCustsPerProd = 
						 Math.max(productCount, mostNumCustsPerProd);
				 leastNumCustsPerProd =
						 Math.min(productCount, leastNumCustsPerProd);
			 }
		 }

		 avgNumCustsPerProd = Math.round( numProducts/(float)(numUniqueProducts) ); 

		 // print the info we have so far
		 System.out.print("Customer: " + numCustomers);
		 System.out.print(", Total Products: " + numProducts);
		 System.out.println(", Unique Products: " + numUniqueProducts);

		 System.out.print("# of products/customer: ");
		 System.out.print("most " + mostNumProdsPerCust);
		 System.out.print(", least " + leastNumProdsPerCust);
		 System.out.println(", average " + avgNumProdsPerCust);

		 System.out.print("# of customers/product: ");
		 System.out.print("most " + mostNumCustsPerProd);
		 System.out.print(", least " + leastNumCustsPerProd);
		 System.out.println(", average " + avgNumCustsPerProd);

		 System.out.print("Most popular product: ");


		 // iterate back over unique products and their counts
		 // to get most popular products and print
		 Iterator<String> productIter = productList.iterator();
		 productCountIter = productCountList.iterator();
		 isFirstProduct = true;
		 while ( productCountIter.hasNext() ) {
			 String product = productIter.next();
			 int productCount = productCountIter.next();
			 if ( productCount == mostNumCustsPerProd) {
				 if ( ! isFirstProduct ) {
					 System.out.print(",");
				 }
				 System.out.print(product);
				 isFirstProduct = false;
			 }
		 }

		 System.out.println(" [" + mostNumCustsPerProd + "]");
	 }

	 /**
	  * Prints the list of command options along with a short description of
	  * one.  This method should not be modified.
	  */
	 private static void printOptions() {
		 System.out.println("d <product> - discontinue the given <product>");
		 System.out.println("f <customer> - find the given <customer>");
		 System.out.println("h - display this help menu");
		 System.out.println("i - display information about this customer database");
		 System.out.println("s <product> - search for the given <product>");
		 System.out.println("q - quit");
		 System.out.println("r <customer> - remove the given <customer>");
	 }

	 /**
	  * Iterate over the specified list of strings and return a comma-
	  * delimited list of the items in the list 
	  *
	  * @param list The list to put in comma-delimited format
	  * @return a comma-delimited string of list items
	  */
	 private static String stringListToString( List<String> list ) {

		 // keep track of first item for comma display
		 boolean isFirstItem = true;
		 // the string to return
		 String outString = "";

		 // if list is null, there is nothing to do
		 if ( list == null ) {
			 return outString; 
		 }

		 Iterator<String> listIter = list.iterator();
		 while ( listIter.hasNext() ) {
			 String item = listIter.next();

			 //only print a comma if not the first item
			 if ( ! isFirstItem ) {
				 outString = outString + ",";
			 }

			 outString = outString + item;
			 isFirstItem = false;
		 }

		 return outString;
	 }
 }