////////////////////////////////////////////////////////////
// FUNCTIONS FOR EXTENDED SCRIPTING
// Use these for your own extensions,
// or to call functions defined elsewhere



/* my_PickFunc IS AUTOMATICALLY CALLED WHEN AN ITEM STARTS TO BE DRAGGED.
The following objects/properties are accessible from here:

- dd.e: current mouse event
- dd.e.property: access to a property of the current mouse event.
  Mostly requested properties:
  - dd.e.x: document-related x co-ordinate
  - dd.e.y: document-related y co-ord
  - dd.e.src: target of mouse event (not identical with the drag drop object itself).
  - dd.e.button: currently pressed mouse button. Left button: dd.e.button <= 1

- dd.obj: reference to currently dragged item.
- dd.obj.property: access to any property of that item.
- dd.obj.method(): for example dd.obj.resizeTo() or dd.obj.swapImage() .
  Mostly requested properties:
	- dd.obj.name: image name or layer ID passed to SET_DHTML();
	- dd.obj.x and dd.obj.y: co-ordinates;
	- dd.obj.w and dd.obj.h: size;
	- dd.obj.is_dragged: 1 while item is dragged, else 0;
	- dd.obj.is_resized: 1 while item is resized, i.e. if <ctrl> or <shift> is pressed, else 0

For more properties and details, visit the API documentation
at http://www.walterzorn.com/dragdrop/api_e.htm (english) or
http://www.walterzorn.de/dragdrop/api.htm (german)	*/
function my_PickFunc()
{
	var drag_square;

	if (drag_square = ac.getSquareByCoords(dd.obj.x, dd.obj.y))
	{
		dd.drag_square = drag_square;
		dd.drag_square_piece = dd.obj.name.charAt(0) == 'w' ? dd.obj.name.charAt(1).toUpperCase() : dd.obj.name.charAt(1).toLowerCase();
	}
}




/* my_DragFunc IS CALLED WHILE AN ITEM IS DRAGGED
See the description of my_PickFunc above for what's accessible from here. */
function my_DragFunc()
{

}




/* my_ResizeFunc IS CALLED WHILE AN ITEM IS RESIZED
See the description of my_PickFunc above for what's accessible from here. */
function my_ResizeFunc()
{

}




/* THIS ONE IS CALLED ONCE AN ITEM IS DROPPED
See the description of my_PickFunc for what's accessible from here.
Here may be investigated, for example, what's the name (dd.obj.name)
of the dropped item, and where (dd.obj.x, dd.obj.y) it has been dropped... */
function my_DropFunc()
{
	var drop_square;

	if ((dd.drop_square = ac.getSquareByCoords(dd.obj.x, dd.obj.y)) && ac.getIsPossibleMove(dd.drag_square, dd.drop_square))
	{
		if (dd.drag_square_piece.toUpperCase() == 'P' && (dd.drop_square.charAt(1) == '1' || dd.drop_square.charAt(1) == '8'))
		{ //promotion
			ac.movePiece(dd.drag_square, dd.drop_square);
			ac.requestPromotion();
		}
		else
		{ //move
			ac.move(dd.drag_square, dd.drop_square, false, true, false, 'mymove');
		}
	}
	else
	{
		ac.movePiece(dd.drag_square, dd.drag_square);
	}
}





function ajaxChessExtendedFEN(FEN)
{
	this.toFEN = function()
	{
		var FEN = '';
		for (var row=0; row >= -7; row--)
		{
			for (var col=0;col<=7;col++) FEN += this.board[ac.getSquare('a8',col,row)];
			FEN += '/';
		}

		var empty_space = '________';
		for (var i=8; i>=1; i--)
		{
			var regexp = new RegExp(empty_space, 'g');
			FEN = FEN.replace(regexp, i);
			empty_space = empty_space.substr(0, empty_space.length-1);
		}

		FEN = FEN.substr(0, FEN.length-1) +' '+ this.active_color + ' ';

		var castling = '';

		if (this.castling.white_king_side) castling += 'K';
		if (this.castling.white_queen_side) castling += 'Q';
		if (this.castling.black_king_side) castling += 'k';
		if (this.castling.black_queen_side) castling += 'q';
		FEN += castling != '' ? castling : '-';
		FEN += ' '+ this.en_passant +' '+ this.half_move_clock +' '+ this.full_move_clock +' '+ this.from +' '+ this.to;

		return FEN;
	}

	this.getBlackIsToMove = function()
	{
		return this.active_color == 'b';
	}

	this.getWhiteIsToMove = function()
	{
		return this.active_color == 'w';
	}

	function castling(str)
	{
		this.white_king_side  = str.search(/K/) >= 0 ? true : false;
		this.white_queen_side = str.search(/Q/) >= 0 ? true : false;
		this.black_king_side  = str.search(/k/) >= 0 ? true : false;
		this.black_queen_side = str.search(/q/) >= 0 ? true : false;
	}

	function board(str)
	{
		var empty_fields = '';

		for (var col=1; col<=8; col++)
		{
			empty_fields += '_';
			while (str.search(col) >= 0) str = str.replace(col, empty_fields);
		}

		var rows = str.split('/');

		var board = new Array();
		var piece_counter = new Array();

		for (var row=0; row<=7; row++)
		{
			for (var col=0;col<=7;col++)
			{
				var square = String.fromCharCode(97+col)+(1+row);
				var piece = rows[7-row].charAt(col);

				//setting king position
				if (piece == 'K') board['white_king'] = square;
				if (piece == 'k') board['black_king'] = square;

				board[square] = piece;

				if (!isNaN(piece_counter[piece])) piece_counter[piece]++; else piece_counter[piece] = 0;

				var piece_num = piece_counter[piece] != 0 ? piece_counter[piece] : '';

				var img_id = piece == '_' ? '_' : ac.getPieceColor(piece) + piece.toLowerCase() + piece_num;
				board['img_' + square] = img_id;
			}
		}

		return board;
	}

	var ar_FEN = FEN.split(' ');

	this.board = board(ar_FEN[0]);
	this.white_king = this.board['white_king'];
	this.black_king = this.board['black_king'];

	this.active_color = ar_FEN[1];
	this.last_SAN = '';
	this.promote = false;

	if (!ar_FEN[2]) ar_FEN[2] = '-'; this.castling = new castling(ar_FEN[2]);
	if (!ar_FEN[3]) ar_FEN[3] = '-'; this.en_passant = ar_FEN[3];
	if (!ar_FEN[4]) ar_FEN[4] = '0'; this.half_move_clock = parseInt(ar_FEN[4]);
	if (!ar_FEN[5]) ar_FEN[5] = '1'; this.full_move_clock = parseInt(ar_FEN[5]);
	if (!ar_FEN[6]) ar_FEN[6] = '-'; this.from = ar_FEN[6];
	if (!ar_FEN[7]) ar_FEN[7] = '-'; this.to = ar_FEN[7];

	this.ply = (this.full_move_clock - 1) * 2 + (this.active_color == 'b' ? 1 : 0);
}

function ajaxChessGame()
{
	this.time_clock = new Date().getTime();
	this.time_white = -1;
	this.time_black = -1;
	this.clock_white = -1;
	this.clock_black = -1;
	this.playing_color = false;
	this.is_running = false;
	this.is_rotated = false;
	this.is_built = false;
	this.ply_starts = 0;
	this.play_ends = 0;
	this.sans = new Array();
	this.coords = new Array();
	this.FENs = new Array('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
}

function ajaxChessConfig()
{
	this.side_color = 'aaaaaa';
	this.side_width = 12;
	this.side_line_color = 'ffffff';
	this.coords_color = '000000';
	this.pieceway_color = '3b5998';

	this.square_size = 35;
	this.board_width = 306;

	this.display_coords = true;

	this.light_field = 'f0f0f0';
	this.dark_field = 'bbbbbb';
}

function ajaxChess()
{
	this.setPieces = function(pieces, square_size)
	{
		this.config.pieces = pieces;
		this.config.square_size = square_size;
		this.config.board_width = square_size * 8 + 2 * 13;
		this.config.piece_src = '/st/imgs/board/pieces/'+ this.config.pieces +'/'+ this.config.square_size +'/'+this.config.pieces+this.config.square_size;
	}

	this.getBoardHTML = function()
	{
		var boardHTML = '';

		var side_background = ' style="background-color:#'+ this.config.side_color +'"';
		if (this.config.side_color.search(/[a-f,A-F,0-9]{6}/) == -1) side_background = ' background="/st/imgs/board/bg/'+ this.config.side_color +'.jpg"';

		//begin main board table
		boardHTML += '<table cellspacing="0" cellpadding="0"'+ side_background +'>\n<tr>\n<td align="right">\n'+

		//begin up coords table
		'<img src="clear.gif" width="1" height="'+ this.config.side_width +'">\n'+
		//end up coords table

		//begin left coords, board, right coords table
		'<table border="0" cellpadding="0" cellspacing="0">\n<tr>\n'+

		//begin left coords td
		'<td>';

		if (this.config.display_coords)
		{
			boardHTML += '<table style="width:'+ this.config.side_width +'px;color:#'+ this.config.coords_color +'" cellspacing="0" cellpadding="0" border="0">\n';
			for (var i=8; i>=1; i--)
			{
				boardHTML += '<tr><td id="left_coord_'+ i +'" style="vertical-align:middle;text-align:center;font-size:'+ (this.config.side_width-2) +'px;height:'+ this.config.square_size +'px">'+ i +'</td></tr>\n';
			}
			boardHTML += '</table>\n';
		}

		boardHTML += '</td>\n'+
		//end left coords td

		//begin main board td
		'<td bgcolor="#'+ this.config.side_line_color +'" style="padding:1px">\n'+

		//begin main board table
		'<div id="board"><table style="border-collapse:collapse;height:'+ (this.config.square_size*8) +'px;width:'+ (this.config.square_size*8) +'px" border="0" cellpadding="0" cellspacing="0">\n';
		for (var row=0; row>=-7; row--)
		{
			boardHTML+='<tr>\n';

			for (var col=0; col<=7; col++)
			{
				var bg_color = (col+row)%2 == 0 ? this.config.light_field : this.config.dark_field;

				var square = this.getSquare('a8', col, row);
				var square_piece = this.eFEN.board[square];
				var border_color = square == this.eFEN.from || square == this.eFEN.to ? '00AA00' : bg_color;

				var img = '&nbsp;';

				if (square_piece != '_')
				{
					img = '<img style="position: absolute" name="'+ this.eFEN.board['img_' + square].toLowerCase() +'" width="'+ this.config.square_size +'" height="'+ this.config.square_size +'" src="'+ this.config.piece_src + this.getPieceColor(square_piece) + square_piece.toLowerCase() +'.gif">';
				}

				img = '<div id="'+ square +'" style="border:1px solid #'+ border_color +';width:'+ (this.config.square_size-(dd.ie ? 2 : 2)) +'px;height:'+ (this.config.square_size-(dd.ie ? 2 : 2)) +'px">'+ img +'</div>';
				boardHTML += '<td width="'+ this.config.square_size +'" height="'+ this.config.square_size +'" style="background-color:#'+ bg_color +'">'+ img +'</td>\n';
				//clear board
			}
			boardHTML+='</tr>\n';
		}
		boardHTML += '</table>\n</div>\n'+
		//end main board table

		'</td>\n'+
		//end main board td

		//begin right coords td
		'<td><img src="clear.gif" height="1" width="'+ this.config.side_width +'"></td>\n'+
		//end right coords td
		'</tr>\n</table>\n';
		//end left coords, board, right coords table

		//begin down coords table
		if (this.config.display_coords)
		{
			boardHTML += '<table align="center" width="'+ (this.config.square_size * 8) +'" cellspacing="0" cellpadding="0" border="0" style="color:#'+ this.config.coords_color +'">\n'+
						 '<tr>\n';
			for (var i=1; i<=8; i++)
			boardHTML +=	'<td id="down_coord_'+ i +'" style="font-size:'+ (this.config.side_width-2) +'px;width:'+ this.config.square_size +'px;text-align:center;">'+
								String.fromCharCode(64+i)+
							'</td>\n';
			boardHTML +=
						'</tr>\n'+
						'</table>\n';
		}
		else
		{
			boardHTML += '<img src="clear.gif" width="1" height="'+ this.config.side_width +'" />\n';
		}
		//end down coords table

		//end main board table
		boardHTML += '</td>\n</tr>\n</table>\n';

		return boardHTML;
	}

	this.getPromotionHTML = function()
	{
		return '<img name="wait" style="position:absolute;" src="/imgs/st/board/progress.gif">\n'+
			'<div id="promote">'+
				'<div class="head">Promotion</b></div>'+
				'<div class="content">'+
					'<a href="javascript:void(0);" onclick="ac.promote(\'Q\')"><img id="promote_queen"  border="0" width='+ this.config.square_size +' height='+ this.config.square_size +' src="'+ this.config.piece_src +'wq.gif"></a>'+
					'<a href="javascript:void(0);" onclick="ac.promote(\'R\')"><img id="promote_rook"   border="0" width='+ this.config.square_size +' height='+ this.config.square_size +' src="'+ this.config.piece_src +'wr.gif"></a>'+
					'<a href="javascript:void(0);" onclick="ac.promote(\'B\')"><img id="promote_bishop" border="0" width='+ this.config.square_size +' height='+ this.config.square_size +' src="'+ this.config.piece_src +'wb.gif"></a>'+
					'<a href="javascript:void(0);" onclick="ac.promote(\'N\')"><img id="promote_knight" border="0" width='+ this.config.square_size +' height='+ this.config.square_size +' src="'+ this.config.piece_src +'wn.gif"></a>'+
				'</div>'+
			'</div>';
	}

	this.setDHTML = function()
	{
		SET_DHTML(CURSOR_MOVE, 'ci_board'+NO_DRAG+DETACH_CHILDREN, 'board'+NO_DRAG+DETACH_CHILDREN, 'promote'+NO_DRAG, 'wait'+NO_DRAG, 'bp','br','bn','bb','bq','bk','wr','wn','wb','wq','wk','wp');

		var board = dd.elements.board;

		for (var i=0; i<dd.elements.length; i++)
		{
			if (dd.elements[i].name.length <= 3)
			{
				board.attachChild(dd.elements[i]);
			}
		}

		dd.elements.ci_board.attachChild(board);

		dd.elements.ci_board.hide();

		dd.elements.promote.moveTo(board.x + board.defw/2 - 74, board.y + board.defh/2 - 28);
		dd.elements.promote.hide();

		dd.elements.wait.moveTo(board.x + board.defw/2-8, board.y + board.defh/2 - 8);
		dd.elements.wait.hide();
	}

	this.setDraggable = function(draggable)
	{
		for (var i=0; i<dd.elements.length; i++)
		{
			if (dd.elements[i].name.length <= 3)
			{
				dd.elements[i].setDraggable(draggable);
				dd.elements[i].setZ(1);
			}
		}
	}

	this.promote = function(piece)
	{
		dd.elements.promote.hide();
		this.move(dd.drag_square, dd.drop_square, piece, true, false, 'mymove');
		this.setDraggable(true);
	}

	this.requestPromotion = function()
	{
		var c = this.eFEN.active_color;
		$("promote_queen").src 	= this.config.piece_src + c + 'q.gif';
		$("promote_rook").src 	= this.config.piece_src + c + 'r.gif';
		$("promote_bishop").src = this.config.piece_src + c + 'b.gif';
		$("promote_knight").src = this.config.piece_src + c + 'n.gif';

		var piece;

		if (piece = dd.elements[this.eFEN.board['img_'+ dd.drop_square]])
		{
			piece.hide();
		}

		this.setDraggable(false);

		dd.elements.promote.setZ(2);
		dd.elements.promote.show();
	}

	this.displayFEN = function(FEN)
	{
		this.displayPieceWay('-', '-');
		this.eFEN = new ajaxChessExtendedFEN(FEN);
		this.displayPosition();
	}

	this.highlightMove = function(ply_old, ply)
	{
		if ($('mymove_'+ ply_old))
		{
			$('mymove_'+ ply_old).style.backgroundColor = '#fff';
		}

		if ($('mymove_'+ ply))
		{
			$('mymove_'+ ply).style.backgroundColor = '#ccc';
		}

		this.game.ply = ply;
	}


	this.displayPosition = function(ply)
	{
		this.displayPieceWay('-', '-');

		if (ply >= 0)
		{
			this.eFEN = new ajaxChessExtendedFEN(this.game.FENs[ply]);
			this.highlightMove(this.game.ply, ply);
		}

		if (!dd.elements.board.visible) dd.elements.board.show();
		if (dd.elements.wait.visible) dd.elements.wait.hide();
		if (dd.elements.promote.visible) dd.elements.promote.hide();

		for (var i=0; i<dd.elements.length; i++) if (dd.elements[i].name.length <= 3) dd.elements[i].hide();
		for (var row=0; row<=7; row++) for (var col=0; col<=7; col++)
		{
			var square = this.getSquare('a1', col, row);
			var piece = this.eFEN.board[square];
			var piece_id = this.eFEN.board['img_' + square];

			if (piece != '_')
			{
				if (!dd.elements[piece_id])
				{
					piece_id = this.getPieceColor(piece) + piece.toLowerCase();
					dd.elements[piece_id].copy();
				}

				this.movePiece(square, square);
			}
		}

		this.displayPieceWay(this.eFEN.from, this.eFEN.to);
	}

	this.getSquare = function (square, x, y)
	{
		var col_char_code_num = square.charAt(0).charCodeAt(0) + x;
		if (col_char_code_num >= 97 && col_char_code_num <= 104)
		{
			var col = String.fromCharCode(col_char_code_num);
		}
		else
		{
			return false;
		}

		var row = parseInt(square.charAt(1)) + y;
		if (row < 1 || row > 8) return false;

		return col + row;
	}

	this.getRotatedSquare = function(square)
	{
		if (this.game.is_rotated)
		{
			var col = square.charCodeAt(0) - 97;
			col = String.fromCharCode( 97 + (7-col) );

			var row = parseInt(square.charAt(1)-1);
			row = 8 - row;

			square = col + row;
		}

		return square;
	}

	this.getSquareByCoords = function(x, y)
	{
		//get position relative to board
		x += Math.round(this.config.square_size / 2) - (dd.elements.board.x + 1);
		y += Math.round(this.config.square_size / 2) - (dd.elements.board.y + 1);

		//check if piece on board
		if (x<0 || y<0 || x>this.config.square_size*8 || y>this.config.square_size*8) return false;

		var square = String.fromCharCode(97 + Math.floor(x/this.config.square_size)) + (8-Math.floor(y/this.config.square_size));

		return this.getRotatedSquare(square);
	}


	this.getPieceColor = function(piece)
	{
		return piece.toUpperCase() == piece ? 'w' : 'b';
	}

	this.movePiece = function(from, to, animated)
	{
		var piece = dd.elements[this.eFEN.board['img_' + from]];
		var x = dd.elements.board.x;
		var y = dd.elements.board.y;

		from = this.getRotatedSquare(from);
		to = this.getRotatedSquare(to);

		x += (to.charCodeAt(0)-97) * this.config.square_size;
		y += (8 - parseInt(to.charAt(1))) * this.config.square_size;

		if (animated)
		{
			new Effect.Move(piece.id,
			{
				mode: 'absolute',
				x: x,
				y: y,
				duration: 0.5,
				queue: { position: 'end', scope: 'global' },
				afterFinish: function()
				{
					piece.moveTo(x, y);
				}
			}, piece);
		}
		else
		{
			piece.moveTo(x,y);
		}

		if (!piece.visible) piece.show();
	}

	this.getOccupiableSquare = function(from, to)
	{
		if (!from || !to) return false;

		var from_piece = this.eFEN.board[from];
		var to_piece = this.eFEN.board[to];

		if (to_piece == '_' || to_piece != '_' && this.getPieceColor(from_piece) != this.getPieceColor(to_piece))
		{
			return 'x' + to_piece + to;
		}

		if (to_piece == '_')
		{
			return to;
		}

		return false;
	}

	this.getFieldIsChecked = function(square)
	{
		var king = 'K', queen = 'Q', bishop = 'B', rock ='R' , knight = 'N';

		if (this.eFEN.getBlackIsToMove())
		{
			king = 'k'; queen = 'q'; bishop = 'b'; rock = 'r'; knight = 'n';
		}

		var checked = false;
		piece_old = this.eFEN.board[square];

		if (!checked)
		{ //checked by R or Q?
			this.eFEN.board[square] = rock;
			var squares = this.getPossibleSquares(square, false).join(',');
			if (squares.search(/xr/i) >= 0 || squares.search(/xq/i) >=0) checked = true;
		}

		if (!checked)
		{ //checked by B or Q?
			this.eFEN.board[square] = bishop;
			var squares = this.getPossibleSquares(square, false).join(',');
			if (squares.search(/xb/i) >= 0 || squares.search(/xq/i) >=0) checked = true;
		}

		if (!checked)
		{ //checked by N?
			this.eFEN.board[square] = knight;
			var squares = this.getPossibleSquares(square, false).join(',');
			if (squares.search(/xn/i) >= 0) checked = true;
		}

		if (!checked)
		{ //checked by K?
			this.eFEN.board[square] = king;
			var squares = this.getPossibleSquares(square, false).join(',');
			if (squares.search(/xk/i) >= 0 || squares.search(/xq/i) >=0) checked = true;
		}

		if (!checked)
		{ //checked by P?
			var add_row = this.eFEN.getWhiteIsToMove() ? 1 : -1;
			var piece_square, piece;

			if (piece_square = this.getSquare(square, -1, add_row))
			{
				piece = this.eFEN.board[piece_square];
				if (piece.toUpperCase() == 'P' && this.getPieceColor(piece) != this.eFEN.active_color) checked = true;
			}

			if (piece_square = this.getSquare(square, 1, add_row))
			{
				piece = this.eFEN.board[piece_square];
				if (piece.toUpperCase() == 'P' && this.getPieceColor(piece) != this.eFEN.active_color) checked = true;
			}
		}

		//restore old piece
		this.eFEN.board[square] = piece_old;

		return checked;
	}

	this.filterChecks = function(from, squares)
	{
		var filtered_squares = new Array();

		var king_square = this.eFEN.getWhiteIsToMove() ? this.eFEN.white_king : this.eFEN.black_king;

		for (var i=0; i < squares.length; i++)
		{
			//king moves?
			var king_moves = this.eFEN.board[from].toUpperCase() == 'K' ? true : false;

			//save old board
			var from_piece = this.eFEN.board[from];
			var to = squares[i].search(/x/) >= 0 ? squares[i].substr(2,2) : squares[i];

			var to_piece = this.eFEN.board[to];

			//wrong rochade?
			var rochade_failure = false;
			if (king_moves && (from == 'e1' || from == 'e8') && (to =='g1' || to == 'c1' || to == 'g8' || to == 'c8'))
			{
				if (this.getFieldIsChecked(from))
				{
					rochade_failure = true;
				}
				else
				{
					if (from == 'e1' && to == 'c1' && this.getFieldIsChecked('d1')) rochade_failure = true;
					if (from == 'e1' && to == 'g1' && this.getFieldIsChecked('f1')) rochade_failure = true;
					if (from == 'e8' && to == 'c8' && this.getFieldIsChecked('d8')) rochade_failure = true;
					if (from == 'e8' && to == 'g8' && this.getFieldIsChecked('f8')) rochade_failure = true;
				}
			}

			//en passant move
			var ep_piece = '';
			var ep_square = '';

			if (this.eFEN.en_passant == to && this.eFEN.board[from].toUpperCase() == 'P')
			{
				ep_square = to.charAt(0) + from.charAt(1);
				ep_piece = this.eFEN.board[ep_square];

				this.eFEN.board[ep_square] = '_';
			}

			this.eFEN.board[to] = from_piece;
			this.eFEN.board[from] = '_';

			if (king_moves) king_square = to;

			//king checked after move?
			if (!rochade_failure && !this.getFieldIsChecked(king_square))
			{
				filtered_squares.push(to);
			}

			//restore old position
			this.eFEN.board[to] = to_piece;
			this.eFEN.board[from] = from_piece;

			if (ep_square != '')
			{
				this.eFEN.board[ep_square] = ep_piece;
			}
		}

		return filtered_squares;
	}

	this.getFreeSquares = function(from, x, y)
	{
		var i=1;
		var range_limit = this.eFEN.board[from].toUpperCase() == 'K' ? 1 : 8;
		var squares = new Array();

		while (this.eFEN.board[this.getSquare(from, x*i, y*i)] == '_' && i <= range_limit)
		{
			squares.push(this.getSquare(from, x*i, y*i));
			i++;
		}

		if (i <= range_limit)
		{
			squares.push(this.getOccupiableSquare(from, this.getSquare(from , x*i, y*i)));
		}

		return squares.without(false);
	}

	this.getIsPossibleMove = function(from, to)
	{
		return this.getPossibleSquares(from, true, to).first() == to;
	}

	this.getPossibleSquares = function(from, filter_checks, to)
	{
		var squares = new Array();

		//wrong color
		if (this.eFEN.active_color != this.getPieceColor(this.eFEN.board[from])) return squares;

		//wrong capture
		if (to && this.eFEN.board[to] != '_' && this.eFEN.active_color == this.getPieceColor(this.eFEN.board[to])) return squares;

		var piece = this.eFEN.board[from];
		var p = piece.toUpperCase();

		//pawn move
		if (p == 'P')
		{
			var add_to_row = this.getPieceColor(piece) == 'w' ? 1 : -1;

			var square = this.getSquare(from, 0, add_to_row);
			var square_piece = this.eFEN.board[square];

			if (square_piece == '_')
			{
				squares.push(square);

				square = this.getSquare(from, 0, add_to_row * 2);
				square_piece = this.eFEN.board[square];

				if (square_piece == '_' && (from.charAt(1) == '2' || from.charAt(1) == '7')) squares.push(square);
			}

			//capturing (incl. en passant)
			square = this.getSquare(from, -1, add_to_row);
			square_piece = this.eFEN.board[square];

			if (square_piece != '_')
			{
				squares.push(this.getOccupiableSquare(from, square));
			}
			else
			{
				if (square == this.eFEN.en_passant) squares.push(square);
			}

			square = this.getSquare(from, 1, add_to_row);
			square_piece = this.eFEN.board[square];

			if (square_piece != '_')
			{
				squares.push(this.getOccupiableSquare(from, square));
			}
			else
			{
				if (square == this.eFEN.en_passant) squares.push(square);
			}
		}

		//knight move
		if (p == 'N')
		{
			squares.push(this.getOccupiableSquare(from, this.getSquare(from,  1,  2 )));
			squares.push(this.getOccupiableSquare(from, this.getSquare(from,  2,  1 )));
			squares.push(this.getOccupiableSquare(from, this.getSquare(from,  1, -2 )));
			squares.push(this.getOccupiableSquare(from, this.getSquare(from, -2,  1 )));
			squares.push(this.getOccupiableSquare(from, this.getSquare(from, -2, -1 )));
			squares.push(this.getOccupiableSquare(from, this.getSquare(from, -1, -2 )));
			squares.push(this.getOccupiableSquare(from, this.getSquare(from, -1,  2 )));
			squares.push(this.getOccupiableSquare(from, this.getSquare(from,  2, -1 )));
		}

		// rock & queen & king move
		if (p == 'R' || p == 'Q' || p == 'K')
		{
			squares = squares.concat(this.getFreeSquares(from,  0,  1));
			squares = squares.concat(this.getFreeSquares(from,  1,  0));
			squares = squares.concat(this.getFreeSquares(from,  0, -1));
			squares = squares.concat(this.getFreeSquares(from, -1,  0));
		}

		//bishop & queen & king move
		if ( p == 'B' || p == 'Q' || p == 'K')
		{
			squares = squares.concat(this.getFreeSquares(from,  1,  1));
			squares = squares.concat(this.getFreeSquares(from,  1, -1));
			squares = squares.concat(this.getFreeSquares(from, -1,  1));
			squares = squares.concat(this.getFreeSquares(from, -1, -1));
		}

		//castling
		if (p == 'K')
		{
			if (from == 'e1')
			{
				if (this.eFEN.board['d1'] == '_' && this.eFEN.board['c1'] == '_' && this.eFEN.board['b1'] == '_' && this.eFEN.castling.white_queen_side) squares.push('c1');
				if (this.eFEN.board['f1'] == '_' && this.eFEN.board['g1'] == '_' && this.eFEN.castling.white_king_side) squares.push('g1');
			}

			if (from == 'e8')
			{
				if (this.eFEN.board['d8'] == '_' && this.eFEN.board['c8'] == '_' && this.eFEN.board['b8'] == '_' && this.eFEN.castling.black_queen_side) squares.push('c8');
				if (this.eFEN.board['f8'] == '_' && this.eFEN.board['g8'] == '_' && this.eFEN.castling.black_king_side) squares.push('g8');
			}
		}

		squares = squares.without(false);

		if (to && squares.join(',').search(to) >= 0) squares = new Array(to);

		if (filter_checks && squares.length > 0)
		{
			squares = this.filterChecks(from, squares);
		}

		return squares;
	}

	this.getKingIsChecked = function()
	{
		var king_square = this.eFEN.getWhiteIsToMove() ? this.eFEN.white_king : this.eFEN.black_king;

		return this.getFieldIsChecked(king_square);
	}

	this.displayPieceWay = function(from, to)
	{
		if (this.eFEN.from != '-') $(this.getRotatedSquare(this.eFEN.from)).style.border = 'none';
		if (this.eFEN.to != '-') $(this.getRotatedSquare(this.eFEN.to)).style.border = 'none';
		if (from != '-') $(this.getRotatedSquare(from)).style.border = '1px solid #'+ this.config.pieceway_color;
		if (to != '-') $(this.getRotatedSquare(to)).style.border = '1px solid #'+ this.config.pieceway_color;
	}

	this.move = function(from, to, promote, visible, animated, mode)
	{
		var mymove = mode == 'mymove' && this.game.is_running ? true : false;

		if (mymove)
		{
			var coords = from + to + (promote ? promote.toUpperCase() : '');
			if (this.game.playing_color == this.eFEN.active_color) lc.moveSend(coords);
		}

		if (this.game.is_running && (this.game.playing_color != this.eFEN.active_color))
		{
			if (this.eFEN.getWhiteIsToMove())
			{
				this.game.time_white += this.game.time_add * 1000;
				lc.clockUpdate('w', true);
			}
			else
			{
				this.game.time_black += this.game.time_add * 1000;
				lc.clockUpdate('b', true);
			}
		}

		if (visible)
		{
			this.movePiece(from, to, animated);
			this.displayPieceWay(from, to);
		}

		this.eFEN.from = from;
		this.eFEN.to = to;
		this.eFEN.promote = promote;

		var piece = this.eFEN.board[from];

		if (mymove)
		{
			//getting SAN san

			var SAN = '';
			if (this.eFEN.board[to] != '_') SAN += 'x';

			if (piece.toUpperCase() != 'P' && piece.toUpperCase() != 'K') for (var row=0; row<=7; row++) for (var col=0; col<=7; col++)
			{ // may same piece type enter this square?
				var from_temp = this.getSquare('a1', col, row);

				if (this.eFEN.board[square] == piece && from_temp != square)
				{
					if (this.isPossibleMove(from_temp, to) >= 0)
					{
						if (from_temp.charAt(0) == from_temp.charAt(0))
						{
							SAN = from_temp.charAt(1) + SAN;
						}
						else
						{
							SAN = from_temp.charAt(0) + SAN;
						}

						break;
					}
				}
			}

			if (piece.toUpperCase() == 'P')
			{
				if (SAN == 'x')
				{
					SAN = from.charAt(0) + SAN;
				}
			}
			else
			{
				SAN = piece.toUpperCase() + SAN;
			}

			SAN += to;
		}

		//halfmove clock
		if (piece.toUpperCase() == 'P' || this.eFEN.board[to] != '_')
		{
			this.eFEN.half_move_clock = 0;
		}
		else
		{
			this.eFEN.half_move_clock++;
		}

		//move_num
		var num = this.eFEN.full_move_clock + (this.eFEN.getWhiteIsToMove() ? '.' : '...');

		//ply
		this.eFEN.ply++;

		//switching colors , setting full move clock
		if (this.eFEN.getWhiteIsToMove())
		{
			this.eFEN.active_color = 'b';
		}
		else
		{
			this.eFEN.active_color = 'w';
			this.eFEN.full_move_clock++;
		}

		//hiding captured piece
		var img_id = this.eFEN.board['img_' + to];
		if (img_id != '_')
		{
			if (visible) dd.elements[img_id].hide();
		}

		//check for pawn promotion
		if (promote)
		{
			promote = this.eFEN.getWhiteIsToMove() ? promote.toLowerCase() : promote.toUpperCase();

			if (visible)
			{
				var id = this.getPieceColor(promote) + promote.toLowerCase();
				var p, img_id;

				for(var i=0; i<10; i++)
				{
					img_id = id + (i == 0 ? '' : i);
					if (p = dd.elements[img_id])
					{
						if (!p.visible)
						{
							break;
						}
					}
					else
					{
						dd.elements[id].copy();
						break;
					}
				}

				dd.elements[this.eFEN.board['img_' + from]].hide();

				this.eFEN.board['img_'+ from] = img_id;
			}

			this.eFEN.board[from] = promote;

			if (visible) this.movePiece(from, to, animated);
			if (mymove) SAN += '=' + promote.toUpperCase();
		}

		//update board
		this.eFEN.board[to] = this.eFEN.board[from];
		this.eFEN.board['img_' + to] = this.eFEN.board['img_' + from];
		this.eFEN.board[from] = '_';
		this.eFEN.board['img_' + from] = '_';

		//clear pawn if captured en passant
		if (this.eFEN.en_passant == to && this.eFEN.board[to].toUpperCase() == 'P')
		{
			var en_passant_square = to.charAt(0) + from.charAt(1);
			this.eFEN.board[en_passant_square] = '_';

			if (visible) dd.elements[this.eFEN.board['img_' + en_passant_square]].hide();
			this.eFEN.board['img_' + en_passant_square]='_';
		}

		//activate en passant
		this.eFEN.en_passant = '-';
		if (this.eFEN.board[to].toUpperCase() == 'P' && Math.abs(parseInt(to.charAt(1)) - parseInt(from.charAt(1))) == 2)
		{
			this.eFEN.en_passant = from.charAt(0) + (parseInt(from.charAt(1)) == 2 ? 3 : 6);
		}

		//SAN rock after castling
		var p = this.eFEN.board[to].toUpperCase();

		if (p == 'K')
		{
			if (from == 'e1' && to == 'g1')
			{ //white king side
				if (visible) this.movePiece('h1', 'f1', animated);

				SAN = 'O-O';
				this.eFEN.board['f1'] = 'R';
				this.eFEN.board['img_f1'] = this.eFEN.board['img_h1'];
				this.eFEN.board['h1'] = '_';
				this.eFEN.board['img_h1'] = '_';
			}

			if (from == 'e1' && to == 'c1')
			{ //white queen side
				if (visible) this.movePiece('a1', 'd1', animated);

				SAN = 'O-O-O';
				this.eFEN.board['d1'] = 'R';
				this.eFEN.board['img_d1'] = this.eFEN.board['img_a1'];
				this.eFEN.board['a1'] = '_';
				this.eFEN.board['img_a1'] = '_';
			}

			if (from == 'e8' && to == 'g8')
			{ //black king side
				if (visible) this.movePiece('h8', 'f8', animated);

				SAN='O-O';
				this.eFEN.board['f8'] = 'r';
				this.eFEN.board['img_f8'] = this.eFEN.board['img_h8'];
				this.eFEN.board['h8'] = '_';
				this.eFEN.board['img_h8'] = '_';
			}

			if (from == 'e8' && to == 'c8')
			{ //black queen side
				if (visible) this.movePiece('a8', 'd8', animated);

				SAN='O-O-O';
				this.eFEN.board['d8'] = 'r';
				this.eFEN.board['img_d8'] = this.eFEN.board['img_a8'];
				this.eFEN.board['a8'] = '_';
				this.eFEN.board['img_a8'] = '_';
			}

			//changing king position
			this.eFEN.getWhiteIsToMove() ? this.eFEN.black_king = to : this.eFEN.white_king = to;
		}

		//forbid castling
		if (from == 'e1')
		{
			this.eFEN.castling.white_king_side = false;
			this.eFEN.castling.white_queen_side = false;
		}

		if (from == 'e8')
		{
			this.eFEN.castling.black_king_side = false;
			this.eFEN.castling.black_queen_side = false;
		}

		if (from == 'a1' || to == 'a1') this.eFEN.castling.white_queen_side = false;
		if (from == 'h1' || to == 'h1') this.eFEN.castling.white_king_side = false;
		if (from == 'a8' || to == 'a8') this.eFEN.castling.black_queen_side = false;
		if (from == 'h8' || to == 'h8') this.eFEN.castling.black_king_side = false;

		if (mymove)
		{
			//new position SAN check
			if (this.getKingIsChecked())
			{
				var check_sign = '#';

				for (var row=0; row<=7; row++) for (var col=0; col<=7; col++)
				{
					var square = this.getSquare('a1', col, row);
					var square_piece = this.eFEN.board[square];

					if (this.getPieceColor(square_piece) == this.eFEN.active_color)
					{
						if (this.getPossibleSquares(square, true).length > 0)
						{
							check_sign = '+';
							row = 8;
							col = 8;
						}
					}
				}

				SAN += check_sign;
			}

			this.eFEN.last_SAN = SAN;

			this.game.coords[this.eFEN.ply] = coords;
			this.game.sans[this.eFEN.ply] = SAN;
			this.game.FENs[this.eFEN.ply] = this.eFEN.toFEN();
			this.game.ply_ends = this.eFEN.ply;
		}

		if (this.game.is_running)
		{
			lc.clockSetActive(this.eFEN.active_color);
		}
	}

	this.rotateBoard = function()
	{
		this.displayPieceWay('-', '-');

		this.game.is_rotated = !this.game.is_rotated;

		//switching coords...
		var add = !this.game.is_rotated ? 0 : 9;
		for (var i=1; i<=8; i++)
		{
			var line = Math.abs(add-i);
			$('left_coord_' +i).innerHTML = line;
			$('down_coord_' +i).innerHTML = String.fromCharCode(64+line);
		}

		this.setGameInfos();
		this.displayPosition();
	}

	this.getSpokenResult = function(result, result_cause)
	{
		var text;

		switch(result)
		{
			case 1: text = '1-0 '; break;
			case 2: text = '0-1 '; break;
			default: text = '&frac12;-&frac12; ';
		}

		if (result_cause == 0 && result != -1) if (result == 0) return text += 'draw accepted'; else text += 'resigned';
		if (result_cause == 1) text += 'time';
		if (result_cause == 2) text += 'mate';
		if (result_cause == 3) text += 'stale mate';
		if (result_cause == 4) text += 'not sufficient material';
		if (result_cause == 5) text += 'threefold repetition';
		if (result_cause == 6) text += '50 moves rule';
		if (result_cause == 7) text += 'referee decision';

		return text;
	}

	this.movesBuild = function()
	{
		if (!this.game.is_built)
		{
			this.game.is_built = true;
			this.game.sans.compact().each(function(san, i)
			{
				if (i%2==0)
				{
					$('move_container').appendChild(new Element('div', {'class' : 'separator'}));
					var node = new Element('div', {'class': 'movenum'});
					node.update((Math.floor(i/2)+1) + '.');
					$('move_container').appendChild(node);
				}

				var node = new Element('div', {'id': 'mymove_'+ (i+1), 'name': (i+1), 'class': 'move'}).observe('click', function(event) {
					ac.displayPosition(Event.element(event).getAttribute('name'));
				});

				node.update(san);
				$('move_container').appendChild(node);
			});

			var result = this.getSpokenResult(this.game.result, this.game.result_cause);
			var node = new Element('div', {'class': 'result'});
			node.update(result);
			$('move_container').appendChild(node);
			$('tab_game').show();
		}
	}

	this.addMove = function(san, coords)
	{
		var from = coords.substring(0,2);
		var to = coords.substring(2,4);
		var promote = coords.length > 4 ? coords.charAt(4) : false;

		this.move(from, to, promote);

		this.game.sans[this.eFEN.ply] = san;
		this.game.coords[this.eFEN.ply] = coords;
		this.game.FENs[this.eFEN.ply] = this.eFEN.toFEN();
	}

	this.moveAnimated = function(coords, mode)
	{
		var from = coords.substring(0,2);
		var to = coords.substring(2,4);
		var promote = coords.length > 4 ? coords.charAt(4) : false;
		this.move(from, to, promote, true, true, mode);
	}

	this.displayMove = function(action)
	{
		var coords;

		switch(action)
		{
			case 'first':
				this.displayPosition(this.game.ply_starts);

				break;

			case 'previous':
				if (this.eFEN.ply-1 >= this.game.ply_starts)
				{
					this.highlightMove(this.eFEN.ply, this.eFEN.ply-1);
					this.displayPosition(this.eFEN.ply-1);
				}

				break;

			case 'next':
				if (coords = this.game.coords[this.eFEN.ply+1])
				{
					this.highlightMove(this.eFEN.ply, this.eFEN.ply+1);
					this.moveAnimated(coords);
				}
				else if (this.game.animated)
				{
					this.animateGame();
				}

				break;

			case 'last':
				this.displayPosition(this.game.ply_ends);

				break;
		}
	}

	this.animateGame = function()
	{
		if (this.game.animated)
		{
			this.game.animated.stop();
			this.game.animated = false;
		}
		else
		{
			this.game.animated = new PeriodicalExecuter(function() {
				ac.displayMove('next');
			}, 1);
		}
	}
}

var ac = new ajaxChess();
ac.eFEN = new ajaxChessExtendedFEN('rnbqk3/p7/8/8/8/8/P7/RNBQK3');
ac.config = new ajaxChessConfig();
ac.game = new ajaxChessGame();
ac.setPieces('alpha', screen.width > 1024 ? 35 : 25);
