import Piece;
import Pawn;
import Game;
import Board;
import TokenInputStream;
import java.util.*;
import java.io.*;
import Move;


// class: Parser
//
// This class parses a PGN chess file into a Games made up of a list of Move classes.
// To parse the game this class makes uses the Board, Piece, Pawn, King, Queen, Bishop,
// Knight, and Rook classes. These classes are only used to parse the file when the 
// file is opened. The parser and these classes are not used while the animation is
// running. 
//


public class Parser {
	
	public int MAX_LINE_LENGTH = 256;
	public int MIN_GAME_LENGTH = 16;
	
	protected Vector m_games = new Vector();
	
	protected int m_currentGame;
	
	protected static Board m_board;
		
	protected int m_xCapture = -1;
  protected int m_yCapture = -1;
	
	public Parser() {
		m_board = null;
		Pawn.m_parser = this;    
	}
	
  public static Board board() { return m_board;};
	
	void Reset() {
		
		m_board = new Board();
		
    m_xCapture = -1;
    m_yCapture = -1;
  }	
	
	void NotifyOfCapture(int x, int y) {
		m_xCapture = x;
    m_yCapture = y;
    
	}
	
	
	
	////////////////////////////////////////////////////////////////
	//
	// Function Name: Parsefile
	// Parameters: filename - name of PGN database
	// Returns: number of games parsed. 0 indicates an error occured.
	// Effects: 
	//
	/////////////////////////////////-------------------------------
	
	
	Vector ParseFile(String filename) throws FileNotFoundException{
		
		m_currentGame = 0;
		
		TokenInputStream is = new TokenInputStream(new FileInputStream(filename));
		
		try {
			while(is.peek() > 0) {
				ParseGame(is);   
			}
		} catch (Exception e) {
			System.out.println("ERROR Parsing Game!!");
			return null;
		}
		if ( m_currentGame > 0)
      return m_games;
    else
      return null;
	}
	
	
	////////////////////////////////////////////////////////////////
	//
	// Function Name: ParseGame
	// Parameters: is - stream to get the game from
	// Effects: parses the game. throws if there is an error. reads the
	// file until the error or the game has been read.
	//
	/////////////////////////////////-------------------------------
	
	
	void ParseGame(TokenInputStream is) throws IOException, Exception {
		char next;

		StringBuffer srcCopy = new StringBuffer();
		
		Reset();
		
		m_games.insertElementAt(new Game(),m_currentGame);
		
		
		char buf[] = new char[MAX_LINE_LENGTH];
		
		//move up to tag pair section
		next = is.peek();
		is.setWhite("\r\n");
		// read in all the tags
		while (next == '['){
			int read = 0;
			read = is.read(buf,0,buf.length);
			
			String tag = new String(buf,0,read);
			
			Game current_game = (Game)m_games.elementAt(m_currentGame);
			current_game.m_tags.insertElementAt(tag, current_game.m_tags.size());
			
			is.eatWhite();
			next = is.peek(); 
		}
		
		is.setWhite("\r\n\t ");
		
		// now we're at the beginning of the movetext
		if (next != -1){ 
			while (is.peek() != 0) {
				if (ParseMove(is, srcCopy))
					break;
			}
		}
		if (((Game)m_games.elementAt(m_currentGame)).m_moves.size() == 0) {
			m_games.removeElementAt(m_currentGame);
			
		} else {
			
			((Game)m_games.elementAt(m_currentGame)).m_src = srcCopy.toString();
			m_currentGame++;
		}
	}
	
	
	////////////////////////////////////////////////////////////////
	//
	// Function Name: CharToType
	// Parameters: character identifying piece type
	// Returns: corresponding enumeration type
	//
	/////////////////////////////////-------------------------------
	
	int CharToType(char c) throws Exception{
		switch(c) {
		case 'P':
			return Piece.PAWN;
		case 'N':
			return Piece.KNIGHT;
		case 'B':
			return Piece.BISHOP;
		case 'R':
			return Piece.ROOK;
		case 'Q':
			return Piece.QUEEN;
		case 'K':
			return Piece.KING;
		default:
			throw new Exception("Invalid piece code");
		}
	}

	public static char TypeToChar(int c) {
	switch(c) {
		case Piece.PAWN:
			return 'P';
		case Piece.KNIGHT:
			return 'N';
		case Piece.BISHOP:
			return 'B';
		case Piece.ROOK:
			return 'R';
		case Piece.QUEEN:
			return 'Q';
		case Piece.KING:
			return 'K';
		default:
			return '!';
		}
	
	}
	
	int FindChar(char buf[], char c, int len) {
		for (int i = 0; i < len; i++) {
			if (buf[i] == c)
				return i;
		}
		return -1;
	}
	
	////////////////////////////////////////////////////////////////
	//
	// Function Name: ParseMove
	// Parameters: input stream with cursor positioned at beginning of move to parse
	// Returns: True if the move signifies the end of the game
	// Effects: Adds move to move list (if appropriate) moves stream cursor to next element of movetext.
	//
	/////////////////////////////////-------------------------------
	
	
	boolean ParseMove(TokenInputStream is, StringBuffer srcCopy)throws IOException, Exception {
		
		char buf[] = new char[16];
		int iEnd, i;
		int xTo, yTo;  
		int xFrom = -1, yFrom = -1;
		char promote = 0;
		
    Move Result = new Move();
		
		int srcType = Piece.PAWN;
		
		int len = is.read(buf, 0, buf.length);

		// copy this move for src
    srcCopy.append(new String(buf,0,len)).append(" ");

		
		// check to see if it's a move number
	if ((i = FindChar(buf,'.', len)) != -1) {
		if (i == (len - 1))
			return false;
		else { // move number and real move together, so toss the move number info
			char temp[] = new char[16];
			i++;
			int j;
			for (j=0; i < len; i++,j++){
				temp[j] = buf[i];
			}
			temp[i] = 0;
			buf=temp;
			len = j;
		}
	}
		
		// check for kingside or queenside castling
		if ( (new String(buf, 0, 5)).equals("O-O-O")  ||
			(new String(buf, 0, 5)).equals("o-o-o")){
			// Queenside castling
			if (m_board.m_currentPlayer == Piece.WHITE) {
				
				m_board.MovePiece(4,0,2,0);
				m_board.MovePiece(0,0,3,0);
				Result.xFrom = 4;
        Result.yFrom = 0;
        Result.xTo = 2;
        Result.yTo = 0;
        Result.xFrom2 = 0;
        Result.yFrom2 = 0;
        Result.xTo2 = 3;
        Result.yTo2 = 0;
        Result.player = m_board.m_currentPlayer;
        
			} else {
				
				m_board.MovePiece(4,7,2,7);
				m_board.MovePiece(0,7,3,7);
        Result.xFrom = 4;
        Result.yFrom = 7;
        Result.xTo = 2;
        Result.yTo = 7;
        Result.xFrom2 = 0;
        Result.yFrom2 = 7;
        Result.xTo2 = 3;
        Result.yTo2 = 7;
        Result.player = m_board.m_currentPlayer;
			}
			m_board.m_currentPlayer = Board.OtherPlayer(m_board.m_currentPlayer);
			((Game)m_games.elementAt(m_currentGame)).m_moves.addElement(Result);
			return false;
		} else if ((new String(buf, 0, 3)).equals("O-O")  ||
			(new String(buf, 0, 3)).equals("o-o")){
			// kingside castling
			if (m_board.m_currentPlayer == Piece.WHITE) {
				m_board.MovePiece(4,0,6,0);
				m_board.MovePiece(7,0,5,0);
        Result.xFrom = 4;
        Result.yFrom = 0;
        Result.xTo = 6;
        Result.yTo = 0;
        Result.xFrom2 = 7;
        Result.yFrom2 = 0;
        Result.xTo2 = 5;
        Result.yTo2 = 0;
        Result.player = m_board.m_currentPlayer;
			} else {
				m_board.MovePiece(4,7,6,7);
				m_board.MovePiece(7,7,5,7);
        Result.xFrom = 4;
        Result.yFrom = 7;
        Result.xTo = 6;
        Result.yTo = 7;
        Result.xFrom2 = 7;
        Result.yFrom2 = 7;
        Result.xTo2 = 5;
        Result.yTo2 = 7;
        Result.player = m_board.m_currentPlayer;
			}
			m_board.m_currentPlayer = Board.OtherPlayer(m_board.m_currentPlayer);
			((Game)m_games.elementAt(m_currentGame)).m_moves.addElement(Result);
			return false;
		}
		
		// check for end of game
		if (FindChar(buf,'-', len) != -1 || 
      FindChar(buf,'*', len) != -1 ||
      (new String(buf,0,4)).equals("draw")) {
			

      if ((new String(buf,0,1)).equals("*")) {
        Result.result = "Game ended";
      } else if ((new String(buf,0,3)).equals("0-1")) {
        Result.result = "Black Wins";
      } else if ((new String(buf,0,3)).equals("1-0")) {
        Result.result = "White Wins";
      } else if ((new String(buf,0,7)).equals("1/2-1/2") || 
        (new String(buf,0,4)).equals("draw")) {
			  Result.result = "Draw Game";
      }
      m_board.m_currentPlayer = Board.OtherPlayer(m_board.m_currentPlayer);
			((Game)m_games.elementAt(m_currentGame)).m_moves.addElement(Result);
      System.err.print("parsed result: ");
      System.err.println(Result.result);
			return true;
		
    }
		
		// check to see if it's an annotation
		if (FindChar(buf,'$',len) != -1) return false;
		
		
		//!!! check for comments and RAV 
		
		iEnd = len - 1;
		
		// move past extra annotations
		switch(buf[iEnd]) {    
		case '+':
		case '#':
		case '?':
		case '!':
			iEnd--;
		}
		
		iEnd--;
		
		// check for promotion
		if ((buf[iEnd] == '=')) {
			promote = buf[iEnd + 1];
			iEnd = iEnd - 2;
		}
		
		
		// now we're at the start of the dest location
		xTo = Board.StringToX(new String(buf,iEnd,2));
		yTo = Board.StringToY(new String(buf,iEnd,2));	
		iEnd--;
		
		if (iEnd >= 0){
			//move through capture indicator if it's there
			if (buf[iEnd] == 'x')
				iEnd--;
			
			// iEnd should be at the end of the src piece identification
			i = 0;
			while (i <= iEnd){
				
				if ((buf[i] >= 'B') && (buf[i] <= 'R')){
					
					srcType = CharToType(buf[i]);
					
				} else if ((buf[i] >= 'a') && (buf[i] <= 'h')) {
					
					xFrom = buf[i] - 'a';
				}
				else if ((buf[i] >= '1') && (buf[i] <= '8')) {
					
					yFrom = buf[i] - '1';
				}        
				i++;
				
			}
		}
		// we've got all the information out of the string, now do something with it.
		Result = FindMove(srcType, xTo, yTo, xFrom, yFrom);
      
		if (promote != 0) {
			// and a "=Q" for piece promotion
			Result.promote = CharToType(promote);
			// and change the promoted pawn to whatever type
			m_board.PromotePiece(xTo,yTo,CharToType(promote));
			
		}
		
		// add an ,xA0 if we need to explicitly notify of a capture
		
		if (m_xCapture != -1) {
      Result.xCapture = m_xCapture;
      Result.yCapture = m_yCapture;

      m_xCapture = -1;
      m_yCapture = -1;
  	
		}
		
		((Game)m_games.elementAt(m_currentGame)).m_moves.
			addElement(Result);
		
    return false;
}



////////////////////////////////////////////////////////////////
//
// Function Name: FindMove
// Parameters: pieceType, square the piece is moving to, and
//  whatever knowledge about where it's coming from you have (xFrom & yFrom as -1 signify no knowledge)
// Returns: string descibing move which you must delete
// Effects: 
//
/////////////////////////////////-------------------------------


Move FindMove( int type,
              int xTo, int yTo, int xFrom, int yFrom) throws Exception{
  
  Vector pieces;
	int i;
  int x,y;
  
  pieces = m_board.GetPiecesOfType(type);
	
  // filter out the other player's pieces
  i = 0;
  while(i < pieces.size()){
    
    if (((Piece)pieces.elementAt(i)).GetColor() != m_board.m_currentPlayer){
      pieces.removeElementAt(i);
			continue;
    }
    i++;
  }
	
	// remove pieces that don't match the rank or
	// file specification (if either is given)
	if (xFrom != -1){
		i = 0;
		while(i < pieces.size()){
			x = ((Piece)pieces.elementAt(i)).GetX();
			y = ((Piece)pieces.elementAt(i)).GetY();
			if (x != xFrom){
				pieces.removeElementAt(i);
				continue;
			}
			i++;
		}
	}
	
	if (yFrom != -1){
		i = 0;
		while(i < pieces.size()){
			x = ((Piece)pieces.elementAt(i)).GetX();
			y = ((Piece)pieces.elementAt(i)).GetY();
			
			if (y != yFrom){
				pieces.removeElementAt(i);
				continue;
			}	
			i++;
		}
	}
	
	// remove pieces that cannot move to target square
	
	i = 0;
	while(i < pieces.size()){
		
		if (!((Piece)pieces.elementAt(i)).CanMoveTo(xTo,yTo)){
			pieces.removeElementAt(i);
			continue;
		}
		i++;
	}
	
	//  check for putting King in check disambiguation
	if (pieces.size() > 1) {
		i = 0;
		while(i < pieces.size()){
			x = ((Piece)pieces.elementAt(i)).GetX();
			y = ((Piece)pieces.elementAt(i)).GetY();
			
			if (!m_board.IsKingStillSafe(x, y, xTo, yTo, m_board.m_currentPlayer)){
				pieces.removeElementAt(i);
				continue;
			}
			i++;
		}
	}
	
	// we should have removed all ambiguity;
	//  assert(pieces->Count() == 1);  
	if (pieces.size() != 1) {
		System.out.println("ERROR!");
//		m_board.PrintBoard();
		throw new Exception("Unresolved ambiguity");
	}
	
	xFrom = ((Piece)pieces.elementAt(0)).GetX();
	yFrom = ((Piece)pieces.elementAt(0)).GetY();
	
  
  Move Result = new Move();
  if (m_board.GetPiece(xTo,yTo) != null) {
    Result.xCapture = xTo;
    Result.yCapture = yTo;
  }
  Result.xFrom = xFrom;
  Result.yFrom = yFrom;
  Result.xTo = xTo;
  Result.yTo = yTo;
  Result.player = m_board.m_currentPlayer;

  m_board.MovePiece(xFrom, yFrom, xTo, yTo);
	m_board.m_currentPlayer = Board.OtherPlayer(m_board.m_currentPlayer);

  
	return Result;
	
} 

} 


