import java.util.*;
import java.lang.*;

///////////////////////////////////////////////////////////////////////////////      
// Main Class File:  InteractiveDBTester
// File:             CustomerDatabase
// Semester:         CS367 Summer 2014 (at Epic)
//
// Author:           Jason Roberts
// Email:            jjroberts345@gmail.com
// CS Login:         jroberts8
// Lecturer's Name:  Beck Hasti
///////////////////////////////////////////////////////////////////////////////

public class CustomerDatabase { 
	ArrayList<Customer> customers;
	ArrayList<Product> products;
	 
	/**
     * Constructs an empty customer database.
     * 
     */
	CustomerDatabase() {
		customers = new ArrayList<Customer>();
		products = new ArrayList<Product>();
	}
	
	/**
     * Add a customer with the given username custName to the end of the data base.  If a customer with
     * username c is already in the database, just return.
     * 
     * @param custName String name of customer
     */
	void createCust(String c) {
        if (getCustomer(c) == null) {
        	Customer myCustomer = new Customer(c);
        	customers.add(myCustomer);
		}
	}
	
	/**
	 * Add a product with the given name prodName to the end of the data base.  If a product with 
	 * name prodName is already in the database, just return
	 * 
	 * @param prodName String name of product
	 */
	void creatProd(String p) {
    	if (getProduct(p) == null) {
    		Product myProduct = new Product(p);
    		products.add(myProduct);
    	}
	}
	
	/**
	 * Add the customer name to the list of customers interested in the Product.  If the customer is already
	 * listed as interested in the product, just return
	 * If the Product is not in the database throw a java.lang.IllegalArgumentException
	 * 
	 * @param prodName String name of product
	 * @param custName String name of customer
	 */
	void addCustToProd (String p, String c) {
		if (getProduct(p) == null) {
			throw new IllegalArgumentException();
			}
		Product myProduct = getProduct(p);
		List<String> myCustList = myProduct.getCustlist();
		if (!myCustList.contains(c)) {
			myCustList.add(c);
		}	
	}
	
	/**
	 * Add the product name to the list of products for the Customer.  If the product is already listed
	 * for the customer, just return
	 * If the customer is not in the database throw a java.lang.IllegalArgumentException
	 * 
	 * @param custName String name of customer
	 * @param prodName String name of product
	 */
	void addProdToCust (String c, String p) {
		if (getCustomer(c) == null) {
			throw new IllegalArgumentException();
			}
		Customer myCustomer = getCustomer(c);
		List<String> myWishList = myCustomer.getWishlist();
		if (!myWishList.contains(p)) {
			myWishList.add(p);
		}	
	}

	/**
	 * Return true iff customer c is in the database
	 * 
	 * @param c customer name
	 * @return boolean
	 */
	boolean containsCustomer(String c) {
		boolean found = false;
		if (!(getCustomer(c) == null)) {
			found = true;
		}
		return found;
	}
	
	/**
	 *Return true iff product p appears in at least one customer's wish list in the database
	 *
	 * @param p product name
	 * @return boolean
	 */
	boolean containsProduct(String p) {
		boolean found = false;
		if (!(getProduct(p) == null)) {
			found = true;
		}
		return found;
	}
	
	/**
	 * Returns true iff product p is in the wish list for customer c 
	 * If customer c is not in the database, return false.
	 * 
	 * @param c customer name
	 * @param p product name
	 * @return boolean
	 */
	boolean hasProduct(String c, String p) {
		boolean found = false;
		Customer myCustomer = getCustomer(c);
		List<String>myWishList = myCustomer.getWishlist();
		if (myWishList.contains(p)) {
			found = true;
		}
		return found;
	}
	
	/**
	 * Returns true iff customer c is in the customer List for product p 
	 * If customer c is not in the database, return false.
	 * 
	 * @param p product name
	 * @param c customer name
	 * @return boolean
	 */
	boolean hasCustomer(String p, String c) {
		boolean found = false;
		Product myProduct = getProduct(p);
		List<String>myCustList = myProduct.getCustlist();
		if (myCustList.contains(c)) {
			found = true;
		}
		return found;
	}
	
	/**
	 * Return the list of customers who have product p in their wish list. 
	 * If product p is not in the database, return null.
	 * 
	 * @param p product name
	 * @return List<String> list of customers with product p in their wish list
	 */
	List<String> getCustList(String p) {
		List<String> myCustList = null;
		Product myProduct = getProduct(p);
		myCustList = myProduct.getCustlist();
		return myCustList;
 	}
	
	/**
	 * Return the wish list for the customer c. If a customer c is not in the database, return null.
	 *
	 * @param c customer name
	 * @return List<String> list of products in the wish list for a customer
	 */
	List<String> getProdList(String c) {
		List<String> myProdList = null;
		Customer myCustomer = getCustomer(c);
		myProdList = myCustomer.getWishlist();
		return myProdList;
	}
	
	/**
	 * Return an Iterator over the Customer objects in the database. 
	 * The customers should be returned in the order they were added to the database 
	 * (resulting from the order in which they are in the text file).
	 * 
	 * @return an iterator
	 */
	ListIterator<Customer> custIterator() {
		ListIterator<Customer> iter = customers.listIterator(0);
		return iter;
	}	
	
	/**
	 * Return an Iterator over the Product objects in the database
	 * The products should be returned in the order they were added to the database
	 * (resulting from the order in which they are in the text file)
	 * 
	 * @return an iterator
	 */
	ListIterator<Product> prodIterator() {
		ListIterator<Product> iter = products.listIterator(0);
		return iter;
	}
	/**
	 * Remove customer c from the database. If customer c is not in the database,
	 * return false; otherwise (i.e., the removal is successful) return true.
	 * 
	 * @param c
	 * @return boolean
	 */
	boolean removeCustomer(String c) {
		boolean removed = false;
		
		// check that customer is in the database
		if (containsCustomer(c)) {
			Customer myCustomer = getCustomer(c);
			
			// loop through Products so we can remove Customer from Products where it is listed
			ListIterator<Product> iter = prodIterator();
			while (iter.hasNext()) {
				Product myProduct = iter.next();
				String prodName = myProduct.getName();
				if (hasCustomer(prodName,c)) {
					List<String> myCustList = myProduct.getCustlist();
					myCustList.remove(c);
					
					// if the customer we're removing is the only one in Product, remove the Product
					if (myCustList.size() < 1) {
						products.remove(myProduct);
						prodName = null;
						myCustList = null;
					}
				}
			}
			
			// remove references to the customer
			customers.remove(myCustomer);
			c = null;
			List<String> myWishList = myCustomer.getWishlist();
			myWishList = null;
			removed = true;
		}
		return removed;
	}
	
	/**
	 * Remove product p from the database, i.e., remove product p from every wish list
	 * in which it appears. If product p is not in the database, return false;
	 * otherwise (i.e., the removal is successful) return true.
	 * 
	 * @param c
	 * @return boolean
	 */
	boolean removeProduct(String p) {
		boolean removed = false;
		
		// check that the Product is in the database
		if (containsProduct(p)) {
			Product myProduct = getProduct(p);
			ListIterator<Customer> iter = custIterator();
			
			// loop through the Customers so we can remove references to the Product in the Customer
			while (iter.hasNext()) {
				Customer myCustomer= iter.next();
				String custName = myCustomer.getUsername();
				if (hasProduct(custName,p)) {
					List<String> myWishList = myCustomer.getWishlist();
					myWishList.remove(p);
					
					
					// if the Product we're removing is the only one in the Customer, remove the Customer too
					if (myWishList.size() < 1) {
						customers.remove(myCustomer);
						custName = null;
						myWishList = null;
					}
				}
			}
			
			// remove references to the Product
			products.remove(myProduct);
			p = null;
			List<String> myCustList = myProduct.getCustlist();
			myCustList = null;
			removed = true;
		}
		return removed;
	}
	
	/**
	 * Return the number of customers in this database
	 * 
	 * @return int number of customers
	 */			
	int size() {
		return customers.size();			
	}
	
	/**
	 * return the number of products in this database
	 * 
	 * @return int number of products
	 */
	int prodCount() {
		return products.size();
	}
	
	/**
	 * Return total counts of customers and products
	 * 
	 * @return String return counts
	 */
	String totals() {
		int custcount = size();
		int prodcount = prodCount();
		String totals = "Customers: " + custcount + ", Products: " + prodcount;
		return totals;
	}
	
	/**
	 * Return statistics about the number of customers per product
	 * 
	 * @return String stats about number of customers per product
	 */
	public String prodStats() {
		int max = 0;
		int min = 0;
		float sum = 0;
		float avg = 0;
		int intAvg = 0;
		
		Iterator<Product> iter = prodIterator();
		while (iter.hasNext()) {
			Product tempProd = iter.next();
			List<String>myCustList = tempProd.getCustlist();
			int size = myCustList.size();
			
			if (max == 0) {
				min = size;
			}
			if (size > max) {
				max = size;
			}
			if (size < min) {
				min = size;
			}
			sum = sum + size;
		}
		
		avg = sum / prodCount();
		intAvg = Math.round(avg);
		
		String prodStats = "# of customers/product: most " + max + ", least " + min + ", average " + intAvg;
		return prodStats;
	}
	
	/**
	 * Return stats about the number of products per customer
	 * 
	 * @return String stats about the number of products per customer
	 */
	public String custStats() {
		int max = 0;
		int min = 0;
		float sum = 0;
		float avg = 0;
		int intAvg = 0;
		
		Iterator<Customer> iter = custIterator();
		while (iter.hasNext()) {
			Customer tempCust = iter.next();
			List<String>myWishList = tempCust.getWishlist();
			int size = myWishList.size();
			
			if (max == 0) {
				min = size;
			}
			if (size > max) {
				max = size;
			}
			if (size < min) {
				min = size;
			}
			sum = sum + size;
		}
		
		avg = sum / size();
		intAvg = Math.round(avg);
		
		String custStats = "# of products/customer: most " + max + ", least " + min + ", average " + intAvg;
		return custStats; 		
	}
	
	/**
	 * return information about the most popular product with customers
	 * 
	 * @return String info about the most popular product
	 */
	String mostPopular() {
		ListIterator<Product> iter = prodIterator();
		int max = 0;
		String mostPopular = null;
		
		// loop over products 
		while (iter.hasNext()) {
			Product tempProd = iter.next();
			String myName = tempProd.getName();
			List<String> myCustList = tempProd.getCustlist();
			int listSize = myCustList.size();
			
			if (listSize > max) {
				max = listSize;
				mostPopular = myName;
			}
			else if (listSize == max) {
				if (mostPopular.length() > 0) {
					mostPopular = mostPopular + ",";
				}
				mostPopular = mostPopular + myName;
			}	
		}
		mostPopular = "Most popular product: " + mostPopular + " [" + max + "]";
		return mostPopular;
	}
	
	/**
	 * Return the Customer<Object> with a given name
	 * 
	 * @param c Customer name
	 * @return Customer<Object>
	 */
	private Customer getCustomer (String c) {
		ListIterator<Customer> iter = custIterator();
		Customer myCustomer = null;
		boolean found = false;
		
		while (iter.hasNext() && found != true) {
			Customer tempCust = iter.next();
			String tempName = tempCust.getUsername();
			
			if (c.equals(tempName)) {
				found = true;
				myCustomer = tempCust;
			}	
		}
		return myCustomer;
	}
		
	/**
	 * Return the Product<Object> with a given name
	 * 
	 * @param p Product name
	 * @return Product<Object>
	 */
	private Product getProduct (String p) {
		ListIterator<Product> iter = prodIterator();
		Product myProduct = null;
		boolean found = false;
		
		while (iter.hasNext() && found != true) {
			Product tempProd = iter.next();
			String tempName = tempProd.getName();
			
			if (p.equals(tempName)) {
				found = true;
				myProduct = tempProd;
			}	
		}
		return myProduct;
	}
	
	/**
	 * Return list of Products for a given customer, or "customer not found"
	 * 
	 * @param c Customer name
	 * @return String of Products for customers or "customer not found"
	 */
	String custInfo (String c) {
		String custInfo = null;
		if (!containsCustomer(c)) {
			custInfo = "customer not found";
		}
		else {
			List<String> myProdList = getProdList(c);
			ListIterator<String> iter = myProdList.listIterator();
			
			while (iter.hasNext()) {
				String tempName = iter.next();
				
				if (!(custInfo == null)) {
					custInfo = custInfo + "," + tempName;
				}
				else {
					custInfo = tempName;
				}
			}
			custInfo = c + ":" + custInfo;
		}
		return custInfo;	
	}
	
	/**
	 * Return list of customers for a given Product or "product not found"
	 * 
	 * @param p Product name
	 * @return String of customers for given product or "product not found"
	 */
	String prodInfo (String p) {
		String prodInfo = null;
		if (!containsProduct(p)) {
			prodInfo = "product not found";
		}
		
		else {
			List<String> myCustList = getCustList(p);
			ListIterator<String> iter = myCustList.listIterator();
			while (iter.hasNext()) {
				String tempName = iter.next();
				
				if (!(prodInfo == null)) {
					prodInfo = prodInfo + "," + tempName;
				}
				else {
					prodInfo = tempName;
				}
			}
			prodInfo = p + ":" + prodInfo;
		}
		return prodInfo;	
	}
}