/* 
 * TPCW_Database.java - Contains all of the code involved with database
 *                      accesses, including all of the JDBC calls. These
 *                      functions are called by many of the servlets.
 *
 ************************************************************************
 *
 * This is part of the the Java TPC-W distribution,
 * written by Harold Cain, Tim Heil, Milo Martin, Eric Weglarz, and Todd
 * Bezenek.  University of Wisconsin - Madison, Computer Sciences
 * Dept. and Dept. of Electrical and Computer Engineering, as a part of
 * Prof. Mikko Lipasti's Fall 1999 ECE 902 course.
 *
 * Copyright (C) 1999, 2000 by Harold Cain, Timothy Heil, Milo Martin, 
 *                             Eric Weglarz, Todd Bezenek.
 *
 * This source code is distributed "as is" in the hope that it will be
 * useful.  It comes with no warranty, and no author or distributor
 * accepts any responsibility for the consequences of its use.
 *
 * Everyone is granted permission to copy, modify and redistribute
 * this code under the following conditions:
 *
 * This code is distributed for non-commercial use only.
 * Please contact the maintainer for restrictions applying to 
 * commercial use of these tools.
 *
 * Permission is granted to anyone to make or distribute copies
 * of this code, either as received or modified, in any
 * medium, provided that all copyright notices, permission and
 * nonwarranty notices are preserved, and that the distributor
 * grants the recipient permission for further redistribution as
 * permitted by this document.
 *
 * Permission is granted to distribute this code in compiled
 * or executable form under the same conditions that apply for
 * source code, provided that either:
 *
 * A. it is accompanied by the corresponding machine-readable
 *    source code,
 * B. it is accompanied by a written offer, with no time limit,
 *    to give anyone a machine-readable copy of the corresponding
 *    source code in return for reimbursement of the cost of
 *    distribution.  This written offer must permit verbatim
 *    duplication by anyone, or
 * C. it is distributed by someone who received only the
 *    executable form, and is accompanied by a copy of the
 *    written offer of source code that they received concurrently.
 *
 * In other words, you are welcome to use, share and improve this codes.
 * You are forbidden to forbid anyone else to use, share and improve what
 * you give them.
 *
 ************************************************************************/
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import javax.servlet.http.HttpSession;

public class TPCW_Database {
    
    // Added by Luis Rodero-Merino
	private final static String DB_PATH_ENVVAR_NAME = "TPCW_DB_PATH";
	private final static String DB_POOLSIZE_ENVVAR_NAME = "TPCW_DB_POOLSIZE";
	private final static int DEFAULT_POOL_SIZE = 250;
	
    static {    	

    	// Paths to database we use //
    	// middle-r port 6501
    	// c-jdbd port 25322
    	// default: postgres port 5432 (or 6551) 
       	boolean useMiddlerDiscovery = true;
       	boolean useCjdbc = false;
       	if(useMiddlerDiscovery && useCjdbc)
       		throw new Error("Cannot use middle-r and c-jdbc at the same time!");

        String jdbcPath = null; 
        if(useCjdbc)
        	jdbcPath = "jdbc:cjdbc://localhost:25322/tpcw";
       	else if(useMiddlerDiscovery)
       		jdbcPath = "jdbc:postgresql://224.1.1.200:6601/tpcw"; // IP and port are the same than the ones in from es.upm.fi.lsd.middler.replicasControl.ReplicasDiscoveryProtocol
        else
        	jdbcPath = "jdbc:postgresql://localhost:5432/tpcw";
        
        String jdbcDriverClass = null;
        if(useCjdbc)
        	jdbcDriverClass = "org.objectweb.cjdbc.driver.Driver";
        else
        	jdbcDriverClass = "org.postgresql.Driver"; // We assume that both for middle-r and postgres the driver uses this name
        
        Map<String, String> env = System.getenv();
        if(env.containsKey(DB_PATH_ENVVAR_NAME)) {
        	jdbcPath = env.get(DB_PATH_ENVVAR_NAME);
            System.out.println("TPCW database path: " + jdbcPath + " (read from environment var '" + DB_PATH_ENVVAR_NAME+ "')");
        } else
            System.out.println("TPCW database path: " + jdbcPath + " (hardcoded one, as environment var '" + DB_PATH_ENVVAR_NAME + "' is not defined)");
    	
        // Setting discovery process configuration
    	Properties driverConf = new Properties();
    	driverConf.put("user","postgres");
    	if(useCjdbc)
    		driverConf.put("password","");
    	else
    		driverConf.put("password","postgres");
    	if(useMiddlerDiscovery) {
    		driverConf.put("middler.disc.waitingTime", 20+""); // Time the driver will wait for replicas info in each try (ms)
    		driverConf.put("middler.disc.tries", 5+""); // Number of tries of the driver
    		driverConf.put("middler.disc.policy", 1+""); // Policy can be 0 (replica is chosen at random) or 1 (prob proportional to load) 
    		driverConf.put("middler.disc.logLevel", "DEBUG");
    		driverConf.put("middler.disc.replicasFraction", 0.5+"");
    	}

    	// Getting pool of connections ready //
    	// Setting initial pool size
    	int poolSize = DEFAULT_POOL_SIZE;
    	if(env.containsKey(DB_POOLSIZE_ENVVAR_NAME)) {
    		String poolSizeString = env.get(DB_POOLSIZE_ENVVAR_NAME);
    		try {
    			poolSize = Integer.parseInt(poolSizeString);
    			if(poolSize <= 0)
        			System.out.println("TPDW database pool size must be a positive integer, but " + poolSize + " was read from enviroment var '" + DB_POOLSIZE_ENVVAR_NAME + "'");    				
    		} catch(NumberFormatException exception) {
    			System.out.println("TPDW database pool size could not be read! " + poolSizeString + " in environment var '" + DB_POOLSIZE_ENVVAR_NAME + "' could not be parsed to an integer");
    			poolSize = -1;    			
    		}
    		if(poolSize > 0)
    			System.out.println("TPDW database pool size: " + poolSize + " (read from environment var '" + DB_POOLSIZE_ENVVAR_NAME + "'");
    		else
    			System.out.println("TPDW database pool size: " + poolSize + " (hardcoded one, as environment var '" + DB_POOLSIZE_ENVVAR_NAME + "' does not have a valid value)");
			
    	} else
    		System.out.println("TPDW database pool size: " + poolSize + " (hardcoded one, as environment var '" + DB_POOLSIZE_ENVVAR_NAME + "' is not defined)");
		int poolThreads = (int)Math.ceil(poolSize/5.0);
		System.out.println("TPDW database pool filling threads: " + poolThreads + " (" + poolSize +"/5)");
		
    	try {
			ConnectionsPool.preparePool(jdbcPath, jdbcDriverClass, true, driverConf, poolSize, poolThreads);
		} catch (SQLException exception) {
			System.err.println("Exception caught when trying to fill connections pool");
			exception.printStackTrace(System.err);
		} 	
    }

    public static String[] getName(HttpSession session, long c_id) {
		String name[] = new String[2];
		try {
		    // Prepare SQL
		    //	    out.println("About to call getConnection!");
		    //            out.flush();
			Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    //	    out.println("About to preparestatement!");
		    //            out.flush();
		    PreparedStatement get_name = con.prepareStatement
			("SELECT c_fname,c_lname FROM customer WHERE c_id = ?");
		    
		    // Set parameter
		    get_name.setLong(1, c_id);
		    // 	    out.println("About to execute query!");
		    //            out.flush();
	
		    ResultSet rs = get_name.executeQuery();
		    
		    // Results
		    rs.next();
		    name[0] = rs.getString("c_fname");
		    name[1] = rs.getString("c_lname");
		    rs.close();
		    get_name.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT c_fname,c_lname FROM customer WHERE c_id = " + c_id);
		    ex.printStackTrace();
		}
		return name;
    }

    public static Book getBook(HttpSession session, long i_id) {
		Book book = null;
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    PreparedStatement statement = con.prepareStatement
			("SELECT * FROM item,author WHERE item.i_a_id = author.a_id AND i_id = ?");
		    
		    // Set parameter
		    statement.setLong(1, i_id);
		    ResultSet rs = statement.executeQuery();
		    
		    // Results
		    rs.next();
		    book = new Book(rs);
		    rs.close();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT * FROM item,author WHERE item.i_a_id = author.a_id AND i_id = " + i_id);
		    ex.printStackTrace();
		}
		return book;
    }

    public static Customer getCustomer(HttpSession session, String UNAME){
		Customer cust = null;
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    PreparedStatement statement = con.prepareStatement("SELECT * FROM customer, address, country WHERE customer.c_addr_id = address.addr_id AND address.addr_co_id = country.co_id AND customer.c_uname = ?");
		    
		    // Set parameter
		    statement.setString(1, UNAME);
		    ResultSet rs = statement.executeQuery();
		    
		    // Results
		    if(rs.next())
				cust = new Customer(rs);
		    else {
				System.err.println("ERROR: NULL returned in getCustomer!, use name is: " + UNAME);
				rs.close();
				statement.close();
				ConnectionsPool.getInstance().returnConnection(con);
				return null;
		    }
		    
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT * FROM customer, address, country WHERE customer.c_addr_id = address.addr_id AND address.addr_co_id = country.co_id AND customer.c_uname = " + UNAME);
		    ex.printStackTrace(System.err);
		}
		return cust;
    }

    public static Vector doSubjectSearch(HttpSession session, String search_key) {
		Vector vec = new Vector();
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    PreparedStatement statement = con.prepareStatement
			("SELECT * FROM item, author WHERE item.i_a_id = author.a_id AND item.i_subject = ? ORDER BY item.i_title LIMIT 50");
		    
		    // Set parameter
		    statement.setString(1, search_key);
		    ResultSet rs = statement.executeQuery();
		    
		    // Results
		    while(rs.next()) {
		    	vec.addElement(new Book(rs));
		    }
		    rs.close();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT * FROM item, author WHERE item.i_a_id = author.a_id AND item.i_subject = " + search_key + " ORDER BY item.i_title LIMIT 50");
		    ex.printStackTrace();
		}
		return vec;	
    }

    public static Vector doTitleSearch(HttpSession session, String search_key) {
		Vector vec = new Vector();
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    PreparedStatement statement = con.prepareStatement
			("SELECT * FROM item, author WHERE item.i_a_id = author.a_id AND item.i_title LIKE ? ORDER BY item.i_title LIMIT 50");
		    
		    // Set parameter
		    statement.setString(1, search_key+"%");
		    ResultSet rs = statement.executeQuery();
		    
		    // Results
		    while(rs.next()) {
		    	vec.addElement(new Book(rs));
		    }
		    rs.close();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT * FROM item, author WHERE item.i_a_id = author.a_id AND item.i_title LIKE " + search_key + " ORDER BY item.i_title LIMIT 50");
		    ex.printStackTrace();
		}
		return vec;	
    }

    public static Vector doAuthorSearch(HttpSession session, String search_key) {
		Vector vec = new Vector();
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    PreparedStatement statement = con.prepareStatement
			("SELECT * FROM author, item WHERE author.a_lname LIKE ? AND item.i_a_id = author.a_id ORDER BY item.i_title LIMIT 50");
	
		    // Set parameter
		    statement.setString(1, search_key+"%");
		    ResultSet rs = statement.executeQuery();
	
		    // Results
		    while(rs.next()) {
		    	vec.addElement(new Book(rs));
		    }
		    rs.close();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT * FROM author, item WHERE author.a_lname LIKE " + search_key + " AND item.i_a_id = author.a_id ORDER BY item.i_title LIMIT 50");
		    ex.printStackTrace();
		}
		return vec;	
    }

    public static Vector getNewProducts(HttpSession session, String subject) {
		Vector vec = new Vector();  // Vector of Books
		
		String sqlStatement = "SELECT i_id, i_title, a_fname, a_lname " +
		 "FROM item, author " +
		 "WHERE item.i_a_id = author.a_id " +
		 "AND item.i_subject = '" + subject + "' " +
		 "ORDER BY item.i_pub_date DESC,item.i_title " +
		 "LIMIT 50";
		
		try {
			
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    PreparedStatement statement = con.prepareStatement(sqlStatement);
	
		    // Set parameter
		    //statement.setString(1, subject);
		    ResultSet rs = statement.executeQuery();
	
		    // Results
		    while(rs.next()) {
		    	vec.addElement(new ShortBook(rs));
		    }
		    rs.close();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		    
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlStatement);
			ex.printStackTrace();
		}
		return vec;	
    }

    public static Vector getBestSellers(HttpSession session, String subject) {
		
    	Vector vec = new Vector();  // Vector of Books
		String sqlStatement = null;
    	
		try {
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    
		    sqlStatement = "SELECT nextval('o_id_seq')";
		    PreparedStatement get_next_id = con.prepareStatement(sqlStatement);
			ResultSet rs = get_next_id.executeQuery();
			rs.next();
			long max_o_id = rs.getLong(1) - 3333;
			get_next_id.close();
			rs.close();
		    
		    //The following is the original, unoptimized best sellers query.
			sqlStatement = "SELECT i_id, i_title, a_fname, a_lname " +
			 				"FROM item, author, order_line " +
			 				"WHERE item.i_id = order_line.ol_i_id " +
			 				"AND item.i_a_id = author.a_id " +
			 				"AND order_line.ol_o_id > ? " +
			 				//	"AND order_line.ol_o_id > (SELECT MAX(o_id)-3333 FROM orders) " +
			 				"AND item.i_subject = ? " +
			 				"GROUP BY i_id, i_title, a_fname, a_lname " +
			 				"ORDER BY SUM(ol_qty) DESC " +
			 				"LIMIT 50";
		    PreparedStatement statement = con.prepareStatement(sqlStatement);

		    // Set parameters
		    statement.setLong(1,max_o_id);
		    statement.setString(2, subject);
		    
		    rs = statement.executeQuery();
		    // Results
		    while(rs.next())
		    	vec.addElement(new ShortBook(rs));
		    
		    rs.close();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlStatement);
			ex.printStackTrace();
		}
		return vec;	
    }

    public static void getRelated(HttpSession session, long i_id, Vector i_id_vec, Vector i_thumbnail_vec) {
	    try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    PreparedStatement statement = con.prepareStatement("SELECT J.i_id,J.i_thumbnail FROM item I, item J WHERE (I.i_related1 = J.i_id OR I.i_related2 = J.i_id OR I.i_related3 = J.i_id OR I.i_related4 = J.i_id OR I.i_related5 = J.i_id) AND I.i_id = ?");
	
		    // Set parameter
		    statement.setLong(1, i_id);
		    ResultSet rs = statement.executeQuery();
	
		    // Clear the vectors
		    i_id_vec.removeAllElements();
		    i_thumbnail_vec.removeAllElements();
	
		    // Results
		    while(rs.next()) {
				i_id_vec.addElement(new Long(rs.getLong(1)));
				i_thumbnail_vec.addElement(rs.getString(2));
		    }
		    rs.close();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT J.i_id,J.i_thumbnail FROM item I, item J WHERE (I.i_related1 = J.i_id OR I.i_related2 = J.i_id OR I.i_related3 = J.i_id OR I.i_related4 = J.i_id OR I.i_related5 = J.i_id) AND I.i_id = " + i_id);
			ex.printStackTrace();
		}
    }

    public static void adminUpdate(HttpSession session, long i_id, double cost, String image, String thumbnail) {
    	
    	String sqlSentence = null;
    	
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    
		    sqlSentence = "UPDATE item SET i_cost = " + cost + ", i_image = " + image + ", i_thumbnail = " + thumbnail + ", i_pub_date = date('now') WHERE i_id = " + i_id;
		    PreparedStatement statement = con.prepareStatement("UPDATE item SET i_cost = ?, i_image = ?, i_thumbnail = ?, i_pub_date = date('now') WHERE i_id = ?");
	
		    // Set parameter
		    statement.setDouble(1, cost);
		    statement.setString(2, image);
		    statement.setString(3, thumbnail);
		    statement.setLong(4, i_id);
		    statement.executeUpdate();
		    statement.close();
		    
		    sqlSentence = "SELECT nextval('o_id_seq')";
		    PreparedStatement get_max_o_id = con.prepareStatement(sqlSentence);
			ResultSet rs = get_max_o_id.executeQuery();
			rs.next();
			long max_o_id = rs.getLong(1) - 10000;
			get_max_o_id.close();
			rs.close();
		    
		    sqlSentence = 	"SELECT ol_i_id " +
							 "FROM orders, order_line " + 
							 "WHERE orders.o_id = order_line.ol_o_id " +
							 "AND NOT (order_line.ol_i_id = " + i_id + ")" +
							 "AND orders.o_c_id IN (SELECT o_c_id " +
					 		 "                      FROM orders, order_line " +
							 "                      WHERE orders.o_id = order_line.ol_o_id " +
							 "                      AND orders.o_id > " + max_o_id + 
							 "                      AND order_line.ol_i_id = " + i_id + ")" +
							 "GROUP BY ol_i_id " +
							 "ORDER BY SUM(ol_qty) DESC " +
							 "LIMIT 5";
		    
		    PreparedStatement related = con.prepareStatement
			("SELECT ol_i_id " +
			 "FROM orders, order_line " + 
			 "WHERE orders.o_id = order_line.ol_o_id " +
			 "AND NOT (order_line.ol_i_id = ?) " +
			 "AND orders.o_c_id IN (SELECT o_c_id " +
	 		 "                      FROM orders, order_line " +
			 "                      WHERE orders.o_id = order_line.ol_o_id " +
			 "                      AND orders.o_id > ?" + 
			 "                      AND order_line.ol_i_id = ?) " +
			 "GROUP BY ol_i_id " +
			 "ORDER BY SUM(ol_qty) DESC " +
			 "LIMIT 5");
	
		    // Set parameter
		    related.setLong(1, i_id);	
		    related.setLong(2, max_o_id);
		    related.setLong(3, i_id);
		    rs = related.executeQuery();
		    
		    long[] related_items = new long[5];
		    // Results
		    int counter = 0;
		    long last = 0;
		    while(rs.next()) {
				last = rs.getLong(1);
				related_items[counter] = last;
				counter++;
		    }
	
		    // This is the case for the situation-there are not 5 related books.
		    for (int i=counter; i<5; i++) {
				last++;
				related_items[i] = last;
		    }
		    rs.close();
		    related.close();
	
		    sqlSentence = "UPDATE item SET i_related1 = " + related_items[0] +
		    				", i_related2 = " + related_items[1] +
		    				", i_related3 = " + related_items[2] +
		    				", i_related4 = " + related_items[3] + 
		    				", i_related5 = " + related_items[4] +
		    				" WHERE i_id = " + i_id;
		    // Prepare SQL
			statement = con.prepareStatement("UPDATE item SET i_related1 = ?, i_related2 = ?, i_related3 = ?, i_related4 = ?, i_related5 = ? WHERE i_id = ?");
			
			// Set parameter
			statement.setLong(1, related_items[0]);
			statement.setLong(2, related_items[1]);
			statement.setLong(3, related_items[2]);
			statement.setLong(4, related_items[3]);
			statement.setLong(5, related_items[4]);
			statement.setLong(6, i_id);
			statement.executeUpdate();
		    statement.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		    
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
			ex.printStackTrace();
		}	
		
    }

    public static String GetUserName(HttpSession session, long c_id){
		String u_name = null;
		String sqlSentence = null;
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    //System.out.println("[LRM] Getting name of user " + c_id + ", jsession " + session.getId() + " in " + con.getMetaData().getURL());
			sqlSentence = "SELECT c_uname FROM customer WHERE c_id = " + c_id;
		    PreparedStatement get_user_name = con.prepareStatement("SELECT c_uname FROM customer WHERE c_id = ?");
		    
		    // Set parameter
		    get_user_name.setLong(1, c_id);
		    ResultSet rs = get_user_name.executeQuery();
		    
		    // Results
		    rs.next();
		    u_name = rs.getString("c_uname");
		    rs.close();
	
		    get_user_name.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
			ex.printStackTrace();
		}
		return u_name;
    }

    public static String GetPassword(HttpSession session, String c_uname){
		String passwd = null;
		String sqlSentence = null;
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    sqlSentence = "SELECT c_passwd FROM customer WHERE c_uname = " + c_uname;
		    PreparedStatement get_passwd = con.prepareStatement("SELECT c_passwd FROM customer WHERE c_uname = ?");
		    
		    // Set parameter
		    get_passwd.setString(1, c_uname);
		    ResultSet rs = get_passwd.executeQuery();
		    
		    // Results
		    rs.next();
		    passwd = rs.getString("c_passwd");
		    rs.close();
	
		    get_passwd.close();
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
			ex.printStackTrace();
		}
		return passwd;
    }

    public static Order GetMostRecentOrder(HttpSession session, String c_uname, Vector order_lines){
    	
    	String sqlSentence = null;
    	
		try {
		    order_lines.removeAllElements();
		    long order_id;
		    Order order;
	
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
	
		    //	    System.out.println("cust_id: " + getCustomer(c_uname).c_id);
	
		    // *** Get the o_id of the most recent order for this user
		    sqlSentence = "SELECT o_id " +
			     "FROM customer, orders " +
			     "WHERE customer.c_id = orders.o_c_id " +
			     "AND c_uname = " + c_uname + 
			     "ORDER BY o_date, orders.o_id DESC " +
			     "LIMIT 1";
			PreparedStatement get_most_recent_order_id = con.prepareStatement
			    ("SELECT o_id " +
			     "FROM customer, orders " +
			     "WHERE customer.c_id = orders.o_c_id " +
			     "AND c_uname = ? " +
			     "ORDER BY o_date, orders.o_id DESC " +
			     "LIMIT 1");
			
			// Set parameter
			get_most_recent_order_id.setString(1, c_uname);
			ResultSet rs = get_most_recent_order_id.executeQuery();
			
			if (rs.next()) {
			    order_id = rs.getLong("o_id");
			} else {
			    // There is no most recent order
			    rs.close();
			    get_most_recent_order_id.close();
			    con.commit();
			    ConnectionsPool.getInstance().returnConnection(con);
			    return null;
			}
			rs.close();
			get_most_recent_order_id.close();
		    
			sqlSentence = "SELECT orders.*, customer.*, " +
			     "  cc_xacts.cx_type, " +
			     "  ship.addr_street1 AS ship_addr_street1, " +
			     "  ship.addr_street2 AS ship_addr_street2, " +
			     "  ship.addr_state AS ship_addr_state, " +
			     "  ship.addr_zip AS ship_addr_zip, " +
			     "  ship_co.co_name AS ship_co_name, " +
			     "  bill.addr_street1 AS bill_addr_street1, " +
			     "  bill.addr_street2 AS bill_addr_street2, " +
			     "  bill.addr_state AS bill_addr_state, " +
			     "  bill.addr_zip AS bill_addr_zip, " +
			     "  bill_co.co_name AS bill_co_name " +
			     "FROM customer, orders, cc_xacts," +
			     "  address AS ship, " +
			     "  country AS ship_co, " +
			     "  address AS bill,  " + 
			     "  country AS bill_co " +
			     "WHERE orders.o_id = " + order_id +
			     "  AND cx_o_id = orders.o_id " +
			     "  AND customer.c_id = orders.o_c_id " +
			     "  AND orders.o_bill_addr_id = bill.addr_id " +
			     "  AND bill.addr_co_id = bill_co.co_id " +
			     "  AND orders.o_ship_addr_id = ship.addr_id " +
			     "  AND ship.addr_co_id = ship_co.co_id " +
			     "  AND orders.o_c_id = customer.c_id";
		    
			// *** Get the order info for this o_id
			PreparedStatement get_order = con.prepareStatement
			    ("SELECT orders.*, customer.*, " +
			     "  cc_xacts.cx_type, " +
			     "  ship.addr_street1 AS ship_addr_street1, " +
			     "  ship.addr_street2 AS ship_addr_street2, " +
			     "  ship.addr_state AS ship_addr_state, " +
			     "  ship.addr_zip AS ship_addr_zip, " +
			     "  ship_co.co_name AS ship_co_name, " +
			     "  bill.addr_street1 AS bill_addr_street1, " +
			     "  bill.addr_street2 AS bill_addr_street2, " +
			     "  bill.addr_state AS bill_addr_state, " +
			     "  bill.addr_zip AS bill_addr_zip, " +
			     "  bill_co.co_name AS bill_co_name " +
			     "FROM customer, orders, cc_xacts," +
			     "  address AS ship, " +
			     "  country AS ship_co, " +
			     "  address AS bill,  " + 
			     "  country AS bill_co " +
			     "WHERE orders.o_id = ? " +
			     "  AND cx_o_id = orders.o_id " +
			     "  AND customer.c_id = orders.o_c_id " +
			     "  AND orders.o_bill_addr_id = bill.addr_id " +
			     "  AND bill.addr_co_id = bill_co.co_id " +
			     "  AND orders.o_ship_addr_id = ship.addr_id " +
			     "  AND ship.addr_co_id = ship_co.co_id " +
			     "  AND orders.o_c_id = customer.c_id");
			
			// Set parameter
			get_order.setLong(1, order_id);
			ResultSet rs2 = get_order.executeQuery();
			
			// Results
			if (!rs2.next()) {
			    // FIXME - This case is due to an error due to a database population error
			    con.commit();
			    rs2.close();
			    //		    get_order.close();
			    ConnectionsPool.getInstance().returnConnection(con);
			    return null;
			}
			order = new Order(rs2);
			rs2.close();
			get_order.close();
		    
			// *** Get the order_lines for this o_id
			sqlSentence = "SELECT * " + 
			     "FROM order_line, item " + 
			     "WHERE ol_o_id = " + order_id +
			     "AND ol_i_id = i_id";
			PreparedStatement get_order_lines = con.prepareStatement
			    ("SELECT * " + 
			     "FROM order_line, item " + 
			     "WHERE ol_o_id = ? " +
			     "AND ol_i_id = i_id");
			
			// Set parameter
			get_order_lines.setLong(1, order_id);
			ResultSet rs3 = get_order_lines.executeQuery();
			
			// Results
			while(rs3.next())
			    order_lines.addElement(new OrderLine(rs3));
			rs3.close();
			get_order_lines.close();
		    
	  
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		    return order;
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
			ex.printStackTrace();
		}
		return null;
    }

    // ********************** Shopping Cart code below ************************* 

    // Called from: TPCW_shopping_cart_interaction 
    public static long createEmptyCart(HttpSession session){
		
		String sqlSentence = null;
		long shopping_id = 0;
		
		try {
			Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    
			sqlSentence = "SELECT nextval('sc_id_seq')";
		    PreparedStatement get_next_id = con.prepareStatement(sqlSentence);
			ResultSet rs = get_next_id.executeQuery();
			rs.next();
			shopping_id = rs.getLong(1);
			rs.close();

			sqlSentence = "INSERT into shopping_cart (sc_id, sc_time) VALUES (" + shopping_id + ", timestamp 'now')";
			PreparedStatement insert_cart = con.prepareStatement("INSERT into shopping_cart (sc_id, sc_time) " + 
																 "VALUES (?," + "timestamp 'now')");
			insert_cart.setLong(1, shopping_id);
			insert_cart.executeUpdate();
			get_next_id.close();
			con.commit();
			
			ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
			ex.printStackTrace();
		}
		
		return shopping_id;
    }
    
    public static Cart doCart(HttpSession session, long SHOPPING_ID, Long I_ID, Vector ids, Vector quantities) {	
		Cart cart = null;
		String sqlSentence = "";
		try {
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    
		    if (I_ID != null)
		    	sqlSentence += addItem(con, SHOPPING_ID, I_ID.intValue()); 

		    sqlSentence += refreshCart(con, SHOPPING_ID, ids, quantities);
		    sqlSentence += addRandomItemToCartIfNecessary(con, SHOPPING_ID);
		    sqlSentence += resetCartTime(con, SHOPPING_ID);
		    cart = TPCW_Database.getCart(con, SHOPPING_ID, 0.0);
		    
		    // Close connection
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			System.err.println("EXECUTED SENTENCE:\n"+sqlSentence);
		    ex.printStackTrace();
		}
		return cart;
    }

    //This function finds the shopping cart item associated with SHOPPING_ID
    //and I_ID. If the item does not already exist, we create one with QTY=1,
    //otherwise we increment the quantity.

    private static String addItem(Connection con, long shopping_id, long i_id){
    	String sqlSentence = null;
		try {
			
			sqlSentence = "SELECT scl_qty FROM shopping_cart_line WHERE scl_sc_id = " + shopping_id + " AND scl_i_id = " + i_id + ";\n";
		    PreparedStatement find_entry = con.prepareStatement("SELECT scl_qty FROM shopping_cart_line WHERE scl_sc_id = ? AND scl_i_id = ?");
		    find_entry.setLong(1, shopping_id);
		    find_entry.setLong(2, i_id);
		    ResultSet rs = find_entry.executeQuery();
		    
		    // Results
		    if(rs.next()) {
				// The shopping cart id, item pair were already in the table, just incrementing the amount
		    	// of units of that item 
				int currqty = rs.getInt("scl_qty");
				currqty+=1;
				sqlSentence = "UPDATE shopping_cart_line SET scl_qty = " + currqty + " WHERE scl_sc_id = " + shopping_id + " AND scl_i_id = " + i_id + ";\n";
				PreparedStatement update_qty = con.prepareStatement
				("UPDATE shopping_cart_line SET scl_qty = ? WHERE scl_sc_id = ? AND scl_i_id = ?");
				update_qty.setInt(1, currqty);
				update_qty.setLong(2, shopping_id);
				update_qty.setLong(3, i_id);
				update_qty.executeUpdate();
				update_qty.close();
		    } else {//We need to add a new row to the table.
			
				// Stick the item info in a new shopping_cart_line
		    	// Get the next scl_id
			    sqlSentence = "SELECT nextval('scl_id_seq')";
		    	PreparedStatement get_next_id = con.prepareStatement("SELECT nextval('scl_id_seq')");
		    	ResultSet rs_scl_id = get_next_id.executeQuery();
		    	rs_scl_id.next();
		    	long SCL_ID = rs_scl_id.getLong(1);
		    	rs_scl_id.close();		    			    	
			    	
				sqlSentence = "INSERT into shopping_cart_line (scl_id, scl_sc_id, scl_qty, scl_i_id) VALUES ("+SCL_ID+","+shopping_id+",1,"+i_id+")";
				PreparedStatement put_line = con.prepareStatement
				    //("INSERT into shopping_cart_line (scl_sc_id, scl_qty, scl_i_id) VALUES (?,?,?)");
					("INSERT into shopping_cart_line (scl_id, scl_sc_id, scl_qty, scl_i_id) VALUES (?,?,?,?)");
				put_line.setLong(1, SCL_ID);
				put_line.setLong(2, shopping_id);
				put_line.setInt(3, 1);
				put_line.setLong(4, i_id);
				put_line.executeUpdate();
				put_line.close();
		    }
		    rs.close();
		    find_entry.close();
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
			ex.printStackTrace();
		}
		
		return sqlSentence;
    }

    private static String refreshCart(Connection con, long SHOPPING_ID, Vector ids, 
				    Vector quantities){
		int i;
		String sqlSentence = null;
		try {
		    for(i = 0; i < ids.size(); i++){
				String I_IDstr = (String) ids.elementAt(i);
				String QTYstr = (String) quantities.elementAt(i);
				long I_ID = Long.parseLong(I_IDstr);
				int QTY = Integer.parseInt(QTYstr);
				
				if(QTY == 0) { // We need to remove the item from the cart
					sqlSentence = "DELETE FROM shopping_cart_line WHERE scl_sc_id = " + SHOPPING_ID + " AND scl_i_id = " + I_ID + ";\n";
				    PreparedStatement statement = con.prepareStatement
					("DELETE FROM shopping_cart_line WHERE scl_sc_id = ? AND scl_i_id = ?");
				    statement.setLong(1, SHOPPING_ID);
				    statement.setLong(2, I_ID);
				    statement.executeUpdate();
				    statement.close();
		   		} else { //we update the quantity
					sqlSentence += "UPDATE shopping_cart_line SET scl_qty = " + QTY + " WHERE scl_sc_id = " + SHOPPING_ID + " AND scl_i_id = " + I_ID + "\n;";
				    PreparedStatement statement = con.prepareStatement
					("UPDATE shopping_cart_line SET scl_qty = ? WHERE scl_sc_id = ? AND scl_i_id = ?");
				    statement.setInt(1, QTY);
				    statement.setLong(2, SHOPPING_ID);
				    statement.setLong(3, I_ID);
				    statement.executeUpdate(); 
				    statement.close();
		   		}
		    }
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
		    ex.printStackTrace();
		}
		
		return sqlSentence;
    }

    private static String addRandomItemToCartIfNecessary(Connection con, long SHOPPING_ID){
		// check and see if the cart is empty. If it's not, we do
		// nothing.
		long related_item = 0;
		String sqlSentence = null;
		try {
		    // Check to see if the cart is empty
			sqlSentence = "SELECT COUNT(*) FROM shopping_cart_line WHERE scl_sc_id = " + SHOPPING_ID;
		    PreparedStatement get_cart = con.prepareStatement("SELECT COUNT(*) FROM shopping_cart_line WHERE scl_sc_id = ?");
		    get_cart.setLong(1, SHOPPING_ID);
		    ResultSet rs = get_cart.executeQuery();
		    rs.next();
		    if (rs.getInt(1) == 0) {
				// Cart is empty
				long rand_id = TPCW_Util.getRandomI_ID();
				related_item = getRelated1(rand_id,con);
				addItem(con, SHOPPING_ID, related_item);
		    }
		    
		    rs.close();
		    get_cart.close();
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
	        ex.printStackTrace();
		    System.err.println("Adding entry to shopping cart failed: shopping id = " + SHOPPING_ID + " related_item = " + related_item);
		}
		
		return sqlSentence;
    }


    //This function gets the value of I_RELATED1 for the row of
    //the item table corresponding to I_ID
    private static long getRelated1(long I_ID, Connection con){
		long related1 = -1;
		try {
		    PreparedStatement statement = con.prepareStatement("SELECT i_related1 FROM item WHERE i_id = ?");
		    statement.setLong(1, I_ID);
		    ResultSet rs = statement.executeQuery();
		    rs.next();
		    related1 = rs.getLong(1);//Is 1 the correct index?
		    rs.close();
		    statement.close();
		    
		}catch (java.lang.Exception ex) {
			System.err.println("Error when running SELECT i_related1 FROM item WHERE i_id = " + I_ID);
			ex.printStackTrace();
		}
		return related1;
    }

    // Only called from this class 
    private static String resetCartTime(Connection con, long SHOPPING_ID){
		String sqlSentence = null;
		try {
			sqlSentence = "UPDATE shopping_cart SET sc_time = timestamp 'now' WHERE sc_id = " + SHOPPING_ID + ";\n";
		    PreparedStatement statement = con.prepareStatement("UPDATE shopping_cart SET sc_time = timestamp 'now' WHERE sc_id = ?");
		
		    // Set parameter
		    statement.setLong(1, SHOPPING_ID);
		    statement.executeUpdate();
		    statement.close();
		} catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
	        ex.printStackTrace();
		}
		
		return sqlSentence;
    }

    public static Cart getCart(HttpSession session, long SHOPPING_ID, double c_discount) {
		Cart mycart = null;
		try {
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    mycart = getCart(con, SHOPPING_ID, c_discount);
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (Throwable throwable) {
			System.err.println("[" + Thread.currentThread().getName() + "]: Unexpected error, Throwable instance caught: '" + throwable.getMessage() + "'");
			System.err.println("[" + Thread.currentThread().getName() + "]: Stack");
			throwable.printStackTrace(System.err);
		}
		return mycart;
    }

    //time .05s
    private static Cart getCart(Connection con, long SHOPPING_ID, double c_discount){
		Cart mycart = null;
		String sqlSentence = null;
		try {
			sqlSentence = "SELECT * " + 
			 	"FROM shopping_cart_line, item " + 
			 	"WHERE scl_i_id = item.i_id AND scl_sc_id = " + SHOPPING_ID + ";";
		    PreparedStatement get_cart = con.prepareStatement("SELECT * " + 
				"FROM shopping_cart_line, item " + 
			 	"WHERE scl_i_id = item.i_id AND scl_sc_id = ?");
		    get_cart.setLong(1, SHOPPING_ID);
		    ResultSet rs = get_cart.executeQuery();
		    mycart = new Cart(rs, c_discount);
		    rs.close();
		    get_cart.close();
		}catch (java.lang.Exception ex) {
			System.err.println("Error when running " + sqlSentence);
	        ex.printStackTrace();
		}
		return mycart;
    }

    // ************** Customer / Order code below ************************* 

    //This should probably return an error code if the customer
    //doesn't exist, but ...
    public static void refreshSession(HttpSession session, long C_ID) {
    	String sqlSentence = null;
		try {
		    // Prepare SQL
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    sqlSentence = "UPDATE customer SET c_login = timestamp 'now', c_expiration = timestamp 'now' + interval '2 hours' WHERE c_id = " + C_ID;
			PreparedStatement updateLogin = con.prepareStatement
			("UPDATE customer SET c_login = timestamp 'now', c_expiration = timestamp 'now' + interval '2 hours' WHERE c_id = ?");
		    
		    // Set parameter
		    updateLogin.setLong(1, C_ID);
		    updateLogin.executeUpdate();
		    
		    con.commit();
		    updateLogin.close();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
		    //ex.printStackTrace();
			System.err.println("[" + Thread.currentThread().getName() + "]: " + ex.getMessage());
			System.err.println("[" + Thread.currentThread().getName() + "]: Stack");
			ex.printStackTrace(System.err);
		}
    }    

    public static Customer createNewCustomer(HttpSession session, Customer cust) {
    	
		try {
		    // Get largest customer ID already in use.
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    
		    cust.c_discount = (int) (java.lang.Math.random() * 51);
		    cust.c_balance = 0.0;
		    cust.c_ytd_pmt = 0.0;
		    // FIXME - Use SQL CURRENT_TIME to do this
		    cust.c_last_visit = new Date(System.currentTimeMillis());
		    cust.c_since = new Date(System.currentTimeMillis());
		    cust.c_login = new Date(System.currentTimeMillis());
		    cust.c_expiration = new Date(System.currentTimeMillis() + 
						 7200000);//milliseconds in 2 hours
		    PreparedStatement insert_customer_row = con.prepareStatement(
		    		"INSERT into customer (c_id, c_uname, c_passwd, c_fname, c_lname, c_addr_id, c_phone, c_email, c_since, c_last_login, c_login, c_expiration, c_discount, c_balance, c_ytd_pmt, c_birthdate, c_data) " +
		    		"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
		    insert_customer_row.setString(4,cust.c_fname);
		    insert_customer_row.setString(5,cust.c_lname);
		    insert_customer_row.setString(7,cust.c_phone);
		    insert_customer_row.setString(8,cust.c_email);
		    insert_customer_row.setDate(9, new java.sql.Date(cust.c_since.getTime()));
		    insert_customer_row.setDate(10, new java.sql.Date(cust.c_last_visit.getTime()));
		    insert_customer_row.setDate(11, new java.sql.Date(cust.c_login.getTime()));
		    insert_customer_row.setDate(12, new java.sql.Date(cust.c_expiration.getTime()));
		    insert_customer_row.setDouble(13, cust.c_discount);
		    insert_customer_row.setDouble(14, cust.c_balance);
		    insert_customer_row.setDouble(15, cust.c_ytd_pmt);
		    insert_customer_row.setDate(16, new java.sql.Date(cust.c_birthdate.getTime()));
		    insert_customer_row.setString(17, cust.c_data);
	
		    // Customer address
		    cust.addr_id = enterAddress(con, 
						cust.addr_street1, 
						cust.addr_street2,
						cust.addr_city,
						cust.addr_state,
						cust.addr_zip,
						cust.co_name);
			insert_customer_row.setLong(6, cust.addr_id);
		    
			// Customer id, name and passw
		    PreparedStatement get_max_id = con.prepareStatement("SELECT nextval('c_id_seq')");
		    ResultSet rs = get_max_id.executeQuery();
			rs.next();
			cust.c_id = rs.getLong(1); // Is 1 the correct index?
			rs.close();
			cust.c_id += 1;
			cust.c_uname = TPCW_Util.DigSyl(cust.c_id); 
			cust.c_passwd = cust.c_uname.toLowerCase();
	
			insert_customer_row.setLong(1, cust.c_id);
			insert_customer_row.setString(2,cust.c_uname);
			insert_customer_row.setString(3,cust.c_passwd);
			
			insert_customer_row.executeUpdate();
		    get_max_id.close();
			insert_customer_row.close();
			con.commit();
			//System.out.println("[LRM] Created Customer " + cust.c_id + ", jsession " + session.getId() + " in " + con.getMetaData().getURL());
			ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
			ex.printStackTrace();
		}
		return cust;
    }

    //BUY CONFIRM 

    public static BuyConfirmResult doBuyConfirm(HttpSession session, long shopping_id,
						long customer_id,
						String cc_type,
						long cc_number,
						String cc_name,
						Date cc_expiry,
						String shipping) {
	
	BuyConfirmResult result = new BuyConfirmResult();
	try {
	    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
	    //System.out.println("[LRM] Confirming buy of customer " + customer_id + ", jsession " + session.getId() + " in " + con.getMetaData().getURL());
		double c_discount = getCDiscount(con, customer_id);
	    result.cart = getCart(con, shopping_id, c_discount);
	    long ship_addr_id = getCAddr(con, customer_id);
	    result.order_id = enterOrder(con, customer_id, result.cart, ship_addr_id, shipping, c_discount);
	    enterCCXact(con, result.order_id, cc_type, cc_number, cc_name, cc_expiry, result.cart.SC_TOTAL, ship_addr_id);
	    clearCart(con, shopping_id);
	    con.commit();
	    ConnectionsPool.getInstance().returnConnection(con);
	} catch (java.lang.Exception ex) {
	    ex.printStackTrace();
	}
	return result;
    }
    
    public static BuyConfirmResult doBuyConfirm(HttpSession session, long shopping_id,
				    long customer_id,
				    String cc_type,
				    long cc_number,
				    String cc_name,
				    Date cc_expiry,
				    String shipping,
				    String street_1, String street_2,
				    String city, String state,
				    String zip, String country) {	
	
		BuyConfirmResult result = new BuyConfirmResult();
		try {
		    Connection con = ConnectionsPool.getInstance().getConnection(session.getId());
		    //System.out.println("[LRM] Confirming buy of customer " + customer_id + ", jsession " + session.getId() + " in " + con.getMetaData().getURL());
			double c_discount = getCDiscount(con, customer_id);
		    result.cart = getCart(con, shopping_id, c_discount);
		    long ship_addr_id = enterAddress(con, street_1, street_2, city, state, zip, country);
		    result.order_id = enterOrder(con, customer_id, result.cart, ship_addr_id, shipping, c_discount);
		    enterCCXact(con, result.order_id, cc_type, cc_number, cc_name, cc_expiry, result.cart.SC_TOTAL, ship_addr_id);
		    clearCart(con, shopping_id);
		    con.commit();
		    ConnectionsPool.getInstance().returnConnection(con);
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
		
		return result;
    }


    //DB query time: .05s
    public static double getCDiscount(Connection con, long c_id) {
		double c_discount = 0.0;
		try {
		    // Prepare SQL
		    PreparedStatement statement = con.prepareStatement
			("SELECT c_discount FROM customer WHERE customer.c_id = ?");
		    
		    // Set parameter
		    statement.setLong(1, c_id);
		    ResultSet rs = statement.executeQuery();
		    
		    // Results
		    rs.next();
		    c_discount = rs.getDouble(1);
		    rs.close();
		    statement.close();
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
		return c_discount;
    }

    //DB time: .05s
    public static long getCAddrID(Connection con, long c_id) {
	    long c_addr_id = 0;
		try {
		    // Prepare SQL
		    PreparedStatement statement = con.prepareStatement
			("SELECT c_addr_id FROM customer WHERE customer.c_id = ?");
		    
		    // Set parameter
		    statement.setLong(1, c_id);
		    ResultSet rs = statement.executeQuery();
		    
		    // Results
		    rs.next();
		    c_addr_id = rs.getLong(1);
		    rs.close();
		    statement.close();
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
		return c_addr_id;
    }

    public static long getCAddr(Connection con, long c_id) {
		long c_addr_id = 0;
		try {
		    // Prepare SQL
		    PreparedStatement statement = con.prepareStatement
			("SELECT c_addr_id FROM customer WHERE customer.c_id = ?");
		    
		    // Set parameter
		    statement.setLong(1, c_id);
		    ResultSet rs = statement.executeQuery();
		    
		    // Results
		    rs.next();
		    c_addr_id = rs.getLong(1);
		    rs.close();
		    statement.close();
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
		return c_addr_id;
    }

    public static void enterCCXact(Connection con,
				   long o_id,        // Order id
				   String cc_type,
				   long cc_number,
				   String cc_name,
				   Date cc_expiry,
				   double total,   // Total from shopping cart
				   long ship_addr_id) {

		// Updates the CC_XACTS table
		if(cc_type.length() > 10)
		    cc_type = cc_type.substring(0,10);
		if(cc_name.length() > 30)
		    cc_name = cc_name.substring(0,30);
		
		try {
		    // Prepare SQL
		    PreparedStatement statement = con.prepareStatement(
		    		"INSERT into cc_xacts (cx_o_id, cx_type, cx_num, cx_name, cx_expiry, cx_auth_id, cx_xact_amt, cx_xact_date, cx_co_id) "+
		    		"VALUES (?, ?, ?, ?, ?, '0',?, date('now'), ?)");
			//("INSERT into cc_xacts (cx_o_id, cx_type, cx_num, cx_name, cx_expiry, cx_xact_amt, cx_xact_date, cx_co_id) " + 
			// "VALUES (?, ?, ?, ?, ?, ?, date('now'), (SELECT co_id FROM address, country WHERE addr_id = ? AND addr_co_id = co_id))");
		    
		    // Set parameter
		    statement.setLong(1, o_id);           // cx_o_id
		    statement.setString(2, cc_type);     // cx_type
		    // Modified by Luis Rodero folliwng instructions to make TPC-W to run with PosgreSQL and Tomcat
		    //statement.setLong(3, cc_number);     // cx_num
		    statement.setString(3, Long.toString(cc_number));     // cx_num
		    statement.setString(4, cc_name);     // cx_name
		    statement.setDate(5, cc_expiry);     // cx_expiry
		    statement.setDouble(6, total);       // cx_xact_amount
		    statement.setLong(7, ship_addr_id);   // ship_addr_id
		    statement.executeUpdate();
		    statement.close();
		    con.commit();
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
    }
    
    public static void clearCart(Connection con, long shopping_id) {
		// Empties all the lines from the shopping_cart_line for the
		// shopping id.  Does not remove the actually shopping cart
		try {
		    // Prepare SQL
		    PreparedStatement statement = con.prepareStatement
			("DELETE FROM shopping_cart_line WHERE scl_sc_id = ?");
		    
		    // Set parameter
		    statement.setLong(1, shopping_id);
		    statement.executeUpdate();
		    statement.close();
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
    }

    public static long enterAddress(Connection con,  // Do we need to do this as part of a transaction?
				   String street1, String street2,
				   String city, String state,
				   String zip, String country) {
    	
		// Returns the address id of the specified address.  Adds a
		// new address to the table if needed
		long addr_id = 0;
		
        try {
			
        	// First getting the country id
		    PreparedStatement get_co_id = con.prepareStatement("SELECT co_id FROM country WHERE co_name = ?");
		    get_co_id.setString(1, country);
		    ResultSet rs = get_co_id.executeQuery();
		    rs.next();
		    long addr_co_id = rs.getLong("co_id");
		    rs.close();
		    get_co_id.close();
		    
		    // Get address id for this customer
		    PreparedStatement match_address = con.prepareStatement("SELECT addr_id FROM address " + 
																	 "WHERE addr_street1 = ? " +
																	 "AND addr_street2 = ? " + 
																	 "AND addr_city = ? " + 
																	 "AND addr_state = ? " + 
																	 "AND addr_zip = ? " + 
																	 "AND addr_co_id = ?");
		    match_address.setString(1, street1);
		    match_address.setString(2, street2);
		    match_address.setString(3, city);
		    match_address.setString(4, state);
		    match_address.setString(5, zip);
		    match_address.setLong(6, addr_co_id);
		    rs = match_address.executeQuery();
		    
		    if(!rs.next()){ // We didn't match an address in the addr table, creating a new one
		    	
		    	// Next address id
		    	PreparedStatement get_max_addr_id = con.prepareStatement("SELECT nextval('addr_id_seq')");
		    	ResultSet nextAddrIdResultSet = get_max_addr_id.executeQuery();
		    	nextAddrIdResultSet.next();
				addr_id = nextAddrIdResultSet.getLong(1);
				nextAddrIdResultSet.close();
				
				PreparedStatement insert_address_row = con.prepareStatement("INSERT into address (addr_id, addr_street1, addr_street2, addr_city, addr_state, addr_zip, addr_co_id) " + 
				     														"VALUES (?, ?, ?, ?, ?, ?, ?)");
				insert_address_row.setLong(1, addr_id);
				insert_address_row.setString(2, street1);
				insert_address_row.setString(3, street2);
				insert_address_row.setString(4, city);
				insert_address_row.setString(5, state);
				insert_address_row.setString(6, zip);
				insert_address_row.setLong(7, addr_co_id);
		
				insert_address_row.executeUpdate();
				insert_address_row.close();
		    } else // We actually matched
		    	addr_id = rs.getLong("addr_id");
		    
		    match_address.close();
		    rs.close();
		} catch (java.lang.Exception ex) {
			System.err.println("[Thread " + Thread.currentThread().getName() + "] Exception caught: " + ex.getMessage());
		    ex.printStackTrace();
		}
		
		return addr_id;
    }

 
    public static long enterOrder(Connection con, long customer_id, Cart cart, long ship_addr_id, String shipping, double c_discount) {
    	
    	// Creates an entry in the 'orders' table 
    	long o_id = 0;
		try {
			PreparedStatement insert_row = con.prepareStatement("INSERT into orders (o_id, o_c_id, o_date, o_sub_total, " + 
		    													"o_tax, o_total, o_ship_type, o_ship_date, " + 
		    													"o_bill_addr_id, o_ship_addr_id, o_status) " + 
			 													"VALUES (?, ?, date('now'), ?, 8.25, ?, ?, date('now') + ?, ?, ?, 'Pending')");
		    insert_row.setLong(2, customer_id);
		    insert_row.setDouble(3, cart.SC_SUB_TOTAL);
		    insert_row.setDouble(4, cart.SC_TOTAL);
		    insert_row.setString(5, shipping);
		    insert_row.setInt(6, TPCW_Util.getRandom(7));
		    insert_row.setLong(7, getCAddrID(con, customer_id));
		    insert_row.setLong(8, ship_addr_id);
	
		    PreparedStatement get_max_id = con.prepareStatement("SELECT nextval('o_id_seq')");
		    ResultSet rs = get_max_id.executeQuery();
			rs.next();
			o_id = rs.getLong(1);
			rs.close();
		    get_max_id.close();
			 
		    insert_row.setLong(1, o_id);
			insert_row.executeUpdate();
		    insert_row.close();
		    con.commit();
		} catch (java.lang.Exception ex) {
			ex.printStackTrace();
		}

		Enumeration e = cart.lines.elements();
		int counter = 0;
		while(e.hasMoreElements()) {
		    // - Creates one or more 'order_line' rows.
		    CartLine cart_line = (CartLine) e.nextElement();
		    addOrderLine(con, counter, o_id, cart_line.scl_i_id, 
		    				cart_line.scl_qty, c_discount, 
		    				TPCW_Util.getRandomString(20, 100));
		    counter++;
	
		    // - Adjusts the stock for each item ordered
		    int stock = getStock(con, cart_line.scl_i_id);
		    if ((stock - cart_line.scl_qty) < 10) {
		    	setStock(con, cart_line.scl_i_id, stock - cart_line.scl_qty + 21);
		    } else {
		    	setStock(con, cart_line.scl_i_id, stock - cart_line.scl_qty);
		    }
		}
		
		return o_id;
    }
    
    public static void addOrderLine(Connection con, 
				    long ol_id, long ol_o_id, long ol_i_id, 
				    int ol_qty, double ol_discount, String ol_comment) {
		
    	try {
    		
    		// Next order_line id (ol_id from the parameters list is ignored)
    		PreparedStatement get_next_id = con.prepareStatement("SELECT nextval('ol_id_seq')");
    		ResultSet rs_ol_id = get_next_id.executeQuery();
    		rs_ol_id.next();
    		long real_new_ol_id = rs_ol_id.getLong(1);
    		rs_ol_id.close();
    		
		    PreparedStatement insert_row = con.prepareStatement
			("INSERT into order_line (ol_id, ol_o_id, ol_i_id, ol_qty, ol_discount, ol_comments) " + 
			 "VALUES (?, ?, ?, ?, ?, ?)");
		    
		    insert_row.setLong(1, real_new_ol_id);
		    insert_row.setLong(2, ol_o_id);
		    insert_row.setLong(3, ol_i_id);
		    insert_row.setInt(4, ol_qty);
		    insert_row.setDouble(5, ol_discount);
		    insert_row.setString(6, ol_comment);
		    insert_row.executeUpdate();
		    insert_row.close();
		    
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
		
    }

    public static int getStock(Connection con, long i_id) {
		int stock = 0;
		try {
		    PreparedStatement get_stock = con.prepareStatement("SELECT i_stock FROM item WHERE i_id = ?");
		    
		    // Set parameter
		    get_stock.setLong(1, i_id);
		    ResultSet rs = get_stock.executeQuery();
		    
		    // Results
		    rs.next();
		    stock = rs.getInt("i_stock");
		    rs.close();
		    get_stock.close();
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
		return stock;
    }

    public static void setStock(Connection con, long i_id, int new_stock) {
		try {
		    PreparedStatement update_row = con.prepareStatement
			("UPDATE item SET i_stock = ? WHERE i_id = ?");
		    update_row.setInt(1, new_stock);
		    update_row.setLong(2, i_id);
		    update_row.executeUpdate();
		    update_row.close();
		} catch (java.lang.Exception ex) {
		    ex.printStackTrace();
		}
    }

    /*public static void verifyDBConsistency(){
	try {
	    Connection con = ConnectionsPool.getInstance().getConnection();
	    int this_id;
	    int id_expected = 1;
	    //First verify customer table
	    PreparedStatement get_ids = con.prepareStatement
		("SELECT c_id FROM customer");
	    ResultSet rs = get_ids.executeQuery();
	    while(rs.next()){
	        this_id = rs.getInt("c_id");
		while(this_id != id_expected){
		    System.out.println("Missing C_ID " + id_expected);
		    id_expected++;
		}
		id_expected++;
	    }
	    
	    id_expected = 1;
	    //Verify the item table
	    get_ids = con.prepareStatement
		("SELECT i_id FROM item");
	    rs = get_ids.executeQuery();
	    while(rs.next()){
	        this_id = rs.getInt("i_id");
		while(this_id != id_expected){
		    System.out.println("Missing I_ID " + id_expected);
		    id_expected++;
		}
		id_expected++;
	    }

	    id_expected = 1;
	    //Verify the address table
	    get_ids = con.prepareStatement
		("SELECT addr_id FROM address");
	    rs = get_ids.executeQuery();
	    while(rs.next()){
	        this_id = rs.getInt("addr_id");
		//		System.out.println(this_cid+"\n");
		while(this_id != id_expected){
		    System.out.println("Missing ADDR_ID " + id_expected);
		    id_expected++;
		}
		id_expected++;
	    }
	    
	    

	    con.commit();
	    ConnectionsPool.getInstance().returnConnection(con);
	} catch (java.lang.Exception ex) {
	    ex.printStackTrace();
	}
    } */
}

