import java.io.*;
import java.net.*;
import java.awt.*;
import java.util.*;
import java.applet.*;
import java.awt.event.*;

public class ChatClient extends Applet implements ActionListener, Runnable {
  /* version history:
 
1.00: first release
1.01: added server-side remembrance of names, who command
1.02: added exit command
1.03: added versioning, display of version on entrance, and fixed? a bug
1.04: passes through all / commands, help added to server, out instead of err
1.05: slowed down versioning, added time command, minor bugfixes
1.06: fixed /exit broken in 1.04, just noticed
1.07: added 'beep' command, changed versioning structure
1.08: fixed beep command (oops?) remember, 8khz, mono, ...
1.09: added duplicate-name checking
1.10: added "whisper" functionality :)
1.11: fixed new /exit quirk, where client would send /exit if not logged in
1.12: think I fixed whisper to nobody crash problem
1.13: added more output
1.14: made output more helpful and understandable
1.15: made output prettier, added /clear command
1.16: Changed the "please enter your name" to a label, brightened colors a little... practically rewrote everything, flow-wise. exit deprecated.  put it into a small, undecorated window.. made it get more information from the server (after making the server give more information)... prettified stuff more (as much as I can, ish) and stuff.  no new version expected for a while.  TODO: record last couple of remarks made.... that's really server side.
1.17: fixed bug where closing before logging in makes you log in as "/exit"
1.18: added "blurb" param
1.19: now should close things more properly when you exit after NOT logging on
1.20: added two optional paramters: fgcolor and bgcolor :)
1.21: played with making the top area not editable, which made the bg the "normal" color. yay!
1.22: scrunched up text a little more, commented out all setEditable(true)s.
1.23: made it so you can be autologgedin with param:username
1.24: may have reduced flickering; 1.23 doesn't work.
  */

  private static final String version="1.24";
  private static int port=7791;
  private String host="localhost";
  private String name="John Doe";
  private String blurb="Welcome; please log in.";
  private String beep="http://alethe.net/chatroom/beep.au";
  private String fgcolor="50,0,80";
  private String bgcolor="255,255,255";


  private DataOutputStream oos=null;
  private DataInputStream ois=null;
  private TextArea view;
  private TextField input, nameField;
  private Panel namePanel;
  private boolean autologin=false;
  private Color fg, bg;

  public void run() {
    boolean good=true;
    String input;
    while (good) {
      try {
        input=ois.readUTF();
        if (input.charAt(0) == '/') {
          if (input.equals("/beep")) {
            try {
              AudioClip ac = getAudioClip(getDocumentBase(),"beep.au");
              //play(new URL(beep));
              if (ac != null) ac.play();
              else {
                //view.setEditable(true);
                view.append("unable to load: "+beep+"\n");
                //view.setEditable(false);
              }
            } catch (Exception e) { 
              //view.setEditable(true);
              view.append("beep error: "+e);
              //view.setEditable(false);
            }
          } else {
            //view.setEditable(true);
            view.append("received unknown command: "+input+"\n");
            //view.setEditable(false);
          }
        } else {
          //view.setEditable(true);
          view.append(input+"\n");
          //view.setEditable(false);
        }
      } catch (Exception e) {
        try {   
          //oos.writeUTF(name + " encountered Intermittent error :"+e);
          //oos.flush();
        } catch (Exception e2) {
          //view.setEditable(true);
          view.append("Error: "+e+"\n");
          //view.setEditable(false);
          good=false;
        }
      }
    }
    try { oos.writeUTF(name + " exiting.... (debug code) "); oos.flush(); }
    catch (Exception e) {}
    //view.setEditable(true);
    view.append("Exiting...");
    //view.setEditable(false);
  }

  private String connectToServer() {
    String answer;
    view.append("Contacting server ("+host+")...");
    try {
      Socket s=new Socket(host,port);
      oos=new DataOutputStream(s.getOutputStream());
      ois=new DataInputStream(s.getInputStream());
      //view.setEditable(true);
      view.append("Connected.  Getting server info: ");
      //view.setEditable(false);
      answer=ois.readUTF();
    } catch (Exception e) {
      answer="\nIt appears that the server is down.  Please try again later.\n";
      remove(namePanel);
      invalidate();
      validate();
    }
    return answer;
  }

  private String login(String name) {
    String answer;
    try {
      //view.setEditable(true);
      view.append("Attempting to log on as "+name+"...\n");
      //view.setEditable(false);
      oos.writeUTF(nameField.getText()); oos.flush();
      answer=ois.readUTF();
    } catch (Exception e) {
      answer="Hrm.  Something ain't right: "+e.toString()+"\n";
    }
    return answer;
  }

  public void actionPerformed(ActionEvent evt) {
    if (nameField != null) { // first!
      //drop it so we don't get multiple messages
      nameField.removeActionListener(this);
      if (nameField.getText().trim().equals("")) {
        nameField.addActionListener(this);
        return;
      } else {
        name=nameField.getText().trim();
        String answer=login(name);
        if (answer.startsWith("ok")) enterChat();
        else {
          //view.setEditable(true);
          view.append(answer+"\n\n");
          //view.setEditable(false);
          nameField.setText("");
          nameField.addActionListener(this);
        }
      }
    } else { //sending a message to the server
      try {
        String s=input.getText();
        if (!s.trim().equals("")) {
          if (s.equalsIgnoreCase("/exit")) {
            //view.setEditable(true);
            view.append("\n/exit has been deprecated.\njust close this window.\n");
            //view.setEditable(false);
          } else if (s.equalsIgnoreCase("/clear")) {
            //view.setEditable(true);
            view.replaceRange("",0,view.getText().length());
            //view.setEditable(false);
          } else if (s.charAt(0) == '/') { // pass on the cmd
            oos.writeUTF(s); oos.flush();
          } else { //send a user message
            oos.writeUTF(name+": "+s); oos.flush();
          }
        }
      } catch (Exception e) {
        //view.setEditable(true);
        view.append("Unable to write: "+input.getText());
        //view.setEditable(false);
      }
      input.setText("");
    }
  }

  private void enterChat() {
    remove(namePanel);
    nameField=null;
    input=new TextField();
    input.addActionListener(this);
    input.setBackground(bg);
    input.setForeground(fg);
    add(input,BorderLayout.SOUTH);
    invalidate();
    validate();
    input.requestFocus();
    new Thread(this).start();
  }

  public void init() {
    if (getParameter("host") != null) host=getParameter("host");
    if (getParameter("port") != null) port=Integer.parseInt(getParameter("port"));
    if (getParameter("beep") != null) beep=getParameter("beep");
    if (getParameter("blurb") != null) blurb=getParameter("blurb");
    if (getParameter("bgcolor") != null) bgcolor=getParameter("bgcolor");
    if (getParameter("fgcolor") != null) fgcolor=getParameter("fgcolor");
    if (getParameter("username") != null) {
      name=getParameter("username");
      autologin=true;
    }
    fg = makeColor(fgcolor);
    bg = makeColor(bgcolor);
    setLayout(new BorderLayout());
    view=new TextArea("ChatClient version "+version+": "+blurb+"\n",5,5,TextArea.SCROLLBARS_VERTICAL_ONLY);
    view.setEditable(false);
    add(view,BorderLayout.CENTER);
    namePanel=new Panel();
    namePanel.setLayout(new BorderLayout());
    Label l = new Label("Please enter your name:");
    namePanel.add(l,BorderLayout.WEST);
    nameField=new TextField();
    namePanel.add(nameField,BorderLayout.CENTER);
    add(namePanel,BorderLayout.SOUTH);
    nameField.addActionListener(this);
    l.setBackground(bg);
    setBackground(bg);
    namePanel.setBackground(bg);
    nameField.setBackground(bg);
    nameField.requestFocus();
    view.setBackground(bg);
    l.setForeground(fg);
    nameField.setForeground(fg);
    view.setForeground(fg);
    setVisible(true);
    //view.setEditable(true);
    view.append(connectToServer()+"\n");
    //view.setEditable(false);
    if (autologin) {
      String answer=login(name);
      if (answer.startsWith("ok")) enterChat();
      else {
        //view.setEditable(true);
        view.append("Unable to log in -- you appear to already be using the chatroom.  If you lost connection, the chatroom should forget you within five to ten minutes.  Or so.  Sorry, I wrote it a long time ago, and I'm amazed it still works at all.");
        //view.setEditable(false);
        nameField.setEnabled(false);
      }
    }
  }

  public void destroy() {
    try {
      if (nameField == null) oos.writeUTF("/exit");
      else {
        try { oos.close(); } catch (Exception e1) {}
        try { ois.close(); } catch (Exception e1) {}
  
      }
    } catch (Exception e) { }
  }

  private Color makeColor(String color) { 
    StringTokenizer s = new StringTokenizer(color,",");
    int r = Integer.parseInt(s.nextToken());
    int g = Integer.parseInt(s.nextToken());
    int b = Integer.parseInt(s.nextToken());
    return new Color(r,g,b);
  }
  
}
