//////////////////////////////////////////////
//
//   GO java applet, Ver 1.1
//   
//   Author: Chenyang Xu, chenyang@jhu.edu
//      http://iacl.ece.jhu.edu/~chenyang
//
//////////////////////////////////////////////

import java.applet.Applet;    
import java.awt.*; 
import java.util.Vector;

public class go extends Applet {

  int AppletWidth, AppletHeight, 
      BoardLeft, BoardTop, BoardSize, Delta,
      mouseMoveX, mouseMoveY,
      StoneColor, START;
  boolean REMOVE;
  Vector StoneList = new Vector();
  int[][] BoardMap = new int[19][19];
  Image offscreenImg;
  Graphics offscreenG;
  Button Start = new Button("Start");
  Button Finish = new Button("Finish");
  Checkbox Remove = new Checkbox("Remove", null, false);

  public void init() {
   
    START = 0;
    REMOVE = false;
    add(Start);
    add(Remove);
    add(Finish);
    Remove.disable();
    Finish.disable();

    Dimension d = size();
    AppletHeight = d.height;
    AppletWidth = d.width;

    offscreenImg = createImage(AppletWidth, AppletHeight);
    offscreenG = offscreenImg.getGraphics();
    
    if (AppletHeight < AppletWidth)
        BoardSize = (int) (0.9 * AppletHeight);
    else
	BoardSize = (int) (0.9 * AppletWidth);

    Delta = BoardSize/18;
    BoardLeft = (AppletWidth - BoardSize)/2;
    BoardTop  = (AppletHeight - BoardSize)/2 + Delta/2;

    StoneList.removeAllElements();
    mouseMoveX = -10;
    mouseMoveY = -10;

    StoneColor = 0;  // 0 --> black,  1 --> white

    // initialize the BoardMap array to zeros
    for (int i=0; i<19; i++) 
	for (int j=0; j<19; j++)
	     BoardMap[i][j] = 0;

    repaint();
  }

  public void paint(Graphics g) {

    Color BoardColor;
    int x0, y0, x1, y1, n, i, j, stone_color;
    Integer tempInt;

    // Draw the background of the applet
    offscreenG.setColor(Color.gray);
    offscreenG.fillRect(0, 0, AppletWidth, AppletHeight);   

    // Paint the GO board
    BoardColor = new Color(230,205,80); // Draw the board background
    offscreenG.setColor(BoardColor);
    offscreenG.fillRect(BoardLeft, BoardTop, BoardSize-1, BoardSize-1);   
    offscreenG.setColor(Color.black);   // Draw the board grids
    for (i=0; i<= 18; i++)  {
	x0 = BoardLeft; y0 = i*Delta + BoardTop; 
        x1 = 18*Delta + BoardLeft; y1 = i*Delta + BoardTop;
        offscreenG.drawLine(x0, y0, x1, y1); // draw the row
	x0 = i*Delta + BoardLeft; y0 = BoardTop; 
        x1 = i*Delta + BoardLeft; y1 = 18*Delta + BoardTop;
        offscreenG.drawLine(x0, y0, x1, y1); // draw the column
    }
    for (i=0; i<3; i++)                // Draw the board markers
	for (j=0; j<3; j++) {
	    x0 = (3 + i*6) * Delta + BoardLeft - 2 ;  
	    y0 = (3 + j*6) * Delta + BoardTop - 2;
	    offscreenG.fillRect(x0, y0, 5, 5);
        }

    // Paint the stone list
    for (n=0; n < StoneList.size(); n+=3) {
	tempInt = (Integer) StoneList.elementAt(n);
        i = tempInt.intValue();
        tempInt = (Integer) StoneList.elementAt(n+1);
	j = tempInt.intValue();
	tempInt = (Integer) StoneList.elementAt(n+2);
        stone_color = tempInt.intValue();
 
        if (stone_color==0) {  // Draw the black stone
   	   offscreenG.setColor(Color.black);
	   offscreenG.fillOval((int)((i-0.5)*Delta) + BoardLeft, 
	   (int)((j-0.5)*Delta) + BoardTop, Delta, Delta);
        } else {               // Draw the white stone
  	   offscreenG.setColor(Color.white);
           offscreenG.fillOval((int)((i-0.5)*Delta) + BoardLeft + 1, 
	     (int)((j-0.5)*Delta) + BoardTop + 1, Delta-2, Delta-2);
   	   offscreenG.setColor(Color.black);
           offscreenG.drawOval((int)((i-0.5)*Delta) + BoardLeft + 1, 
	     (int)((j-0.5)*Delta) + BoardTop + 1, Delta-2, Delta-2);
        }

        // paint a marker on the current stone
        if (n == StoneList.size()-3) {
           if (stone_color==0) {  // Draw the black marker
              offscreenG.setColor(Color.white);
	      offscreenG.fillRect(i*Delta + BoardLeft - 1, 
	        j*Delta + BoardTop - 1, 3, 3);
           } else {               // Draw the white marker
              offscreenG.setColor(Color.black);
              offscreenG.fillRect(i*Delta + BoardLeft -1, 
	        j*Delta + BoardTop - 1, 3, 3);
           }
        }
    }

    // Paint the stone moving with the mouse
    if (START!=0 && mouseMoveX > BoardLeft-3 && mouseMoveY > BoardTop-3 &&
        mouseMoveX < BoardLeft+BoardSize+3 
	&& mouseMoveY < BoardTop+BoardSize+3) {

	if (!REMOVE) { // plot stone if REMOVE state is false
           if (StoneColor==0) { // Draw the black stone 
              offscreenG.setColor(Color.black);
              offscreenG.fillOval(mouseMoveX-Delta/2 + 1, 
	        mouseMoveY-Delta/2 + 1, Delta-2, Delta-2); 
           } else {             // Draw the white stone
  	      offscreenG.setColor(Color.white);
              offscreenG.fillOval(mouseMoveX-Delta/2 + 1, 
	        mouseMoveY-Delta/2 + 1, Delta-2, Delta-2); 
              offscreenG.setColor(Color.black);
              offscreenG.drawOval(mouseMoveX-Delta/2, 
	        mouseMoveY-Delta/2, Delta, Delta); 
           }
        } else { // draw the removing cross symbol
	   x0 = mouseMoveX-Delta/2 + 2; 
	   y0 = mouseMoveY-Delta/2 + 2;
	   x1 = x0 + Delta - 4;
	   y1 = y0 + Delta - 4;
           offscreenG.setColor(Color.red);
	   offscreenG.drawLine(x0,   y0,   x1,   y1);
	   offscreenG.drawLine(x0+1, y0,   x1,   y1-1);
	   offscreenG.drawLine(x0,   y0+1, x1-1, y1);
	   offscreenG.drawLine(x0,   y1,   x1,   y0);
	   offscreenG.drawLine(x0,   y1-1, x1-1, y0);
	   offscreenG.drawLine(x0+1, y1,   x1,   y0+1);
        }
	
    }

    g.drawImage(offscreenImg, 0, 0, this);

  }

  public Point stoneCoord(int x, int y) {

    int i, j;
    Point p = new Point(0,0);

    p.x = (int) (((float)(x)-BoardLeft)/Delta+0.5);
    p.y = (int) (((float)(y)-BoardTop)/Delta+0.5);

    return p;
  }

  public boolean mouseDown(Event e, int x, int y) {

     Point coord = stoneCoord(x,y);
     int MapVal, index, i, j;
     Integer tempInt;
     boolean found;

     if (START==1 && x > BoardLeft-3 && y > BoardTop-3 &&
        x < BoardLeft+BoardSize+3 && y < BoardTop+BoardSize+3) {
        // add stone while in play
        MapVal = BoardMap[coord.y][coord.x];
        if (REMOVE && MapVal != 0) { 
           // handle removing stones 
           found = false;  
	   int n = 0;   
	   while (!found && n < StoneList.size()) {
	       tempInt = (Integer) StoneList.elementAt(n);
               i = tempInt.intValue();
               tempInt = (Integer) StoneList.elementAt(n+1);
	       j = tempInt.intValue();
	       if (i==coord.x && j==coord.y) 
	          found = true;
	       n += 3;
           }
	   // under no circumstance, found must be true when finish
           // the above loop unless there is a bug in the program.
           // So I don't deal with exceptions here.
	   StoneList.removeElementAt(n-3);
	   StoneList.removeElementAt(n-3);
	   StoneList.removeElementAt(n-3);
           BoardMap[coord.y][coord.x] = 0;

           repaint();

        } else if (!REMOVE && MapVal == 0) {
           // handle adding stones
           BoardMap[coord.y][coord.x] = 1;
           StoneList.addElement(new Integer(coord.x));
           StoneList.addElement(new Integer(coord.y));
           StoneList.addElement(new Integer(StoneColor));
           StoneColor = 1 - StoneColor;  
           repaint();
        }
     }

     return true;
  }

  public boolean mouseMove(Event e, int x, int y) {

     mouseMoveX = x;
     mouseMoveY = y;
     repaint();

     return true;
  }

  public boolean action(Event evt, Object arg) {

     if (evt.target instanceof Button) 
	handleButton((String)arg);
     else if (evt.target instanceof Checkbox)	
	handleCheckbox(evt);

     return true;		
  }

  void handleButton(String bname) {

     if (bname.equals("Start")) {
	START = 1;
 	Start.disable();
	Remove.enable();
	Finish.enable();        
        // initialize the BoardMap array to zeros
        for (int i=0; i<19; i++) 
	    for (int j=0; j<19; j++)
	        BoardMap[i][j] = 0;

	repaint();

     } else if (bname.equals("Finish")) {
        StoneList.removeAllElements();
        START = 0;
	REMOVE = false;
        StoneColor = 0;
	Start.enable();
	Remove.setState(false);
	Remove.disable();
	Finish.disable();
	repaint();
     }
     else; // handle exceptions
  }

  void handleCheckbox(Event evt) {

     Checkbox currentCheckbox = (Checkbox)evt.target;
     if (currentCheckbox.getLabel() == "Remove") {
	REMOVE = Remove.getState();
	repaint();
     }

  }

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

}