/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
/*
 * Java Mud/Telnet Client
 */

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

public class MudClient extends Applet {
  MudClientPanel clientPanel;

  public void init() {
    setLayout(new BorderLayout());
    MudClientPanel clientPanel = new MudClientPanel(this);
    add("Center", clientPanel);
    add("South",new MudClientControls(clientPanel));
  }

  public boolean handleEvent(Event e) {
    switch (e.id) {
      case Event.WINDOW_DESTROY:
        System.exit(0);
        return true;
      default:
        return false;
    }
  }

  public static void main(String args[]) {
    Frame f = new Frame("MudClient");
    MudClient mudClient = new MudClient();
    mudClient.init();
    mudClient.start();

    f.add("Center", mudClient);
    f.resize(590, 418);
    f.show();
  }

  public void start() {
    //super.start();
    if (clientPanel != null)
      clientPanel.start();
  }

  public void stop() {
    if (clientPanel != null)
      clientPanel.stop();
    //super.stop();
  }

/*
    public boolean mouseDown(Event e, int x, int y) {
      if (frozen) {
        frozen = false;
        start();
      } else {
        frozen = true;
        stop();
      }
      return true;
    }
*/

}



class MudClientPanel extends Panel implements Runnable {
  Image         blitBuffer;
  int           blitHeight;
  int           blitWidth;
  char          intensity;
  char          fg;
  char          bg;
  char          col;
  char          row;
  int           maxRow;
  int           maxCol;
  int           w;
  int           a;
  int           d;
  int           h;  /* a + d */
  Font          font;
  Applet        observer;
  Socket        socket;
  Thread        monitor; /* monitor the socket */
  boolean       frozen;  /* background thread is frozen */
  String        s;
  Color         dkGray;
  Color         red;
  Color         green;
  Color         yellow;
  Color         blue;
  Color         purple;
  Color         cyan;
  Color         white;
  Color         black;
  Color         dkRed;
  Color         dkGreen;
  Color         brown;
  Color         dkBlue;
  Color         dkPurple;
  Color         dkCyan;
  Color         silver;
  Rectangle     r;

  public MudClientPanel(Applet observer) {
    this.observer = observer;
    intensity = '0';
    fg = '7';
    bg = '0';
    dkGray   = new Color(128,128,128); /* dk gray */
    red      = new Color(255,0,0); /* red */
    green    = new Color(0,255,0); /* green */
    yellow   = new Color(255,255,0); /* yellow */
    blue     = new Color(128,128,255); /* blue */
    purple   = new Color(255,0,255); /* purple */
    cyan     = new Color(0,255,255); /* cyan */
    white    = new Color(255,255,255); /* white */
    black    = new Color(0,0,0); /* black */
    dkRed    = new Color(128,0,0); /* dk red */
    dkGreen  = new Color(0,128,0); /* dk green */
    brown    = new Color(128,128,0); /* brown */
    dkBlue   = new Color(0,0,128); /* dk blue */
    dkPurple = new Color(128,0,128); /* dk purple */
    dkCyan   = new Color(0,128,128); /* dk cyan */
    silver   = new Color(192,192,192); /* silver */
    maxRow = 25;
    maxCol = 80;
    col = 0;
    row = 0;
    frozen = false;
    setBackground(Color.black);
    try {
      socket = new Socket("daydream.uvic.ca", 5000);
      socket.setTcpNoDelay(true);
    } catch (IOException ioe) {
      System.out.println("new Socket(): IO Exception");
    }
    start(); /* start the background socket monitor thread */
  }

  /* (re)init the offscreen blitbuffer */
  public void initBlitImage() {
    char  c[] = new char[1];
  
    /* draw the current lines */
    r = bounds();
    Graphics g = getGraphics();
    font = new Font("Courier", Font.PLAIN, 12);
    g.setFont(font);
    FontMetrics fm = g.getFontMetrics();
    w = fm.charWidth('M');
    a = fm.getMaxAscent();
    d = fm.getMaxDescent();
    h = fm.getHeight();  /* a + d */

    blitWidth = w*this.maxCol;
    blitHeight = h*this.maxRow;
    blitBuffer = createImage(blitWidth, blitHeight);
    g = blitBuffer.getGraphics();
    g.setFont(font);
    g.setPaintMode();
    for (int j=0; j < this.maxRow; j++) {
      for (int i=0; i < this.maxCol; i++) {
        g.setColor(getBGColor('0'));
        g.fillRect(w*(i), h*(j), w, h);
        g.setColor(getFGColor('0', '7'));
        c[0] = ' ';
        g.drawChars(c, 0, 1, w*(i), h*(j)+a);
      }
    }
  }

  public Color getFGColor(char intensity, char fg) { 
    if (intensity=='1') {
      if (fg=='0') {
        return dkGray;
      } else if (fg=='1') {
        return red;
      } else if (fg=='2') {
        return green;
      } else if (fg=='3') {
        return yellow;
      } else if (fg=='4') {
        return blue;
      } else if (fg=='5') {
        return purple;
      } else if (fg=='6') {
        return cyan;
      } else if (fg=='7') {
        return white;
      }
    } else {
      if (fg=='0') {
        return black;
      } else if (fg=='1') {
        return dkRed;
      } else if (fg=='2') {
        return dkGreen;
      } else if (fg=='3') {
        return brown;
      } else if (fg=='4') {
        return dkBlue;
      } else if (fg=='5') {
        return dkPurple;
      } else if (fg=='6') {
        return dkCyan;
      } else if (fg=='7') {
        return silver;
      }
    }
    return silver; /* by default make it silver */
  }

  public Color getBGColor(char bg) { 
    return getFGColor('0', bg); /* Black by default */
  }

  public void drawChar(char charValue, int i, int j) {
    char  c[] = new char[1];
    Graphics g = blitBuffer.getGraphics();
    g.setFont(font); /* should be set from init */
    g.setPaintMode(); /* should be set from init */

    /* we should 'cache' color changes here???? */
    g.setColor(getBGColor(bg));
    g.fillRect(w*(i), h*(j), w, h);
    g.setColor(getFGColor(intensity, fg));
    c[0] = charValue;
    g.drawChars(c, 0, 1, w*(i), h*(j)+a);
  }

  public void scrollUp() {
    /* Update data */
    this.row-=1;
    /* scroll up a line */
    Graphics g = blitBuffer.getGraphics();
    g.copyArea(0, h, this.blitWidth, (this.maxRow-1)*h, 0, -h);

    /* Erase the next line with current colors */
    g.setColor(getBGColor(bg));
    g.fillRect(0, h*(maxRow-1), w*80, h);

    /* this is too slow of course */
    /*
    int j = maxRow-1;
    for (int i=0;i<this.maxCol;i++) {
      drawChar(' ',i,j);
    }
    */
  }

  /* Comments on ANSI:
   * A carriage return places the cursor on the next line and fills
   * it with spaces with the current fg/bk color
   * \033[k erases from current pos to end of the current line
   * \033[<intensity>;3<fg>;4<bk>m sets colors
   * any of the 3 can be omitted, if omitted color set to default, SO:
   * \033[m sets the color back to \033[0;37;40m
   * (which is silver on black)
   */
  public int appendText(String buf) {
    int        bRead = 0;

    while (bRead < buf.length()) {
      /* go down one line, erase it with current colors */
      if (buf.charAt(bRead)=='\n') {
        this.col=0;
        this.row++;
        /* Only one screen of data - turf overflow */
        if (this.row>=this.maxRow) {
          scrollUp();
        }
        bRead++;
      
      } else if (buf.charAt(bRead)=='\r') {
        /* skip over \r's, (we treat \n's as \r\n) */
        bRead++;
      
      } else if (buf.charAt(bRead)=='\033') {
        /* Translate ANSI */
        if((bRead+1)>=buf.length())
          return bRead;

        if (buf.charAt(bRead+1)!='[') {
          bRead++;
          continue;
        }
 
        if((bRead+2)>=buf.length())
          return bRead;

        /* Find the command */
        int ansiOffset = bRead;
        int ansiLen = 2;

        while (Character.isDigit(buf.charAt(ansiOffset+ansiLen))
              ||buf.charAt(ansiOffset+ansiLen)==';') {

          ansiLen++;

          /* command isnt complete */
          if (ansiOffset+ansiLen>=buf.length())
            return bRead;
        }

        if (buf.charAt(ansiOffset+ansiLen)=='k'
         || buf.charAt(ansiOffset+ansiLen)=='K') { /* erase command */
          for (int i = col; i<maxCol; i++) {
            drawChar(' ', i,this.row);
          }

        } else if (buf.charAt(ansiOffset+ansiLen)=='m') { /* character formatting command */
          this.intensity = '0'; /* Silver */
          this.fg = '7'; /* Silver */
          this.bg = '0'; /* Black */
          /*
           * This is how easy this is in C, but nothing is this easy in Java
           * cmdNum = sscanf(&buf[bRead], "\033[%s;m%s;m%s;m", cmd[1], cmd[2], cmd[3]);
          */
          for (int i = ansiOffset+2; i<=ansiLen+ansiOffset; ) {
            if (buf.charAt(i)=='0') {
              this.intensity = '0';
            } else if (buf.charAt(i) == '1') {
              this.intensity = '1';
            } else if (buf.charAt(i) == '3') {
              if ( Character.isDigit(buf.charAt(i+1)) ) {
                this.fg = buf.charAt(i+1);
                i++;
              }
            } else if (buf.charAt(i) == '4') {
              if ( Character.isDigit(buf.charAt(i+1)) ) {
                this.bg = buf.charAt(i+1);
                i++;
              }
            }
            i++;
          }
        
        }

        /* mark ansi commands as processed regardless */
        bRead+=ansiLen+1;
    
      /* Guard against control chars */
      } else if (buf.charAt(bRead)>=26){
      /* write character out with current color */
        if (this.col>=this.maxCol) {
          this.col=0;
          this.row+=1;
        }
        if (this.row>=this.maxRow) {
          scrollUp();
        }
        drawChar(buf.charAt(bRead), this.col,this.row);
        bRead++;
        this.col++;
      
      } else {
        /* skip unprintable character */
        bRead++;
      }
    }
    return bRead;
  }

  public boolean handleEvent(Event e) {
    switch (e.id) {
      case Event.WINDOW_DESTROY:
        System.exit(0);
        return true;
      default:
        return false;
    }
  }

  public void start() {
    if (frozen) { 
      //Do nothing.  The user has requested that we 
      //stop changing the image.
    } else {
      //Start animating!
      if (monitor == null) {
        monitor = new Thread(this, "SocketMonitor");
      }
      monitor.start();
    }
  }

  public void stop() {
    //Stop the animating thread.
    monitor.setPriority(Thread.MAX_PRIORITY);
    monitor = null;
  }

  public void run() {
    // UI takes precedence
    Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

    try {
      // Delay start until we can draw
      while (blitBuffer == null)
        Thread.sleep(1);
      while (Thread.currentThread() == monitor) {

        // Anything new
        InputStream is = socket.getInputStream();
        if (is.available()>0) {
          // Read the socket
          if (s==null) s=new String(""); // ensure string is here
          int isAvail = is.available();
          if (isAvail > 128) isAvail = 128; /* update every once in awhile */
          while (isAvail>0 && Thread.currentThread() == monitor) {
            int i = is.read();
            if (i==-1) break; // end of data
            char c = (char) i;
            s = s + String.valueOf(c);
            /*
             * Dont use s.concat(String) because it doesnt fucking work
             */
            if (c=='\n') { //keep the string down to managable size
              // np = NumberProcessed
              int np = appendText(s);
              if (np>0) {
                s = s.substring(np);
                // force redraw screen once a full line has been read in
                Graphics g = getGraphics();
                //should set cliprect here
                //g.clipRect(5, 3, this.blitWidth, this.blitHeight);
                repaint(); 
              }
            }

            isAvail--;
          }

          // np = NumberProcessed
          int np = appendText(s);
          if (np>0) {
            s = s.substring(np);
            // force redraw screen once a full line has been read in
            Graphics g = getGraphics();
            //should set cliprect here
            //g.clipRect(5, 3, this.blitWidth, this.blitHeight);
            repaint();
          }
        }

        Thread.sleep(10); // sleep for a millisecond or two
      }
    } catch (IOException ioe) {
      System.out.println("Thread.InputStream: IOException");
    } catch (InterruptedException ie) {
      System.out.println("Thread.Sleep(): Interrupted Exception");
    }
  }

  public void paint(Graphics g) {
    update(g);
  }

  public void update(Graphics g) {
    if (blitBuffer == null)
      initBlitImage();

    /* Draw the borders around the terminal area */
    //g.setColor(dkBlue);
    //g.fillRect(0, 0, r.width, 3); /* top */
    //g.fillRect(0, 3, 5, r.height-(3)); /* left */
    //g.fillRect(blitWidth+5, 3, r.width-(blitWidth+5), r.height-(3)); /* right */
    //g.fillRect(5, (blitHeight+3), blitWidth, r.height-(blitHeight+3)); /* bottom */

    /* copy blitBuffer */
    g.drawImage(blitBuffer, 5, 3, this.observer);
  }
}


class MudClientControls extends Panel {
  MudClientPanel target;

  public MudClientControls(MudClientPanel target) {
    this.target = target;
    setLayout(new FlowLayout());
    setBackground(Color.lightGray);
    target.setForeground(Color.black);

    TextField tf = new TextField("", 80);
    add(tf);   

/*
    CheckboxGroup group = new CheckboxGroup();
    Checkbox b;
    add(b = new Checkbox(null, group, false));
    b.setBackground(Color.red);
    add(b = new Checkbox(null, group, false));
    b.setBackground(Color.green);
    add(b = new Checkbox(null, group, false));
    b.setBackground(Color.blue);
    add(b = new Checkbox(null, group, false));
    b.setBackground(Color.pink);
    add(b = new Checkbox(null, group, false));
    b.setBackground(Color.orange);
    add(b = new Checkbox(null, group, true));
    b.setBackground(Color.black);
    target.setForeground(b.getForeground());
/*
/*
    Choice shapes = new Choice();
    shapes.addItem("Lines");
    shapes.addItem("Points");
    shapes.setBackground(Color.lightGray);
    add(shapes);
*/
  }

  public void paint(Graphics g) {
    Rectangle r = bounds();

    g.setColor(Color.lightGray);
    g.draw3DRect(0, 0, r.width, r.height, false);
  }

  public boolean action(Event e, Object arg) {
    if (e.target instanceof TextField) {
        /* do something with typing here */
      TextField tf = (TextField)e.target;
      String s = tf.getText();
      tf.setText("");

      // send s over target.socket 
      try {
        OutputStream os = target.socket.getOutputStream();
        for (int i=0; i<s.length(); i++)
          os.write(s.charAt(i));
        os.write('\n');
      } catch (IOException ioe) {
        System.out.println("Write OutputStream: IOException");
      }
   
      /* repaint the target with updated text */
      // Is java re-entrant I wonder?
      target.frozen = true;
      target.stop();
      target.appendText("\n");
      target.repaint();
      target.frozen = false;
      target.start();
 
    } else if (e.target instanceof Choice) {
      /*
      String choice = (String)arg;
      if (choice.equals("Lines")) {
        target.setDrawMode(MudClientPanel.LINES);
      } else if (choice.equals("Points")) {
        target.setDrawMode(MudClientPanel.POINTS);
      }
      */

    }
    return true;
  }
}