import java.awt.*;
import java.awt.event.*;
import java.rmi.RemoteException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import javax.swing.*;

/**
 * Java Chat - A simple chatroom application - GUI Class.
 *
 * This is the GUI class. It waits for user input and relays
 * method calls to the ChatClient object passed in upon
 * instanciation.
 *
 * @author Sean Handley
 * @version November, 2006
 */
public class ClientGUI extends JFrame implements ActionListener, Runnable
{
    private static final long serialVersionUID = -3830449240459968936L;
    private ChatClient cc;
    private boolean requiresCleanExit = true;
    //swing components
    private Container frameMessageBuffer, frameMessageEnter;
    private JLabel lblMessage;
    private JTextArea txtMessageBuffer;
    private JTextArea txtUserList;
    private JTextField txtMessageEnter;
    private JButton btnSay, btnOptions, btnLogoutOrIn;
    /**
     * A welcome message displayed when the GUI is activated.
     */
    public final String welcomeMessage = "*** Welcome to SeanChat 1.0\n*** Enjoy your stay :-)\n\n";
    /**
     * A title for the GUI window.
     */
    public final String windowTitle = "SeanChat 1.0";

    /**
     * Get the chat client reference.
     * 
     * @return the chat client referred to by the gui
     */
    public ChatClient getChatClient()
    {
        return cc;
    }
    /**
     * Constructor for the client GUI. Takes a chat client as a
     * parameter and puts a reference in a class variable. This
     * allows client methods to be called by GUI events.
     * 
     * @param cc
     */
    public ClientGUI(ChatClient cc)
    {
        super();
        this.cc = cc;
        //add listener for window close event
        addWindowListener(
            new WindowAdapter()
            {
                public void windowClosing(WindowEvent e)
                {
                    dispose();
                    System.exit(0);
                }
            }
        );
        /*
         * Add a hook into the runtime to ensure clean shutdown
         * in case the client is forcefully terminated
         */

        Runtime.getRuntime().addShutdownHook(
            new Thread()
            {
                public void run()
                {
                    try
                    {
                        exitProcedure();
                    }
                    catch(Throwable t)
                    {
                        //At this point, just die...
                    }
                }
            }
        );

        //causes problems with the JVM
    }
    /*
     * Cleans up gracefully by announcing a user's departure,
     * removing them from the collection and disposing the window.
     */
    public void exitProcedure()
    {
        if(requiresCleanExit)
        {
            try
            {
                cc.removeClient();
                if(cc.isLoggedIn())
                {
                    cc.leave();
                    cc.setLoggedIn(false);
                }
            }
            catch(RemoteException e)
            {
                //At this point, just die...
            }
            catch(Exception e)
            {
                //At this point, just die...
            }
        }
    }
    /**
     * Builds the GUI in a new thread.
     */
    public void run()
    {
        buildGUI();
    }
    /**
     * Add a new message to the message buffer.
     * 
     * @param msg
     */
    public void addMessage(String msg)
    {
        Date date = new Date();
        String timestamp = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date);
        //append the new message and add a newline
        txtMessageBuffer.append("[" + timestamp + "] " + msg + "\n");
        //auto-scroll to the bottom
        txtMessageBuffer.setCaretPosition(txtMessageBuffer.getDocument().getLength());
    }
    /**
     * Update the current user list with the supplied arraylist.
     * 
     * @param users
     */
    public void updateUserList(ArrayList<String> users)
    {
        txtUserList.setText("Current users:\n");
        Iterator it = users.iterator();
        while(it.hasNext())
        {
            txtUserList.append("[" + (String)it.next() + "]\n");
        }
    }
    /**
     * Respond to action events accordingly. Implements the ActionListener
     * interface.
     * 
     * @param event
     */
    public void actionPerformed(ActionEvent event)
    {
        if (event.getSource() == btnSay)
        {
            if(cc.isLoggedIn()) //check if the user is logged in
            {
                if(txtMessageEnter.getText().length() > 0) //check the message is not null
                {
                    cc.sendMessage(cc.getUserName() + "> " + txtMessageEnter.getText());
                    txtMessageEnter.setText(""); //clear the message typing area
                }
            }
        }
        else if(event.getSource() == btnOptions)
        {
            //open an options dialog
            OptionsFrame of = new OptionsFrame(cc);
            of.buildGUI();
        }
        else if(event.getSource() == btnLogoutOrIn)
        {
            if(btnLogoutOrIn.getText().equalsIgnoreCase("Logout"))
            {
                logout();
            }
            else
            {
                login();
            }
            pack();
        }
    }
    /**
     * Log the user out.
     */
    public void logout()
    {
        try
        {
            cc.removeClient();
            cc.leave();
        }
        catch(RemoteException e)
        {
            System.err.println(e.toString());
        }
        cc.setLoggedIn(false);
        btnLogoutOrIn.setText("Login");
        btnSay.setEnabled(false);
        txtMessageEnter.setEnabled(false);
        updateUserList(new ArrayList<String>());
        addMessage("*** You are now logged out.");
    }
    /**
     * Log the user in.
     */
    public void login()
    {
        try
        {
            boolean success = cc.addClient();
            if(success)
            {
                cc.setLoggedIn(true);
                btnLogoutOrIn.setText("Logout");
                btnSay.setEnabled(true);
                txtMessageEnter.setEnabled(true);
                addMessage("*** You are now logged in.");
                cc.announce();
            }
            else
            {
                JOptionPane.showMessageDialog(this,"Username is in use. Please select another.");
                //show options frame to allow new username selection
                OptionsFrame of = new OptionsFrame(cc);
                of.buildGUI();
            }
        }
        catch(RemoteException e)
        {
            JOptionPane.showMessageDialog(this,"Cannot connect to server.");
        }
    }
    /*
     * Build the GUI using swing components.
     */
    private void buildGUI()
    {
        //put the GUI together using swing
        Container contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout());

        frameMessageBuffer = new Container();
        frameMessageBuffer.setLayout(new FlowLayout());

        txtMessageBuffer = new JTextArea("");
        txtMessageBuffer.setEditable(false);
        txtMessageBuffer.setText(welcomeMessage);
        txtMessageBuffer.setLineWrap(true);
        txtMessageBuffer.setBorder(BorderFactory.createLineBorder(Color.black));
        JScrollPane scrollingArea = new JScrollPane(txtMessageBuffer);
        scrollingArea.setPreferredSize(new Dimension(400,300));
        frameMessageBuffer.add(scrollingArea);

        txtUserList = new JTextArea("");
        txtUserList.setEditable(false);
        txtUserList.setText("Current users:\n");
        txtUserList.setLineWrap(true);
        txtUserList.setBorder(BorderFactory.createLineBorder(Color.black));
        txtUserList.setFocusable(false);
        JScrollPane scrollingArea2 = new JScrollPane(txtUserList);
        scrollingArea2.setPreferredSize(new Dimension(100,300));
        frameMessageBuffer.add(scrollingArea2);

        frameMessageEnter = new Container();
        frameMessageEnter.setLayout(new FlowLayout());

        lblMessage = new JLabel("Message:");
        txtMessageEnter = new JTextField("");
        txtMessageEnter.setPreferredSize(new Dimension(200,25));
        btnSay = new JButton("Say");
        btnSay.addActionListener(this);
        btnOptions = new JButton("Options");
        btnOptions.addActionListener(this);
        btnLogoutOrIn = new JButton("Login");
        btnLogoutOrIn.addActionListener(this);

        frameMessageEnter.add(lblMessage);
        frameMessageEnter.add(txtMessageEnter);
        frameMessageEnter.add(btnSay);
        frameMessageEnter.add(btnOptions);
        frameMessageEnter.add(btnLogoutOrIn);

        contentPane.add(frameMessageBuffer,BorderLayout.NORTH);
        contentPane.add(frameMessageEnter,BorderLayout.SOUTH);

        getRootPane().setDefaultButton(btnSay);

        pack();
        setLocationRelativeTo(null);
        setTitle(windowTitle + " [" + cc.getUserName() + "]");
        setResizable(false);
        txtMessageEnter.requestFocus();
        btnSay.setEnabled(false);
        txtMessageEnter.setEnabled(false);

        //wait forever
        java.lang.Object sync = new java.lang.Object();
        synchronized(sync)
        {
            while(true)
            {
                try {
                    sync.wait();
                } catch (InterruptedException e) {
                    System.err.println(e.toString());
                }
            }
        }
    }
    /**
     * Make the GUI visible.
     */
    public void showGUI()
    {
        setVisible(true);
    }
    public boolean getRequiresCleanExit() {
        return requiresCleanExit;
    }
    public void setRequiresCleanExit(boolean requiresCleanExit) {
        this.requiresCleanExit = requiresCleanExit;
    }
}