package dbms;

import java.util.Vector;
import java.sql.*;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import javax.swing.*;
import javax.swing.table.*;
import connectfour.*;
import server.*;

public class SQLiteDBMS extends UnicastRemoteObject implements DBMS {
	
	private String m_databaseFileName;
	private Connection m_database;
	private Statement m_statement;
	private Vector<DBMSListener> m_listeners;
	private boolean m_initialized;
	final public static String DEFAULT_DATABASE_FILENAME = "users.sql"; 
	final public static String USER_DATA_TABLENAME = "UserData";
	final public static String GAME_HISTORY_TABLENAME = "GameHistory";
	private static final long serialVersionUID = 2928990798728092201L;
	
	public SQLiteDBMS() throws RemoteException {
		m_database = null;
		m_statement = null;
		m_listeners = new Vector<DBMSListener>();
		m_initialized = false;
	}
	
	// initialize the SQLite database management system
	public boolean initialize() {
		if(m_initialized) { return false; }
		
		// set the database file name
		m_databaseFileName = DEFAULT_DATABASE_FILENAME;
		
		DatabaseServer.console.writeLine("Connecting to SQL Database: " + m_databaseFileName);
		
		// initialize the driver and connect to the database
		try {
			DriverManager.registerDriver(new org.sqlite.JDBC());
			m_database = DriverManager.getConnection("jdbc:sqlite:" + m_databaseFileName);
			m_statement = m_database.createStatement();
			
			DatabaseServer.console.writeLine("Connected to SQL Database");
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Unable to initialize SQLite Database: " + e.getMessage());
			return false;
		}
		
		// create the tables (if they do not already exist)
		createTables();
		
		m_initialized = true;
		
		return true;
	}
	
	// create all tables
	public void createTables() {
		createUserDataTable();
		createGameHistoryTable();
	}
	
	// drop all tables
	public void dropTables() {
		dropUserDataTable();
		dropGameHistoryTable();		
	}
	
	// clear all tables
	public void clearTables() {
		dropTables();
		createTables();
	}
	
	// drop the user data table
	public void dropUserDataTable(){
		try{
			// drop the user data table
			m_statement.executeUpdate(
				"DROP TABLE IF EXISTS " + USER_DATA_TABLENAME
			);
			
			DatabaseServer.console.writeLine("Dropped table " + USER_DATA_TABLENAME);
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error dropping table " + USER_DATA_TABLENAME + " table: " + e.getMessage());
		}
	}
	
	// drop the game history table
	public void dropGameHistoryTable(){
		try{
			// drop the game history table
			m_statement.executeUpdate(
				"DROP TABLE IF EXISTS " + GAME_HISTORY_TABLENAME
			);
			
			DatabaseServer.console.writeLine("Dropped table " + GAME_HISTORY_TABLENAME);
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error dropping table " + GAME_HISTORY_TABLENAME + " table: " + e.getMessage());
		}	
	}
	
	// create the user data table
	public void createUserDataTable(){
		try {
			// check if the user data table already exists
			boolean tableExists = new SQLResult(m_statement.executeQuery(
				"SELECT name FROM sqlite_master " +
					"WHERE type = 'table' " +
					"AND name = '" + USER_DATA_TABLENAME + "'"
			)).getData().size() != 0;
			
			// if the user data table does not already exist, create it
			if(!tableExists) {
				m_statement.executeUpdate(
					"CREATE TABLE IF NOT EXISTS " + USER_DATA_TABLENAME + " (" +
						"UserName			VARCHAR(32)		NOT NULL," +
						"Password			VARCHAR(32)		NOT NULL," +
						"LastLogin			DATETIME		NOT NULL," +
						"JoinDate			DATETIME		NOT NULL," +
						"Wins				INT				NOT NULL," +
						"Losses				INT				NOT NULL," +
						"Draws				INT				NOT NULL," +
						"PRIMARY KEY(UserName)" +
					")"
				);
			}
			
			// if the user data table was created, display a message in the console indicating as such
			if(!tableExists) {
				DatabaseServer.console.writeLine("Created table " + USER_DATA_TABLENAME);
			}
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error creating table " + USER_DATA_TABLENAME + " table: " + e.getMessage());
		}
	}

	public void createGameHistoryTable(){
		try {
			// check if the game history table already exists
			boolean tableExists = new SQLResult(m_statement.executeQuery(
					"SELECT name FROM sqlite_master " +
						"WHERE type = 'table' " +
						"AND name = '" + GAME_HISTORY_TABLENAME + "'"
				)).getData().size() != 0;
				
			
			// if the game history table does not already exist, create it
			if(!tableExists) {
				m_statement.executeUpdate(
					"CREATE TABLE IF NOT EXISTS " + GAME_HISTORY_TABLENAME + " (" +
						"GameDate			DATETIME		NOT NULL," +
						"FirstPlayer		VARCHAR(32)		NOT NULL," +
						"SecondPlayer		VARCHAR(32)		NOT NULL," +
						"Outcome			TINYINT			NOT NULL," +
						"PRIMARY KEY(GameDate, FirstPlayer, SecondPlayer)," +
						"FOREIGN KEY(FirstPlayer) REFERENCES " + USER_DATA_TABLENAME + "(UserName)," +
						"FOREIGN KEY(SecondPlayer) REFERENCES " + USER_DATA_TABLENAME + "(UserName)" +
					")"
				);
			}
			
			// if the game history table was created, display a message in the console indicating as such
			if(!tableExists) {
				DatabaseServer.console.writeLine("Created table " + GAME_HISTORY_TABLENAME);
			}
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error creating table " + GAME_HISTORY_TABLENAME + " table: " + e.getMessage());
		}
	}
	
	// create a new user entry in the user data table
	public boolean createUser(String userName, String password) throws RemoteException {
		if(userName == null || password == null) { return false; }
		
		try {
			// verify that the user does not already exist
			if(new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + USER_DATA_TABLENAME + " " +
				"WHERE UserName = '" + userName + "' " 
			)).getRowCount() != 0) { return false; }
			
			// create the user
			boolean userCreated = m_statement.executeUpdate(
				"INSERT INTO " + USER_DATA_TABLENAME + " VALUES(" +
					"'" + userName + "', " +
					"'" + password + "', " +
					"CURRENT_TIMESTAMP, " +
					"CURRENT_TIMESTAMP, " +
					"'0'," +
					"'0'," +
					"'0'" + 
				")"
			) != 0;
			
			// if the user was created, display a message in the console indicating as such
			if(userCreated) {
				DatabaseServer.console.writeLine("Added user \"" + userName + "\" to database");
			}
			
			return userCreated;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error adding user \"" + userName + "\" to database: " + e.getMessage());
		}
		return false;
	}
	
	// get all unique user names from the user data table
	public String[] getUserNames() throws RemoteException {
		try {
			// get all of the users in the user data table
			SQLResult users = new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + USER_DATA_TABLENAME
			));
			
			// collect all of the user names
			String[] userNames = new String[users.getRowCount()];
			for(int i=0;i<users.getRowCount();i++) {
				userNames[i] = users.getElement(i, 0);
			}
			
			DatabaseServer.console.writeLine("Retrieved user name list from database");
			
			return userNames;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error user name list from database: " + e.getMessage());
		}
		
		return null;
	}
	
	// get all unique user names in the game history table
	public String[] getUserNamesFromHistory() throws RemoteException {
		try {
			// get all matches from the game history table
			SQLResult history = new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + GAME_HISTORY_TABLENAME
			));
			
			// iterate over all of the entries in the result
			String userName = null;
			boolean duplicateName = false;
			Vector<String> userNames = new Vector<String>();
			for(int i=0;i<history.getRowCount();i++) {
				// get the first user name, and add it to the collection of names if it is not a duplicate
				userName = history.getElement(i, 1).trim();
				duplicateName = false;
				for(int j=0;j<userNames.size();j++) {
					if(userName.equalsIgnoreCase(userNames.elementAt(j))) {
						duplicateName = true;
					}
				}
				if(!duplicateName) {
					userNames.add(userName);
				}
				
				// get the second user name, and add it to the collection of names if it is not a duplicate
				userName = history.getElement(i, 2).trim();
				duplicateName = false;
				for(int j=0;j<userNames.size();j++) {
					if(userName.equalsIgnoreCase(userNames.elementAt(j))) {
						duplicateName = true;
					}
				}
				if(!duplicateName) {
					userNames.add(userName);
				}
			}
			
			// create a string array of the user names
			String[] data = new String[userNames.size()];
			for(int i=0;i<userNames.size();i++) {
				data[i] = userNames.elementAt(i);
			}
			
			DatabaseServer.console.writeLine("Retrieved user name list from database");
			
			return data;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error user name list from database: " + e.getMessage());
		}
		
		return null;
	}
	
	// delete a user from the user data table
	public boolean deleteUser(String userName) throws RemoteException {
		if(userName == null) { return false; }
		
		boolean userDeleted = false;
		
		try {
			// delete the user
			userDeleted = m_statement.executeUpdate(
				"DELETE FROM " + USER_DATA_TABLENAME + " " +
					"WHERE UserName = '" + userName + "'"
			) != 0;
			
			// if the user was deleted, display a message indicating as such
			if(userDeleted) {
				DatabaseServer.console.writeLine("Deleted user \"" + userName + "\" from database");
			}
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error deleting \"" + userName + "\" from database: " + e.getMessage());
		}
		
		return userDeleted;
	}
	
	// delete all game history associated with the specified user
	public void deleteUserHistory(String userName) throws RemoteException {
		if(userName == null) { return; }
		
		try {
			// delete all history for the specified user
			m_statement.executeUpdate(
				"DELETE FROM " + GAME_HISTORY_TABLENAME + " " + 
					"WHERE " +
						"FirstPlayer = '" + userName + "'" +
					"OR " + 
						"SecondPlayer = '" + userName + "'"
			);
			
			DatabaseServer.console.writeLine("Deleted history for \"" + userName + "\" from database");
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error deleting history for \"" + userName + "\" from database: " + e.getMessage());
		}
	}
	
	// add a win for the specified user
	public boolean addWin(String userName) throws RemoteException {
		if(userName == null) { return false; }
		
		boolean updated = false;
		
		try {
			// increment the user's wins
			updated = m_statement.executeUpdate(
				"UPDATE " + USER_DATA_TABLENAME + 
				" SET Wins = Wins + 1 " +
				" WHERE UserName = '" + userName + "'" 
			) != 0;
			
			DatabaseServer.console.writeLine("Added win for \"" + userName + "\".");
			
			// notify any listeners that there has been a stats update for the specified user
			notifyStatsUpdate(userName);
		} 
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error adding win for \"" + userName + "\" to database: " + e.getMessage());
		}
		
		return updated;
	}
	
	// add a loss for the specified user
	public boolean addLoss(String userName) throws RemoteException {
		if(userName == null) { return false; }
		
		boolean updated = false;
		
		try {
			// increment the user's losses
			updated = m_statement.executeUpdate(
				"UPDATE " + USER_DATA_TABLENAME + 
				" SET Losses = Losses + 1 " +
				" WHERE UserName = '" + userName + "'" 
			) != 0;
			
			DatabaseServer.console.writeLine("Added loss for \"" + userName + "\".");
			
			// notify any listeners that there has been a stats update for the specified user
			notifyStatsUpdate(userName);
		} 
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error adding loss for \"" + userName + "\" to database: " + e.getMessage());
		}
		
		return updated;
	}
	
	// add a draw for the specified user
	public boolean addDraw(String userName) throws RemoteException {
		if(userName == null) { return false; }
		
		boolean updated = false;
		
		try {
			// increment the user's draws
			updated = m_statement.executeUpdate(
				"UPDATE " + USER_DATA_TABLENAME + 
				" SET Draws = Draws + 1 " +
				" WHERE UserName = '" + userName + "'" 
			) != 0;
			
			DatabaseServer.console.writeLine("Added draw for \"" + userName + "\".");
			
			// notify any listeners that there has been a stats update for the specified user
			notifyStatsUpdate(userName);
		} 
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error adding draw for \"" + userName + "\" to database: " + e.getMessage());
		}
		
		return updated;
	}
	
	// add a match to the game history table
	public boolean addMatch(MatchData match) throws RemoteException {
		if(match == null) { return false; }
		
		boolean matchAdded = false;
		
		try {
			// verify that the first player exists
			if(new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + USER_DATA_TABLENAME + " " +
				"WHERE UserName = '" + match.getFirstPlayerName() + "' " 
			)).getRowCount() == 0) {
				DatabaseServer.console.writeLine("Failed to add match for user \"" + match.getFirstPlayerName() + "\" without account");
				return false;
			}
			
			// verify that the first player exists
			if(new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + USER_DATA_TABLENAME + " " +
				"WHERE UserName = '" + match.getSecondPlayerName() + "' " 
			)).getRowCount() == 0) {
				DatabaseServer.console.writeLine("Failed to add match for user \"" + match.getSecondPlayerName() + "\" without account");
				return false;
			}
			
			// add the match to the game history table
			matchAdded = m_statement.executeUpdate(
				"INSERT INTO " + GAME_HISTORY_TABLENAME + " VALUES(" +
					"'" + match.getTimestampSQLString() + "', " +
					"'" + match.getFirstPlayerName() + "', " +
					"'" + match.getSecondPlayerName() + "', " +
					"'" + match.getOutcome().ordinal() + "'" +
				")"
			) != 0;
			
			// if the match was added to the game history table, display a message indicating as such
			if(matchAdded) {
				DatabaseServer.console.writeLine("Added match for \"" + match.getFirstPlayerName() + "\" vs. \"" + match.getSecondPlayerName() + "\" with outcome " + match.getOutcome().toString());
			}
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error adding match between \"" + match.getFirstPlayerName() + "\" vs. \"" + match.getSecondPlayerName() + "\": " + e.getMessage());
		}
		
		return matchAdded;
	}
	
	// verify a user's credentials and log them in
	public boolean userLogin(String userName, String password) throws RemoteException {
		if(userName == null || password == null) { return false; }
		
		boolean authenticated = false;
		
		try {
			// verify the user's credentials and update their last login to the current time
			authenticated = m_statement.executeUpdate(
				"UPDATE " + USER_DATA_TABLENAME + " " +
				"SET LastLogin = CURRENT_TIMESTAMP " +
				"WHERE UserName = '" + userName + "' " +
				"AND Password = '" + password + "'"
			) != 0;
			
			// if the user was authenticated
			if(authenticated) {
				// notify all listeners that the specified user has logged in
				for(int i=0;i<m_listeners.size();i++) {
					try {
						m_listeners.elementAt(i).userLogin(userName);
					}
					catch(RemoteException e) {
						DatabaseServer.console.writeLine("Removing database listener: " + e.getMessage());
						m_listeners.remove(i);
						i--;
					}
				}
				
				// display a message indicating that the user was logged in
				DatabaseServer.console.writeLine("User \"" + userName + "\" logged in");
			}
			// if the user was not authenticated
			else {
				// display a message indicating that the user failed to log in
				DatabaseServer.console.writeLine("User \"" + userName + "\" failed to login with valid credentials");
			}
			
			return authenticated;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error processing login request for user \"" + userName + "\": " + e.getMessage());
		}
		return false;
	}
	
	// log a user out
	public boolean userLogout(String userName) throws RemoteException {
		if(userName == null) { return false; }
		
		boolean loggedOut = false;
		
		try {
			// log the user out, and update their last login time
			loggedOut = m_statement.executeUpdate(
				"UPDATE " + USER_DATA_TABLENAME + " " +
				"SET LastLogin = CURRENT_TIMESTAMP " +
				"WHERE UserName = '" + userName + "'"
			) != 0;
			
			if(loggedOut) {
				// notify all listeners that the specified user has logged out
				for(int i=0;i<m_listeners.size();i++) {
					try {
						m_listeners.elementAt(i).userLogout(userName);
					}
					catch(RemoteException e) {
						DatabaseServer.console.writeLine("Removing database listener: " + e.getMessage());
						m_listeners.remove(i);
						i--;
					}
				}
				
				// display a message indicating that the user logged out
				DatabaseServer.console.writeLine("User \"" + userName + "\" logged out");
			}
			
			return true;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error processing logout request for user \"" + userName + "\": " + e.getMessage());
		}
		return false;
	}
	
	// allow a user to change their password
	public boolean changePassword(String userName, String oldPassword, String newPassword) throws RemoteException {
		if(userName == null || oldPassword == null || newPassword == null) { return false; }
		
		boolean passwordChanged = false;
		
		try {
			// change the user's password if their credentials are verified
			passwordChanged = m_statement.executeUpdate(
				"UPDATE " + USER_DATA_TABLENAME + " " +
				"SET Password = '" + newPassword + "' " +
				"WHERE UserName = '" + userName + "' " +
				"AND Password = '" + oldPassword + "'"
			) != 0;
			
			// if the user's password was changed, display a message indicating as such
			if(passwordChanged) {
				DatabaseServer.console.writeLine("Changed password for user \"" + userName + "\"");
			}
			
			return passwordChanged;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error changing password for user \"" + userName + "\": " + e.getMessage());
		}
		return false;
	}
	
	// add a match to the game history table
	public boolean addGame(String firstPlayer, String secondPlayer, Outcome outcome) throws RemoteException {
		if(firstPlayer == null || secondPlayer == null || outcome == null) { return false; }
		
		boolean gameCreated = false;
		
		try {
			// add the gamew to the game history table
			gameCreated = m_statement.executeUpdate(
				"INSERT INTO " + GAME_HISTORY_TABLENAME + " VAULES(" +
					"CURRENT_TIMESTAMP, " +
					"'" + firstPlayer + "', " +
					"'" + secondPlayer + "', " +
					"'" + outcome.ordinal() + "'" +	
				")"
			) != 0;
			
			// if the game was added to the game history table, display a message indicating as such
			if(gameCreated) {
				DatabaseServer.console.writeLine("Added game beween \"" + firstPlayer + "\" and \"" + secondPlayer + "\" to database");
			}
			
			return gameCreated;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error adding game for users \"" + firstPlayer + "\" and \"" + secondPlayer + "\": " + e.getMessage());
		}
		
		return false;
	}
	
	// get the stats for the specified player
	public int[] getPlayerStats(String userName) throws RemoteException {
		if(userName == null) { return null; }
		
		try {
			// get the specified user from the user data table
			SQLResult users = new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + USER_DATA_TABLENAME + " " +
					"WHERE UserName = '" + userName + "'"
			));
			
			// check that the user's information was retrieved
			if(users.getRowCount() != 1) { return null; }
			
			// initialize variables with the user's stats
			int[] stats = new int[3];
			try {
				for(int i=0;i<3;i++) {
					stats[i] = Integer.parseInt(users.getElement(0, i+4));
					if(stats[i] < 0) { return null; }
				}
			}
			catch(NumberFormatException e) {
				return null;
			}
			
			DatabaseServer.console.writeLine("Retrieved stats for user \"" + userName + "\"");
			
			return stats;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error retrieving stats for user \"" + userName + "\": " + e.getMessage());
		}
		
		return null;
	}
	
	// obtain a specific player's game history
	public Vector<MatchData> getPlayerHistory(String playerName) throws RemoteException {
		if(playerName == null) { return null; }
		
		try {
			// obtain all matches from the game history table which the specified user participated in
			SQLResult result = new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + GAME_HISTORY_TABLENAME + " " +
				"WHERE FirstPlayer = '" + playerName + "'" +
				"OR SecondPlayer = '" + playerName + "'"
			));
			
			// verify that the user has played at least one match
			Vector<MatchData> matchData = new Vector<MatchData>();
			if(result.getRowCount() == 0) { return matchData; }
			
			// create a collection of match data objects with the user's match history 
			for(int i=0;i<result.getRowCount();i++) {
				SQLResultRow r = result.getRow(i);
				
				matchData.add(new MatchData(r.elementAt(0), r.elementAt(1), r.elementAt(2), Outcome.values()[Integer.parseInt(r.elementAt(3))]));
			}
			
			DatabaseServer.console.writeLine("Retrieved match history for \"" + playerName + "\"");
			
			return matchData;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error retrieving player history for player \"" + playerName + "\": " + e.getMessage());
		}
		
		return null;
	}
	
	// get the game history for two specified players where they played against one another
	public Vector<MatchData> getMatchHistory(String firstPlayerName, String secondPlayerName) throws RemoteException {
		if(firstPlayerName == null || secondPlayerName == null) { return null; }
		
		try {
			// obtain all matches from the game history table which the two specified users participated in
			SQLResult result = new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + GAME_HISTORY_TABLENAME + " " +
				"WHERE " +
					"(FirstPlayer = '" + firstPlayerName + "'" +
					"AND " +
					"SecondPlayer = '" + secondPlayerName + "')" +
				"OR " + 
					"(FirstPlayer = '" + secondPlayerName + "'" +
					"AND " +
					"SecondPlayer = '" + firstPlayerName + "')"
			));
			
			// verify that the two specified users have played at least one match
			Vector<MatchData> matchData = new Vector<MatchData>();
			if(result.getRowCount() == 0) { return matchData; }
			
			// create a collection of match data objects with the match history for the two specified players
			for(int i=0;i<result.getRowCount();i++) {
				SQLResultRow r = result.getRow(i);
				
				matchData.add(new MatchData(r.elementAt(0), r.elementAt(1), r.elementAt(2), Outcome.values()[Integer.parseInt(r.elementAt(3))]));
			}
			
			DatabaseServer.console.writeLine("Retrieved match history history for players \"" + firstPlayerName + "\" and \"" + secondPlayerName + "\"");
			
			return matchData;
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error retrieving player history for players \"" + firstPlayerName + "\" and \"" + secondPlayerName + "\": " + e.getMessage());
		}
		
		return null;
	}
	
	// add a database listener 
	public synchronized boolean addListener(DBMSListener listener) throws RemoteException {
		if(listener == null) { return false; }
		
		// only add the listener if it is not already a listener
		if(!m_listeners.contains(listener)) {
			m_listeners.add(listener);
			
			DatabaseServer.console.writeLine("Adding database listener");
		}
		
		return true;
	}
	
	// remove a database listener
	public synchronized boolean removeListener(DBMSListener listener) throws RemoteException {
		if(listener == null) { return false; }
		
		// remove the specified database listener
		if(m_listeners.remove(listener)) {
			DatabaseServer.console.writeLine("Removing database listener");
		}
		
		return true;
	}
	
	// close the database
	public void close() throws RemoteException {
		m_initialized = false;
		
		try { unexportObject(this, true); } catch(NoSuchObjectException e) { }
		
		try { m_database.close(); } catch (SQLException e) { }
	}
	
	// notify all listeners that the specified user's stats have been updated
	private void notifyStatsUpdate(String userName) {
		if(userName == null) { return; }
		
		try {
			// get the specified user from the database
			SQLResult users = new SQLResult(m_statement.executeQuery(
				"SELECT * FROM " + USER_DATA_TABLENAME + " " +
					"WHERE UserName = '" + userName + "'"
			));
			
			// verify that the user's information was obtained
			if(users.getRowCount() != 1) { return; }
			
			// obtain the user's stats
			int wins = -1, losses = -1, draws = -1;
			try {
				wins = Integer.parseInt(users.getElement(0, 4));
				losses = Integer.parseInt(users.getElement(0, 5));
				draws = Integer.parseInt(users.getElement(0, 6));
			}
			catch(NumberFormatException e) {
				return;
			}
			if(wins < 0 || losses < 0 || draws < 0) { return; }
			
			// inform all listener's of the user's new stats
			for(int i=0;i<m_listeners.size();i++) {
				try {
					m_listeners.elementAt(i).statsUpdated(userName, wins, losses, draws);
				}
				catch(RemoteException e) {
					DatabaseServer.console.writeLine("Removing database listener: " + e.getMessage());
					m_listeners.remove(i);
					i--;
				}
			}
			
			DatabaseServer.console.writeLine("Notified " + m_listeners.size() + " listeners of stats update for \"" + userName + "\"");
		}
		catch(SQLException e) {
			DatabaseServer.console.writeLine("Error retrieving user \"" + userName + "\" data for stats update: " + e.getMessage());
		}
	}
	
	// update the specified table with the current contents from the database
	public void updateTable(String tableName, JTable table, boolean autoSizeColumns) {
		// verify that the table and parameters are valid
		if(tableName == null || table == null) { return; }
		if(!(tableName.equals(USER_DATA_TABLENAME) || tableName.equals(GAME_HISTORY_TABLENAME))) { return; }
		
		try {
			// get the selected table cell
			int x = table.getSelectedColumn();
			int y = table.getSelectedRow();
			
			// get the table's model and clear it safely
			DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
			tableModel.getDataVector().removeAllElements();
			tableModel.fireTableDataChanged();
			
			// get the contents of the specified table from the database
			ResultSet result = m_statement.executeQuery("SELECT * FROM " + tableName);
			ResultSetMetaData metaData = result.getMetaData();
			
			// update the table model with the result
			String[] rowData;
			int numberOfColumns = metaData.getColumnCount();
			while(result.next()) {
				rowData = new String[numberOfColumns];
				for(int i=1;i<=numberOfColumns;i++) {
					rowData[i-1] = result.getString(i);
				}
				tableModel.addRow(rowData);
			}
			
			// if the previous selection is still valid, re-select the previously selected cell
			if(x >= 0 && y >= 0 && x < table.getColumnCount() && y < table.getRowCount()) { 
				table.getSelectionModel().setSelectionInterval(y, y);
				table.getColumnModel().getSelectionModel().setSelectionInterval(x, x);
			}
			
			// if the columns should be autosized, then autosize them using the table renderer
			if(autoSizeColumns) {
				final TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer();
				
				for(int i=0;i<table.getColumnCount();i++) {
					table.getColumnModel().getColumn(i).setPreferredWidth(renderer.getTableCellRendererComponent(table, table.getModel().getColumnName(i), false, false, 0, i).getPreferredSize().width);
				}
			}
		}
		catch(Exception e) { }
	}
	
}
