// Writing skeleton: 8:12 - 8:25am
// pseudocode 8:03 - 8:20pm
// implementation 4:20 - 5:30
// unit test 5:30 - 5:50
// enhancements 6:10 - 7:18
// LOC 110
import java.util.*;
import java.io.*;

public class UpcReceiptCalc
{
public static void main(String args[]) 
{
    (new UpcReceiptCalc()).calculateSalesReceipt();
}

public void calculateSalesReceipt()
{
    purchaseList = new HashMap<String,ProductCount>();
    invalidList = new ArrayList<InvalidItem>();    
    readProductDB();
    processUPCs();
    assembleSalesReceipt();
    System.out.println(salesReceipt);
    showInvalidList();
}

public void readProductDB()  
{   
    productDB = new HashMap<String,Product>();
    try
    {
        //open "productDB.txt" file
        Scanner fileReader = new Scanner(new FileReader("productDB.txt"));
        
        String currentLine = null;
        String[] splitLine = null;
        String nameAndPrice = null;
        String delims = "[{\",]+";
        
        //FOR each line in input file LOOP
        while (fileReader.hasNextLine()) 
        {                  
            currentLine = fileReader.nextLine();
            // parse the input line
            splitLine = currentLine.split(delims);
            nameAndPrice = splitLine[2] + "," + splitLine[4];
            Product newItem = new Product();
            newItem.upc = splitLine[1];
            newItem.productName = splitLine[2];
            newItem.price = dollarsToInt(splitLine[4]);
            //Add line to product db
            productDB.put(newItem.upc, newItem);
        }     //END LOOP
    }
    catch (FileNotFoundException ex)
    {
        ex.printStackTrace();
        System.exit(1);
    }
}

private int dollarsToInt(String amount)
{
    return Integer.parseInt(amount.replaceAll(",|\\.", ""));  
}
public void processUPCs()
{
    console = new Scanner(System.in);
    //WHILE more upc's exist LOOP
    while (console.hasNextLine())
    {
        //READ upc
        rawUPC = console.nextLine();
        product = null;
        lookupProduct();
        //IF product found in DB THEN
        if (product != null)
        {
            // increase count and total
            countProduct();
            computeTotal();
        } //END IF    
    } //END LOOP
}
public void assembleSalesReceipt()
{
    salesReceipt = new StringBuffer();
    // FOR each item in purchase list LOOP
    for (String upc : purchaseList.keySet())
    {
        ProductCount purchase = purchaseList.get(upc);
        //         compute the subtotal as itemcount * price.  
        subtotal = purchase.itemCount * purchase.item.price;
        //         format a line with product name, itemcount, subtotal
        String lineout = String.format("%-30s",purchase.item.productName) 
        +"\t("+ String.format("%3d",purchase.itemCount) +")\t"
        + String.format("%3.2f",subtotal/100.0)+"\n";
        //         append the line to the receipt
        salesReceipt.append(lineout);
    }        //     END LOOP

    //     Append the total price as the last line of the receipt
    salesReceipt.append(String.format("%30s","TOTAL") 
        + "\t("+ String.format("%3d",totalItems) +")\t"
        + String.format("%3.2f",totalPrice/100.0)+"\n");
}
public void showInvalidList()
{
    //Display each item in invalidlist
    for (InvalidItem item : invalidList)
    {
        System.out.print(item.upc + " ");
        System.out.println(item.message);
    }
}
public void lookupProduct()
{
    verifiedUPC = null;
    verifyUPC();
    //IF (verifiedUPC exists) THEN
    if (verifiedUPC != null)
    {
        product = productDB.get(verifiedUPC);
        //IF productDB does not contain (upc) THEN
        if (product == null)
        {
            // create an invalid item from upc and "not in DB"
            InvalidItem baditem = new InvalidItem();
            baditem.upc = verifiedUPC;
            baditem.message = "product doesn't exist in the database";
            //    add invalid item to the invalid list.    
            invalidList.add(baditem);
        }
    } //   END IF    
}
public void verifyUPC()
{
  //Given a raw upc apply the Check Digit algorithm provided.
  boolean verified = checkDigit(rawUPC);
  //IF not verified THEN
  if (!verified)
  {
      //    create an invalid item from upc and "invalid check digit"
      InvalidItem baditem = new InvalidItem();
      baditem.upc = rawUPC;
      baditem.message = "check digit doesn't match";
      //    add invalid item to the invalid list.    
      invalidList.add(baditem);
  }
  //ELSE
  else
  {
      //    set verifiedUPC from upc
      verifiedUPC = rawUPC;
  }
  //END
}  
    public boolean checkDigit(String upc)
    {
        int checkDigit;
        //calc sum of odd-numbered positions & multiply by 3
        int oddNumberedPos = (Character.getNumericValue(upc.charAt(0)) + 
                        Character.getNumericValue(upc.charAt(2)) + 
                        Character.getNumericValue(upc.charAt(4)) +
                        Character.getNumericValue(upc.charAt(6)) +
                        Character.getNumericValue(upc.charAt(8)) +
                        Character.getNumericValue(upc.charAt(10))) * 3;
        int evenNumberedSum = Character.getNumericValue(upc.charAt(1)) + 
                        Character.getNumericValue(upc.charAt(3)) + 
                        Character.getNumericValue(upc.charAt(5)) +
                        Character.getNumericValue(upc.charAt(7)) +
                        Character.getNumericValue(upc.charAt(9));
                                
        Integer result = new Integer(oddNumberedPos + evenNumberedSum);
        String strResult = result.toString();
        int lastDigit = 
            Character.getNumericValue(strResult.charAt(strResult.length() - 1));
        
        //if lastDigit == 0
        if (lastDigit == 0)
            checkDigit = 0;
        else
            checkDigit = 10 - lastDigit;
            
        return checkDigit == Character.getNumericValue(upc.charAt(11));
        
    }
public void countProduct()
{
    //Given a product, increment the itemcount in purchase list.    
    ProductCount pc = purchaseList.get(verifiedUPC);
    // If this is the first purchase of this product
    if (pc == null)
    {
        pc = new ProductCount();
        pc.item = product;
        pc.itemCount = 1;
        purchaseList.put(verifiedUPC, pc);
    }
    // update the count for a previously purchased product
    else
    {
        pc.itemCount++;
        purchaseList.put(verifiedUPC, pc);
    }
  
}
public void computeTotal()
{
    //Given a product, increment the total price with the price of this product.    
    totalPrice += product.price;
    totalItems++;
}

// Data Structures
// List of products.
Map<String,Product> productDB;
// List of products and itemcounts, in order of arrival.
Map<String,ProductCount> purchaseList;
// List of invalid items, in order of arrival.
List<InvalidItem> invalidList;

String rawUPC;
String verifiedUPC;
Product product;
String productName;
String price;
int quantityPurchased;
int subtotal;
int totalPrice;
int totalItems;
StringBuffer salesReceipt;
Scanner console;

}
class Product
{
    String upc;
    String productName;
    int price; // in cents
}

class ProductCount
{
    Product item;
    int itemCount;
}

class InvalidItem
{
    String upc;
    String message;
}