import java.rmi.*;


/**
 * Java Chat - A simple chatroom application - Client Class.
 *
 * This is the client class. It's responsible for instigating a
 * connection with the server and setting the username as well
 * as general communication between user and server. In addition,
 * it launches the GUI object silently.
 *
 * @author Sean Handley
 * @version November, 2006
 */
public class ChatClient
{
    private Callback returnComms;
    private IChatServer serverObject;
    private String userName;
    private boolean loggedIn;
    private ClientGUI gui;
    private final String defaultUsername = "User1";

    /**
     * Main entry point for the client. Instanciates the client with
     * command-line arguments as specified by the user. This will always
     * include a server URI and optionally a username.
     * 
     * @param args
     */
    public static void main (String args[])
    {
        if (args.length < 1)
        {
            System.out.println("usage: java ChatClient <server address> [username]");
        }
        else if(args.length < 2)
        {
            ChatClient cc = new ChatClient(args[0]);
            cc.toString(); //removes a compiler warning about local usage
        }
        else if(args.length < 3)
        {
            ChatClient cc = new ChatClient(args[0],args[1]);
            cc.toString(); //removes a compiler warning about local usage
        }
    }
    /**
     * Constructor with only the URI. Adds in a default user name and
     * calls the main 2-parameter constructor.
     * 
     * @param serverAddress
     */
    public ChatClient(String serverAddress)
    {
        ChatClient cc = new ChatClient(serverAddress,defaultUsername);
        cc.toString(); //removes a compiler warning about local usage
    }
    /**
     * Constructor for the chat client.
     * 
     * @param serverAddress
     * @param userName
     */
    public ChatClient(String serverAddress, String userName)
    {
        this.userName = userName;
        loggedIn = false;
        //start building the GUI silently
        gui = new ClientGUI(this);
        //(pass a copy of _this_ so controls can invoke methods.
        new Thread(gui).start();

        Remote remoteObject;

        // Set up a security manager
        System.setSecurityManager(new RMISecurityManager());

        try
        {
            String name = "rmi://" + serverAddress + "/CHAT-SERVER";
            remoteObject = Naming.lookup(name);

            serverObject = (IChatServer)remoteObject;
            returnComms = new Callback(serverObject, gui,userName);
        }
        catch(java.rmi.ConnectException e)
        {
            System.out.println("FATAL ERROR: Could not connect to server.");
            System.exit(1);
        }
        catch (Exception e)
        {
            System.out.println("General error: " + e.toString());
            System.exit(1);
        }

        //now that all is well, it's safe to show the GUI
        gui.showGUI();
    }
    /**
     * Change the username and perform the necessary related operations. 
     * 
     * @param newName
     * @throws java.rmi.RemoteException
     */
    public void changeName(String newName) throws RemoteException
    {
        removeClient();
        String oldName = userName;
        userName = newName;
        returnComms.setUsername(userName);
        addClient();
        gui.setTitle(gui.windowTitle + " [" + userName + "]");
        announceNameChange(oldName, newName);
    }
    /**
     * 
     * Test if client is logged in.
     * 
     * @return flag
     */
    public boolean isLoggedIn()
    {
        return loggedIn;
    }
    /**
     * Changed logged in status.
     * 
     * @param status
     */
    public void setLoggedIn(boolean status)
    {
        loggedIn = status;
    }
    /**
     * Change the username and pass on the new name to the
     * gui's title.
     * 
     * @param userName
     */
    public void setUserName(String userName)
    {
        this.userName = userName;
        gui.setTitle(gui.windowTitle + " [" + userName + "]");
    }
    /**
     * Return the username.
     * 
     * @return username
     */
    public String getUserName()
    {
        return userName;
    }
    /**
     * Send a new message to the server using a threaded
     * transaction.
     * 
     * @param msg
     */
    public void sendMessage(String msg)
    {
        MessageTransaction mc = new MessageTransaction(serverObject,msg);
        new Thread(mc).start();
    }
    /**
     * Calls the server method for leaving.
     * 
     * @throws java.rmi.RemoteException
     */
    public void leave() throws RemoteException
    {
        serverObject.leave(userName);
    }
    /**
     * Calls the server's check user method.
     * 
     * @param username
     * @return flag
     * @throws java.rmi.RemoteException
     */
    public boolean checkUser(String username) throws RemoteException
    {
        return serverObject.checkUser(username);
    }
    /**
     * Calls the server's disconnect method and updates the gui's
     * userlist.
     * 
     * @throws java.rmi.RemoteException
     */
    public void removeClient() throws RemoteException
    {
        serverObject.removeClient(returnComms);
        gui.updateUserList(serverObject.getUsers());
    }
    /**
     * Calls the server's connect method.
     * 
     * @return true if the user was added successfully, false otherwise
     * @throws java.rmi.RemoteException
     */
    public boolean addClient() throws RemoteException
    {
        returnComms.setUsername(userName); //update the username in the callback object
        return serverObject.addClient(returnComms);
    }
    /**
     * Calls the server's announce method and updates the
     * gui's user list.
     * 
     * @throws java.rmi.RemoteException
     */
    public void announce() throws RemoteException
    {
        serverObject.announce(userName);
        gui.updateUserList(serverObject.getUsers());
    }
    /**
     * Calls the server's announceNameChange method.
     * 
     * @param newName
     * @throws java.rmi.RemoteException
     */
    public void announceNameChange(String oldName, String newName) throws RemoteException
    {
        serverObject.announceNameChange(oldName, newName);
    }
}