import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.util.StringTokenizer;

public class ReversiBoard extends Canvas {

  private static final int BLACK = -1;
  private static final int WHITE = 1;
  private static final int EMPTY = 0;
  private static final int LAST_BLACK = -2;
  private static final int LAST_WHITE = 2;
  private static final int LEGAL_MOVE = 99;
  private static final int SELECTED_MOVE = 98;
  private static final int TABLE_BORDER = 10;

  private int[][] pieces = new int[][] {
    { 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0},
  };

  private Socket socket;
  private BufferedReader in;
  private PrintWriter out;
  private int hoverX = -1;
  private int hoverY = -1;
  private Label statusLabel;
  private String host;
  private int color;
  private boolean aiMoving = false;

  private int getSquareAtX(int x) {
    if (x < TABLE_BORDER) {
      return -1;
    }
    int square = (x - TABLE_BORDER) * 8 / (getSize().width - TABLE_BORDER * 2);
    if (square > 7) {
      return -1;
    }
    return square;
  }

  private int getSquareAtY(int y) {
    if (y < TABLE_BORDER) {
      return -1;
    }
    int square = (y - TABLE_BORDER) * 8 / (getSize().height - TABLE_BORDER * 2);
    if (square > 7) {
      return -1;
    }
    return square;
  }

  public ReversiBoard(Label statusLabel, String host) {
    this.statusLabel = statusLabel;
    this.host = host;
    addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        int newHoverX = getSquareAtX(e.getX());
        int newHoverY = getSquareAtY(e.getY());
        if (newHoverX < 0 || newHoverY < 0 || pieces[newHoverY][newHoverX] != LEGAL_MOVE) {
          newHoverX = -1;
          newHoverY = -1;
        }
        if (hoverX != newHoverX || hoverY != newHoverY) {
          int oldHoverX = hoverX;
          int oldHoverY = hoverY;
          hoverX = newHoverX;
          hoverY = newHoverY;
          repaintSquare(oldHoverX,oldHoverY);
          repaintSquare(hoverX,hoverY);
        }
      }
    });
    addMouseListener(new MouseAdapter() {
      public void mouseExited(MouseEvent e) {
        if (hoverX != -1 || hoverY != -1) {
          int oldHoverX = hoverX;
          int oldHoverY = hoverY;
          hoverX = -1;
          hoverY = -1;
          repaintSquare(oldHoverX,oldHoverY);
        }
      }
      public void mouseClicked(MouseEvent e) {
        synchronized(this) {
          if (aiMoving) {
            return;
          }
        }
        int x = getSquareAtX(e.getX());
        int y = getSquareAtY(e.getY());
        if (x >= 0 && y >= 0) {
          if (pieces[y][x] != 99) {
            return;
          }
          int moveNum = 0;
          for (int sy = 0; sy < 8; sy++) {
            for (int sx = 0; sx < 8; sx++) {
              if (pieces[sy][sx] == LAST_BLACK) {
                pieces[sy][sx] = BLACK;
                repaintSquare(sx,sy);
              }
              else if (pieces[sy][sx] == LAST_WHITE) {
                pieces[sy][sx] = WHITE;
                repaintSquare(sx,sy);
              }
              else if (pieces[sy][sx] == LEGAL_MOVE) {
                moveNum++;
                if (sy == y && sx == x) {
                  pieces[sy][sx] = SELECTED_MOVE;
                  hoverX = -1;
                  hoverY = -1;
                  out.println(moveNum);
                  repaintSquare(sx,sy);
                  doAI();
                  return;
                }
              }
            }
          }
        }
      }
    });
  }

  public void clearBoard() {
    for (int y = 0; y < 8; y++) {
      for (int x = 0; x < 8; x++) {
        pieces[y][x] = 0;
      }
    }
    repaint();
  }

  public void connectAsColor(int color) throws IOException {
    System.out.println("Got here");
    synchronized(this) {
      if (aiMoving) {
        return;
      }
    }
    this.color = color;
    clearBoard();
    if (socket != null) {
      try {socket.close();} catch (IOException ex) {}
      socket = null;
      in = null;
      out = null;
    }
    socket = new Socket(host,9000);
    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    out = new PrintWriter(socket.getOutputStream(),true);
    StringBuffer sb = new StringBuffer();
    while (true) {
      int ch = in.read();
      if (ch < 0) {
        throw new EOFException();
      }
      sb.append((char) ch);
      if (sb.toString().indexOf("Your choice: ") >= 0) {
        out.println("999");
        break;
      }
    }
    doAI();
  }

  public boolean process(String s) {
    System.out.println("Got Command: " + s);
    StringTokenizer st = new StringTokenizer(s," ");
    if (!st.hasMoreTokens()) {
      return false;
    }
    String command = st.nextToken();
    if (command.equals("CHOOSECOLOR")) {
      out.println(color);
      return false;
    }
    else if (command.equals("BOARDSTATE")) {
      for (int y = 0; y < 8; y++) {
        for (int x = 0; x < 8; x++) {
          int newPiece = Integer.parseInt(st.nextToken());
          if (newPiece != pieces[y][x]) {
            pieces[y][x] = newPiece;
            repaintSquare(x,y);
          }
        }
      }
      return false;
    }
    else if (command.equals("YOURMOVE")) {
      statusLabel.setText("Your turn.");
      statusLabel.setForeground(Color.red);
      st.nextToken();
      while (st.hasMoreTokens()) {
        int y = Integer.parseInt(st.nextToken());
        int x = Integer.parseInt(st.nextToken());
        pieces[y - 1][x - 1] = LEGAL_MOVE;
      }
      return true;
    }
    else if (command.equals("NOMOVES")) {
        statusLabel.setText("No available moves.");
        statusLabel.setForeground(Color.blue);
        return false;
    }
    else if (command.equals("GAMEOVER")) {
      int black = Integer.parseInt(st.nextToken());
      int white = Integer.parseInt(st.nextToken());
      if (black > white) {
        statusLabel.setText("Game over. Black wins " + black + " to " + white + ".");
      }
      else if (white > black) {
        statusLabel.setText("Game over. White wins " + white + " to " + black + ".");
      }
      else {
        statusLabel.setText("Game over. Tied at " + black + " discs.");
      }
      statusLabel.setForeground(Color.red);
      out.println();
      return true;
    }
    else return false;
  }

  public void repaintSquare(int x, int y) {
    if (x < 0 || y < 0) {
      return;
    }
    int width = getSize().width - TABLE_BORDER * 2;
    int height = getSize().height - TABLE_BORDER * 2;
    int px = TABLE_BORDER + x * width / 8;
    int py = TABLE_BORDER + y * height / 8;
    int pwidth = width / 8;
    int pheight = height / 8;
    repaint(px,py,pwidth,pheight);
  }

  public void paint(Graphics g) {
    int x = 0;
    int y = 0;
    int width = getSize().width;
    int height = getSize().height;
    g.setColor(Color.black);
    g.fillRect(x,y,width,height);
    x += TABLE_BORDER;
    y += TABLE_BORDER;
    width -= TABLE_BORDER * 2;
    height -= TABLE_BORDER * 2;
    g.setColor(new Color(64,128,64));
    g.fillRect(x,y,width,height);
    g.setColor(new Color(99,157,109));
    for (int i=0; i < 8; i++) {
      g.drawLine(x,y + 1 + i * height / 8,x + width,y + 1 + i * height / 8);
      g.drawLine(x + 1 + i * width / 8,y,x + 1 + i * width / 8,y + height);
    }
    for (int by = 0; by < 8; by++) {
      for (int bx = 0; bx < 8; bx++) {
        int piece = pieces[by][bx];
        if (piece == EMPTY) {
          continue;
        }
        int px = x + bx * width / 8;
        int py = y + by * height / 8;
        int pwidth = width / 8;
        int pheight = height / 8;
        if (piece == BLACK || piece == LAST_BLACK) { // BLACK
          g.setColor(Color.black);
          g.fillOval(px + 5,py + 5,pwidth - 10,pheight - 10);
          g.setColor(Color.black);
          g.drawOval(px + 5,py + 5,pwidth - 10,pheight - 10);
          if (piece == LAST_BLACK) {
            g.setColor(Color.white);
            g.drawLine(px + pwidth / 2 - 3,
                       py + pheight / 2 - 3,
                       px + pwidth / 2 + 3,
                       py + pheight / 2 + 3
                       );
            g.drawLine(px + pwidth / 2 + 3,
                       py + pheight / 2 - 3,
                       px + pwidth / 2 - 3,
                       py + pheight / 2 + 3
                       );
          }
        }
        else if (piece == WHITE || piece == LAST_WHITE) { // WHITE
          g.setColor(Color.white);
          g.fillOval(px + 5,py + 5,pwidth - 10,pheight - 10);
          g.setColor(Color.black);
          g.drawOval(px + 5,py + 5,pwidth - 10,pheight - 10);
          if (piece == LAST_WHITE) {
            g.setColor(Color.black);
            g.drawLine(px + pwidth / 2 - 3,
                       py + pheight / 2 - 3,
                       px + pwidth / 2 + 3,
                       py + pheight / 2 + 3
                       );
            g.drawLine(px + pwidth / 2 + 3,
                       py + pheight / 2 - 3,
                       px + pwidth / 2 - 3,
                       py + pheight / 2 + 3
                       );
          }
        }
        else if (piece == SELECTED_MOVE ||
                (piece == LEGAL_MOVE && hoverX == bx && hoverY == by)) {
          g.setColor(new Color(57,115,57));
          g.fillRect(x + bx * width / 8,y + by * height / 8,pwidth + 1,pheight + 1);
          g.setColor(new Color(45,90,45));
          g.drawLine(x + bx * width / 8 + 1,
                     y + by * height / 8,
                     x + bx * width / 8 + 1,
                     y + (by + 1) * height / 8);
          g.drawLine(x + bx * width / 8,
                     y + by * height / 8 + 1,
                     x + (bx + 1) * width / 8,
                     y + by * height / 8 + 1);
          if (piece == SELECTED_MOVE) {
            g.setColor(Color.white);
            g.drawLine(px + pwidth / 2 - 3,
                       py + pheight / 2 - 3,
                       px + pwidth / 2 + 3,
                       py + pheight / 2 + 3
                       );
            g.drawLine(px + pwidth / 2 + 3,
                       py + pheight / 2 - 3,
                       px + pwidth / 2 - 3,
                       py + pheight / 2 + 3
                       );
          }
        }
      }
    }
    g.setColor(Color.black);
    for (int i=0; i < 8; i++) {
      g.drawLine(x,y + i * height / 8,x + width,y + i * height / 8);
      g.drawLine(x + i * width / 8,y,x + i * width / 8,y + height);
    }
  }

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

  public void doAI() {
    new AIThread().start();
  }

  private class AIThread extends Thread {
    public AIThread() {
      synchronized(ReversiBoard.this) {
        if (aiMoving) {
          return;
        }
        aiMoving = true;
      }
      statusLabel.setText("Computer is thinking...");
      statusLabel.setForeground(Color.black);
      new Thread() {
        public void run() {
          waitForAI();
        }
      }.start();
    }

    private void waitForAI() {
      try {
        while (true) {
          String line = in.readLine();
          if (line == null) {
            statusLabel.setText("Connection to server lost...");
            statusLabel.setForeground(Color.red);
            return;
          }
          if (process(line)) {
            return;
          }
        }
      }
      catch (IOException ex) {
        statusLabel.setText(ex.toString());
        statusLabel.setForeground(Color.red);
      }
      finally {
        aiMoving = false;
      }
    }

  }

}