import java.nio.ByteBuffer;

/**
* @author Jeff Kesselman
*/
public class ChessProtocol {

   private enum OPCODE {

       NEWGAME, MOVE
   }

   public enum MOVE_FLAG {

       NORMAL, CHECK, CHECKMATE
   }

   private ChessProtocol() {}

   public static byte[] makeNewGamePacket(boolean white) {
       byte[] bytes = new byte[1 + 1];
       ByteBuffer buff = ByteBuffer.wrap(bytes);
       buff.put((byte) OPCODE.NEWGAME.ordinal());
       buff.put(white ? (byte) 1 : (byte) 0);
       return bytes;
   }

   public static byte[] makeMovePacket(char fromCol, char fromRow, char toCol,
           char toRow, MOVE_FLAG flag) {
       byte[] bytes = new byte[1 + 11];
       ByteBuffer buff = ByteBuffer.wrap(bytes);
       buff.put((byte) OPCODE.MOVE.ordinal());
       buff.putChar(fromCol);
       buff.putChar(fromRow);
       buff.putChar(toCol);
       buff.putChar(toRow);
       if (flag != null) {
           buff.put((byte) flag.ordinal());
       } else {
           buff.put((byte) MOVE_FLAG.NORMAL.ordinal());
       }
       return bytes;
   }

   public static void parsePacket(byte[] bytes, Listener l) {
       ByteBuffer buff = ByteBuffer.wrap(bytes);
       OPCODE op = OPCODE.values()[buff.get()];
       switch (op) {
           case NEWGAME:
               boolean white = (buff.get() == 1) ? true : false;
               l.newGame(white);
               break;
           case MOVE:
               char fromCol = buff.getChar();
               char fromRow = buff.getChar();
               char toCol = buff.getChar();
               char toRow = buff.getChar();
               MOVE_FLAG flag = MOVE_FLAG.values()[buff.get()];
               l.move(fromCol, fromRow, toCol, toRow, flag);
               break;
           default:
               System.err.println("Unknown opcode: "+op.name());
       }
   }

   public static interface Listener {

       /**
        * A chess move in standard algebraic notation.  NB: row identifiers
        * are characters, not int's!
	*
        * @param fromCol The piece's old column ('a' - 'g')
        * @param fromRow The piece's old row ('1'- '8')
        * @param toCol The piece's new column ('a' - 'g')
        * @param toRow The piece's new row ('1'-'8')
        * @param flag NORMAL,CHECK or CHECKMATE
        */
       public void move(char fromCol, char fromRow,
               char toCol, char toRow, MOVE_FLAG flag);

       /**
        * A message to start a new game.  
        * @param white true if we are the white player, false if we are black
        */
       public void newGame(boolean white);
   }

   public static void main(String[] args) {
       parsePacket(makeNewGamePacket(true), new Listener() {

           public void move(char fromCol, char fromRow, char toCol, char toRow, MOVE_FLAG flag) {
               System.err.println("Error: received move, expected newGame");
           }

           public void newGame(boolean white) {
               if (!white) {
                   System.err.println("Error: newGame expected true, got false");
               }
           }
       });

       parsePacket(makeNewGamePacket(false), new Listener() {

           public void move(char fromCol, char fromRow, char toCol, char toRow, MOVE_FLAG flag) {
               System.err.println("Error: received move, expected newGame");
           }

           public void newGame(boolean white) {
               if (white) {
                   System.err.println("Error: newGame expected false, got true");
               }
           }
       });

       parsePacket(makeMovePacket('a', '6', 'e', '1', null), new Listener() {

           public void move(char fromCol, char fromRow, char toCol, char toRow,
                   MOVE_FLAG flag) {
                   if(fromCol != 'a'){
                       System.out.println("Error: expected a got "+fromCol);
                   }
                   if(toCol != 'e'){
                       System.out.println("Error: expected e got "+toCol);
                   }
                   if(fromRow !=  '6'){
                       System.out.println("Error: expected 6 got "+fromRow);
                   }
                    if(toRow !=  '1'){
                       System.out.println("Error: expected 1 got "+toRow);
                   }
                    if(flag !=  MOVE_FLAG.NORMAL){
                       System.out.println("Error: expected NORMAL got "+
                               flag.name());
                   }

           }

           public void newGame(boolean white) {
               if (white) {
                   System.err.println("Error: received newGame, expected move");

               }
           }
       });
       parsePacket(makeMovePacket('a', '6', 'e', '1', MOVE_FLAG.CHECKMATE), 
               new Listener() {

           public void move(char fromCol, char fromRow, char toCol, char toRow,
                   MOVE_FLAG flag) {
                   if(fromCol != 'a'){
                       System.out.println("Error: expected a got "+fromCol);
                   }
                   if(toCol != 'e'){
                       System.out.println("Error: expected e got "+toCol);
                   }
                   if(fromRow !=  '6'){
                       System.out.println("Error: expected 6 got "+fromRow);
                   }
                    if(toRow !=  '1'){
                       System.out.println("Error: expected 1 got "+toRow);
                   }
                    if(flag !=  MOVE_FLAG.CHECKMATE){
                       System.out.println("Error: expected NORMAL got "+
                               flag.name());
                   }

           }

           public void newGame(boolean white) {
               if (white) {
                   System.err.println("Error: received newGame, expected move");

               }
           }
       });
       System.out.println("Done test.");
   }
}