/*
	Example applet of Conway's Life.
	©1996 Peter N Lewis <peter@stairways.com.au>
	Written for Metrowerks
	
	Feel free to use this source code in any way.
*/

/*package org.thibault.francis.games.ninemans;*/

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


public class NineMans extends Applet
{
  Board p;
  
	public void init()
	{
		StatusPane sp;
		
		setLayout( new BorderLayout() );
		
		sp=new StatusPane();
		add ("South",sp);
		p = new Board(100, 100, sp);
		add( "Center", p );

		resize(300, 300);
		p.repaint();
	}  // end init()
	
	public void restart()
	{
	  p.restart();
	}
	
	public void setLineWidth(int linewidth)
	{
	  p.setLineWidth(linewidth);
	}

/*
	public static void main(String args[])
	{
		com.metrowerks.AppletFrame.startApplet("org.thibault.francis.games.ninemans.NineMans", "NineMans", args);
	}
	*/

}

class CircleIcon extends Canvas {
  Color c;
  CircleIcon(Color c)
  {
     this.c=c;
     resize(16,16);
  }
  
  public void paint(Graphics g)
  {
	Rectangle b = bounds();
	g.setColor(c);
/*	g.drawRect(b.x,b.y,b.width,b.height);*/
	g.fillOval(b.x,b.y,b.width/2,b.height/2);
  }
}

class StatusPane extends Panel {
  Label turn,blackCount,whiteCount;
  Panel counts;
  
  StatusPane()
  {
    setLayout( new BorderLayout() );
    turn=new Label("");
    add("West",turn);
    
    counts=new Panel();
    counts.setLayout(new BorderLayout());
    
    Panel blackPanel=new Panel();
    Panel whitePanel=new Panel();
    
    blackCount=new Label("9");
    whiteCount=new Label("9");
    
    blackPanel.add("West",new CircleIcon(Board.pieceColor(Board.pieceTagBlack)));
    whitePanel.add("West",new CircleIcon(Board.pieceColor(Board.pieceTagWhite)));
        
    blackPanel.add("East",blackCount);
    whitePanel.add("East",whiteCount);
    
    counts.add("West",blackPanel);
    counts.add("East",whitePanel);
    
    add("East",counts);
  }
  
  public void setTurnMsg(String msg)
  {
  	turn.setText(msg);
  }
  
  public void setBlackCount(int N)
  {
    blackCount.setText(new Integer(N).toString());
  }
  
  public void setWhiteCount(int N)
  {
    whiteCount.setText(new Integer(N).toString());
  }
  
  public void hideCounts()
  {
  	counts.hide();
  }
  
  public void showCounts()
  {
  	counts.show();
  }
}

class BoardPoint extends Object {
  public int square,corner;
  public BoardPoint(int s, int c)
  {
    square=s;
    corner=c;
  }
}

class Board extends Canvas {
	
	public static final int pieceTagEmpty=0;
	public static final int pieceTagBlack=1;
	public static final int pieceTagWhite=2;
	
	StatusPane statusPane;
	
	public static Color pieceColor(int pieceTag)
	{
	  switch(pieceTag)
	    {
	      case pieceTagBlack:
	        return Color.blue;
	        
	      case pieceTagWhite:
	        return Color.white;

	      case pieceTagEmpty:
	      default:
	        return null;
	    }
	}
	
	public static String pieceColorName(int pieceTag)
	{
	  switch(pieceTag)
	    {
	      case pieceTagBlack:
	        return "blue";
	        
	      case pieceTagWhite:
	        return "white";

	      case pieceTagEmpty:
	      default:
	        return null;
	    }
	}
	
	int border;
	int linewidth;
	int nextPieceTag;
	boolean removing,dragging,movePhase;
	int blacksLeft,whitesLeft,blacksOnBoard,whitesOnBoard;
	BoardPoint pieceDragged;
	int lastx,lasty;
	
	String turnString()
	{
	  return pieceColorName(nextPieceTag)
	  		+(movePhase ? " moves" : " places");
	}
	
	String chooseString(int pieceTag)
	{
	  return "Choose a "+pieceColorName(pieceTag)+" piece to remove";
	}
	
	void setTurn(int pieceTag)
	{
	  nextPieceTag=pieceTag;
	  statusPane.setTurnMsg(turnString());
	  if (!movePhase)
	    {
	      statusPane.setBlackCount(blacksLeft);
	      statusPane.setWhiteCount(whitesLeft);
	    }
	  removing=false;
	}
	
	int otherPieceTag(int pieceTag)
	{
	  return (pieceTag==pieceTagBlack) ? pieceTagWhite : pieceTagBlack;
	}
	
	void bumpTurn()
	{
	  if ((blacksLeft==0) && (whitesLeft==0))
	    {
	      movePhase=true;
	  	  statusPane.hideCounts();
	  	}

	  setTurn(otherPieceTag(nextPieceTag));
	}
	
	int board[][]; // Inner dimension is which row, outer is where around the row
				   // (0 is upper left corner, then counterclockwise)
	
	public Board( int x_size, int y_size, StatusPane statusPane ) {
		board = new int[3][8];
		
		border=10;
		linewidth=3;
		this.statusPane=statusPane;
		
		resize( x_size, y_size );
		restart();
	}
	
	public void restart()
	{
	  enable();
	  for (int square=0; square<3; square++)
		for (int corner=0; corner<8; corner++)
	      board[square][corner]=pieceTagEmpty;
	  blacksLeft=9;
	  whitesLeft=9;
	  blacksOnBoard=0;
	  whitesOnBoard=0;
	  setTurn(pieceTagBlack);
	  statusPane.showCounts();
	  dragging=false;
	  movePhase=false;
	  repaint();
	}

	public void setLineWidth(int linewidth)
	{
	  this.linewidth=linewidth;
	  repaint();
	}
		
	static void drawThickHLine(Graphics g, int x1, int x2, int y, int thickness)
	{
		g.fillRect(x1,y,x2-x1,thickness);
	}
	
	static void drawThickVLine(Graphics g, int x, int y1, int y2, int thickness)
	{
		g.fillRect(x,y1,thickness,y2-y1);
	}
	
	static void drawThickRect(Graphics g, int x, int y, int width, int height, int thickness)
	{
		drawThickHLine(g,x,x+width,y,thickness);
		drawThickHLine(g,x,x+width,y+height-thickness,thickness);
		drawThickVLine(g,x,y,y+height,thickness);
		drawThickVLine(g,x+width-thickness,y,y+height,thickness);
	}
	
	Point vertex(int square, int corner)
	{
		int dx,dy;
		switch (corner)
		  {
		  	case 0:
		  	  dx=dy=-1;
		  	  break;
		  	  
		  	case 1:
		  	  dx=0;
		  	  dy=-1;
		  	  break;

		  	case 2:
		  	  dx=1;
		  	  dy=-1;
		  	  break;
		  	  
		  	case 3:
		  	  dx=1;
		  	  dy=0;
		  	  break;

		  	case 4:
		  	  dx=dy=1;
		  	  break;
		  	  
		  	case 5:
		  	  dx=0;
		  	  dy=1;
		  	  break;

		  	case 6:
		  	  dx=-1;
		  	  dy=1;
		  	  break;
		  	  
		  	case 7:
		  	  dx=-1;
		  	  dy=0;
		  	  break;
		  	  
		  	default:
		  	  return null;
		  }
		  
		return new Point(centerx()+dx*(minwidth()+square*dw())/2,
						 centery()+dy*(minwidth()+square*dw())/2);
	}
	
	Point vertex(BoardPoint p)
	{
	  return vertex(p.square,p.corner);
	}
	
	int maxwidth()
	{
	  Rectangle b=bounds();
	  int w=((b.width<b.height) ? b.width : b.height);
	  return w-border*2;
	}
	
	int minwidth()
	{
	  return maxwidth()/3;
	}
	
	int dw()
	{
	  return (maxwidth()-minwidth())/2;
	}
	
	int centerx()
	{
	  return bounds().x+bounds().width/2;
	}
	
	int centery()
	{
	  return bounds().y+bounds().height/2;
	}
	
	public void paint( Graphics g ) {
		Rectangle b = bounds();

		for ( int square = 0; square < 3; square++ ) {
			int w=minwidth()+square*dw();
			drawThickRect(g,centerx()-w/2,centery()-w/2,w,w,linewidth);
		}
		drawThickHLine(g,centerx()+minwidth()/2,centerx()+maxwidth()/2,centery(),linewidth);
		drawThickHLine(g,centerx()-maxwidth()/2,centerx()-minwidth()/2,centery(),linewidth);
		drawThickVLine(g,centerx(),centery()-maxwidth()/2,centery()-minwidth()/2,linewidth);
		drawThickVLine(g,centerx(),centery()+minwidth()/2,centery()+maxwidth()/2,linewidth);
		
		for ( int square = 0; square < 3; square++ )
		  {
		  	for (int corner=0; corner<8; corner++)
		  	  {
				Point p=vertex(square,corner);
				g.setColor(Color.red);
				g.fillOval(p.x-linewidth*5/2,p.y-linewidth*5/2,linewidth*5,linewidth*5);
				
				Color c=pieceColor(board[square][corner]);
				if (c!=null)
				  {
				    g.setColor(c);
					g.fillOval(p.x-linewidth*3/2,p.y-linewidth*3/2,linewidth*3,linewidth*3);
				  }
			  }
		  }
	}
	
	BoardPoint locatePoint(Point p)
	{
	  int r=linewidth*5/2;
	  for (int square=0; square<3; square++)
	    {
		  for (int corner=0; corner<8; corner++)
		    {
	          Point v=vertex(square,corner);
	          int dx=v.x-p.x;
	          int dy=v.y-p.y;
	          if (dx<0)
	            dx=-dx;
	          if (dy<0)
	            dy=-dy;
	          if (   (dx<=r)
	          	  && (dy<=r)
	          	  )
	          	return new BoardPoint(square,corner);
	        }
	    }
	    
	  return null;
	}
	
	BoardPoint locatePoint(int x, int y)
	{
	  return locatePoint(new Point(x,y));
	}
	
	boolean canMoveFrom(BoardPoint p)
	{
	  if (   (getPieceTag(p.square,p.corner+1)==pieceTagEmpty)
	      || (getPieceTag(p.square,p.corner-1)==pieceTagEmpty)
	      )
	    return true;
	    
	  if ((p.corner%2)!=0) /* At the middle of a side */
	    {
	      switch (p.square)
	        {
	          case 0:
	          	return (board[1][p.corner]==pieceTagEmpty);
	          case 1:
	          	return (   (board[0][p.corner]==pieceTagEmpty)
	          		    || (board[2][p.corner]==pieceTagEmpty)
	          		    );
	          case 2:
	          	return (board[1][p.corner]==pieceTagEmpty);
	          default:
	          	return false;
	         }
	    }
	  else
	    return false; /* around the square is the only way to move from a corner */
	}

	public boolean mouseDown( Event e, int x, int y ) {
		if (movePhase && (!removing))
		  {
		  	BoardPoint p=locatePoint(x,y);
		  	if (p!=null)
		  	  {
		  	    if (   (board[p.square][p.corner]==nextPieceTag)
		  	    	&& canMoveFrom(p)
		  	    	)
		  	      {
		  	      	startDrag(x,y);
		  	      	pieceDragged=p;
		  	      }
		  	  }
		  }
		return true;
	}
	
	/* Does wraparound on corner, so that -1 is equivalent to 7, 8 is equivalent to 0, etc. */
	int getPieceTag(int square, int corner)
	{
	  corner%=8;
	  if (corner<0)
	    corner+=8;
	  return board[square][corner];
	}
	
	boolean formedThree(BoardPoint p)
	{
	  int thisPiece=board[p.square][p.corner];
	  if ((p.corner%2)==0) /* At a corner of the square */
	    {
	      return (   (   (getPieceTag(p.square,p.corner+1)==thisPiece)
	      			  && (getPieceTag(p.square,p.corner+2)==thisPiece)
	      		  	  )	
	      	  	  || (   (getPieceTag(p.square,p.corner-1)==thisPiece)
	      		  	  && (getPieceTag(p.square,p.corner-2)==thisPiece)
	      		  	  )
	      	  	  );
	    }
	    
	  /* OK, so this point is at the middle of a side */
	  return (   (   (getPieceTag(p.square,p.corner+1)==thisPiece)
	  			  && (getPieceTag(p.square,p.corner-1)==thisPiece)
	  		  	  )	
	  		  || (   (board[0][p.corner]==thisPiece)
	  		  	  && (board[1][p.corner]==thisPiece)
	  		  	  && (board[2][p.corner]==thisPiece)
	  		  	  )
	  	  	  );
	  
	}
	
	boolean legalCapture(BoardPoint p)
	{
	  if (!formedThree(p))
	    return true;
	    
	  for (BoardPoint cur=new BoardPoint(0,0); cur.square<2; cur.square++)
	    for (cur.corner=0; cur.corner<8; cur.corner++)
	      {
	        if (board[cur.square][cur.corner]==board[p.square][p.corner])
	          if (!formedThree(cur))
	            return false;
	      }
	      
	  return true;
	}
	
	void startChoice(int pieceTag)
	{
	  statusPane.setTurnMsg(chooseString(otherPieceTag(pieceTag)));
	  removing=true;
	}
	
	void proclaimVictory(int victorTag)
	{
	  statusPane.setTurnMsg(pieceColorName(victorTag)+" wins");
	  disable();
	}
	
	void redrawBoardPoint(BoardPoint p)
	{
	  Graphics g=getGraphics();
	  Point v=vertex(p);
	  g.clipRect(v.x-linewidth*5,v.y-linewidth*5,linewidth*10,linewidth*10);
	  paint(g);
	}
	
	boolean legalMove(BoardPoint p1,
					  BoardPoint p2)
	{
	  if (   (p1.square==p2.square)
		  && (p1.corner==p2.corner)
		  )
		return false;

	  if (   (p1.square!=p2.square)
		  && (p1.corner!=p2.corner)
		  )
		return false;
		
	  if (p1.square==p2.square)
	    {
	      int valid1=p1.corner+1;
	      int valid2=p1.corner-1;
	      valid1%=8;
	      if (valid2<0)
	        valid2+=8;
	      if (!(   (p2.corner==valid1)
	      	  	|| (p2.corner==valid2)
	      	  	)
	      	  )
	      	 return false;
	    }
	  else
	    {
	  	  if (   (p1.square>(p2.square+1))
	  	  	  || (p1.square<(p2.square-1))
	  	  	  )
			return false;
	    }
		
	  return (board[p2.square][p2.corner]==pieceTagEmpty);
	}
	
	public boolean mouseUp( Event e, int x, int y ) {
		BoardPoint p=locatePoint(x,y);
		if (dragging)
		  stopDrag();
		if (p!=null)
		  {
		    if (removing)
		      {
		      	if (   (board[p.square][p.corner]==otherPieceTag(nextPieceTag))
		      	    && legalCapture(p)
		      	    )
		      	  {
		      	    board[p.square][p.corner]=pieceTagEmpty;
		      	    redrawBoardPoint(p);
		      	    if (nextPieceTag==pieceTagBlack)
		      	      {
		      	        whitesOnBoard--;
		      	        if (whitesOnBoard<=0)
		      	          {
		      	            proclaimVictory(pieceTagBlack);
		      	            return true;
		      	          }
		      	      }
		      	    else
		      	      {
		      	        blacksOnBoard--;
		      	        if (blacksOnBoard<=0)
		      	          {
		      	            proclaimVictory(pieceTagWhite);
		      	            return true;
		      	          }
		      	       }
		  		    bumpTurn();
		      	  }
		      }
			else
			  {
			  	if (movePhase)
			  	  {
			  	  	if (legalMove(pieceDragged,p))
			  	  	  {
			  	  	  	 board[p.square][p.corner]
			  	  	  	   =board[pieceDragged.square][pieceDragged.corner];
			  	  	  	 board[pieceDragged.square][pieceDragged.corner]=pieceTagEmpty;
			  	  	  	 redrawBoardPoint(p);
			  	  	  	 redrawBoardPoint(pieceDragged);
		  				 if (formedThree(p))
	  		  	  	  	   startChoice(nextPieceTag);
	  		  			 else
		  		      	   bumpTurn();
			  	  	  }
			  	  }
			  	else if (board[p.square][p.corner]==pieceTagEmpty)
		      	  {
		  	    	board[p.square][p.corner]=nextPieceTag;
		      		redrawBoardPoint(p);
	  				if (nextPieceTag==pieceTagBlack)
	  				  {
	    			  	blacksLeft--;
	    			  	blacksOnBoard++;
	    			  }
	  				else
	  				  {
	    			 	whitesLeft--;
	    			 	whitesOnBoard++;
	    			  }
		  			if (formedThree(p))
	  		  	  	  startChoice(nextPieceTag);
	  		  		else
		  		      bumpTurn();
		  		  }
		  	  }
		  }
		return true;
	}
	
	void startDrag(int x, int y)
	{
	  Graphics g=getGraphics();
	  g.setColor(Color.black);
	  g.setXORMode(Color.white);
	  g.drawOval(x,y,linewidth*5/2,linewidth*5/2);
	  lastx=x;
	  lasty=y;
	  dragging=true;
	}
	
	void doDrag(int x, int y)
	{
	  Graphics g=getGraphics();
	  g.setColor(Color.black);
	  g.setXORMode(Color.white);
	  g.drawOval(lastx,lasty,linewidth*5/2,linewidth*5/2);
	  g.drawOval(x,y,linewidth*5/2,linewidth*5/2);
	  lastx=x;
	  lasty=y;
	 }

	void stopDrag()
	{
	  Graphics g=getGraphics();
	  g.setColor(Color.black);
	  g.setXORMode(Color.white);
	  g.drawOval(lastx,lasty,linewidth*5/2,linewidth*5/2);
	  g.setXORMode(Color.black);
	  dragging=false;
	}
	
	public boolean mouseDrag( Event e, int x, int y ) {
		if ( dragging ) {
		  doDrag(x,y);
		}
		return true;
	}
}
