/*
 * User.java
 */
import java.sql.*;

/** Objects of the User class represent records in the
 * users table in the database.
 * @author Timothy Paul Fox
 * @version July 24, 2004, 4:30 PM
 */
public class User {
    
    private UserID userID;
    private String password;
    private java.sql.Date dateAdded;
    private java.sql.Date dateRetired;
    private long contactID;
    private Statement userStmt;
    private Connection dbWire;
    private ResultSet rowRS;
    private boolean isNewRow;
    
    {
        userID = null;
        password = null;
        dateAdded = null;
        dateRetired = null;
        contactID = 0;
        userStmt = null;
        dbWire = null;
        rowRS = null;
        isNewRow = false;
    }
    
    /** Creates a new User object and record
     * @param userType identifies the category of User being created: 
     * 'A' for Administrator, 'L' for Librarian, or 'P' for Patron.
     * @param passWord is required to be non-blank and non-null. It can 
     * be no longer than 12 characters. It must not match a password 
     * already in the users table.
     * @param contactId is the long integer value that is the primary key 
     * of a record in the contactinfo table. This 
     * record (created by instantiating a ContactInfo object) must exist 
     * before the new User object is created.
     * @throws Exception any problem that happens in creating the 
     * User object. (LAZY)
     */
    public User(char userType, String passWord, long contactId) 
                                                throws Exception {
        isNewRow = true;
        userID = new UserID(userType);
        if ((null == passWord) || (passWord.length() < 1)) 
            throw new IllegalArgumentException(
                                "User():Bad passWord arg");
        
        if (contactId < 1) 
            throw new IllegalArgumentException(
                                "User():illegal contactId value");
        try {
            ContactInfo idCheck = new ContactInfo(contactId);
        }
        catch (Exception e) {
            throw new IllegalArgumentException(
                  "User():could not confirm ContactInfo for contactID");
        }
        
        try {
            makeDbConnection("root");
        
            if (userPasswordAlreadyExists(passWord))
                throw new IllegalArgumentException(
                                    "User():passWord already exists");
            password = passWord;
            contactID = contactId;
            dateAdded = new java.sql.Date(System.currentTimeMillis());
            int result;
            String valueString; 
            valueString = "'" + getUserID() + "'," +
                          "'" + password + "'," +
                          "'" + dateAdded.toString() + "'," +
                          contactID ;

            result = putToRow(
                        "INSERT INTO users " +
                        "(user_id, password, date_added, contact_id) " +
                        "VALUES (" + valueString + ")");
            if (result != 1) 
                throw new SQLException("New user record not written");
        }
        finally {
            if (userStmt != null) userStmt.close();
            if (dbWire != null) dbWire.close();
        }
    }

    private boolean userPasswordAlreadyExists(String password) 
                                                throws Exception {
        getFromRow(
            "SELECT * from users " +
            "WHERE (password = '" + password +  "')");
        return rowRS.first(); // true if there is a matching row
    }
        
    /** Creates an object that is a copy of an existing 
     * user record.
     * Tries to find a record in the users table whose 
     * primary key matches knownID. If it finds the record, it instantiates 
     * a User object and reads the record's data into the object's properties.
     * @param knownID This String must start with one of the legal userType 
     * characters [ALP], not case sensitive. The rest of the string must 
     * parse as a long integer. If the ID does not match that of a record 
     * in the users table, the constructor will throw 
     * SQLException.
     * @throws Exception any problem that happens in creating the 
     * User object. (LAZY)
     */    
    public User(String knownID) throws Exception {
        userID = new UserID(knownID);
        try {
            makeDbConnection("root");
            getFromRow("SELECT * from users WHERE (user_id = '" +
                                        userID.toString() +  "')");
            rowRS.first();
            password = rowRS.getString("password");
            dateAdded = rowRS.getDate("date_added");
            dateRetired = rowRS.getDate("date_retired");
            contactID = rowRS.getInt("contact_id");
        }
        finally {
            if (userStmt != null) userStmt.close();
            if (dbWire != null) dbWire.close();
        }
    }
    
    private void getFromRow(String query) throws Exception {
        rowRS = userStmt.executeQuery(query);
    }
    
    private void makeDbConnection(String user) throws Exception {
        LibManDbConnection plug;
        
        if (dbWire != null) {
            dbWire.close();
        }
        plug = new LibManDbConnection(user);
        dbWire = plug.connect();
        userStmt = dbWire.createStatement();
    }
    
    private int putToRow(String updateSpec) throws Exception{
        return userStmt.executeUpdate(updateSpec);
    }
    
    /** describes the properties of the User object.
     * @return description of the User object
     *
     */    
    public String toString() {
        StringBuffer sb = new StringBuffer("User: ");
        sb.append(getUserID() + ":");
        sb.append(getPassword() + ":");
        sb.append("added>");
        sb.append((null == getDateAdded())? 
                            "Unknown" : getDateAdded().toString());
        sb.append((null == getDateRetired())? 
                            ":?" : ":retired>" +
                            getDateAdded().toString());
        sb.append(":" + getContactID());
        return sb.toString();
    } // end toString()
    
    /** gets userID from the User object. 
     * @return A ten character string that starts with an uppercase 
     * character indicating the userType, followed by a nine character
     * string of the object's numeric value, left-padded with zeros.
     */    
    public String getUserID() {
        return userID.toString();
    }
    
    /** gets the password string from the User object.
     * @return the password for this user
     */    
    public String getPassword() {
        return (null == password) ? "" : password;
    }
    
    /** gets the date the User record was added to the
     * users table.
     * @return a reference to a Date object
     */    
    public java.sql.Date getDateAdded() {
        return dateAdded;
    }
    
    /** gets the date the User record was retired. 
     * If the record has not been retired, returns null.
     * @return a reference to a Date object, or null
     */    
    public java.sql.Date getDateRetired() {
        return dateRetired;
    }
   
    /** gets the contactID
     * @return the long integer that is primary key of the contact
     * infomation record for this user
     */
    public long getContactID() {
        return contactID;
    }
    
    /** indicates whether the user record has been retired
     * @return true if the dateRetired property has 
     * been set, otherwise false
     */    
    public boolean isRetired() /* throws Exception */ {
        if (
            (!isNewRow) && (dateRetired != null) &&
            (dateAdded != null) && 
            (dateAdded.compareTo(dateRetired) < 0)
            )
            return true;
        else
            return false;
    }
    
    /** marks a user record as retired. The User object must be one
     * that was created by reading the users table.
     * @throws Exception any exception from accessing the database, plus 
     * IllegalArgumentException if retire() is called on a record that 
     * is already retired.
     */    
    public void retire() throws Exception {
        String valueString; 
        int result;
        if (isNewRow) 
            throw new IllegalArgumentException(
                "User:Cannot retire() a newly created record!");
        if (isRetired())
            throw new IllegalArgumentException(
                "User:Cannot retire() a retired record!");
            
        dateRetired = new java.sql.Date(System.currentTimeMillis());
        valueString = "'" + dateRetired.toString() + "'";
        try {
            makeDbConnection("root");
            result = putToRow("UPDATE users " +
                              "SET date_retired = " + valueString + 
                              "WHERE (user_id = '" + getUserID() + "')");
        }
        finally {
            if (userStmt != null) userStmt.close();
            if (dbWire != null) dbWire.close();
        }
    } // end retire()
    
    /** changes the password. The password must not duplicate one
     * already in the users table.
     * @param pswd is required to be non-blank and non-null. It can
     * be no longer than 12 characters. It must not match a password
     * already in the users table.
     * @throws Exception any exception from accessing the database,
     * plus IllegalArgumentException if pswd is not valid.
     */    
    public void changePassword(String pswd) throws Exception {
        String valueString; 
        int result;
        if ((null == pswd) || (pswd.length() < 1)) 
            throw new IllegalArgumentException(
                        "User:Bad arg to changePassword()");
        
        makeDbConnection("root");
        if (userPasswordAlreadyExists(pswd))
            throw new IllegalArgumentException(
                "User:changePassword(" + pswd + ") already exists");
        valueString = "'" + pswd + "'";
        result = putToRow("UPDATE users " +
                          "SET password =  " + valueString +
                          " WHERE (user_id = '" + getUserID() + "')");
        userStmt.close();
        dbWire.close();
    } // end changePassword()
    
} // end class User