% Equates in XGod.equ
% These constants are passed into Xboard_t to specify highlight levels
% highlight_level_t = int

% highlight_none = 0
% highlight_move_from = 1
% highlight_move_to = 2

Xboard_t = cluster is create, get_size, reset, set_piece, find_place, make_move
    
    % Overview: The Xboard_t date type is used to represent the visual
    %           aspects of the anti-chess board itself to the user.
    %           It maintains all the bitmaps and other visual
    %           elements, such as the row and column labels.  Any
    %           "fancy" features, such as shadows and highlights will
    %           be transparently handled by Xboard_t.  All procedures
    %           that signals file_not_found leave the Xboard in an
    %           inderterminant state.
    
    % Representation

    rep = record[w: window, bd: bd_t, piece_width, piece_height: pixel,
		 file_prefix: string, x0, y0: pixel]
    bd_t = array[apc_t]
    apc_t = array[pc_t]
    pc_t = record[wi: window_item, fg, bg: color, filename: string]

    % The representation stores the window to which updates should be
    % made, the prefix to be added to bitmap names, and a
    % two-dimensional array to represent what pieces are where on the
    % board.  Each piece stores the window_item to represent it, the
    % foreground & background for the piece, and the filename of the
    % piece.

    % Representation Invariants

    % * W is a window with a visual representation of an anti-chess
    %   board (after Xboard_t$create returns).  This is the window
    %   passed into Xboard_t$create.
    % * File_prefix is a string representing the path to the bitmaps,
    %   and any prefix characters to add to the beginning of the
    %   bitmap filenames.  The value is passed into Xboard_t$create.
    % * (x0, y0) is the upper-left corner of the playing board, i.e.,
    %   the upper-left corner of "a8".
    % * piece_width and piece_height are the values passed into
    %   Xboard_t$create.
    % * bd is a two-dimensional array, all dimensions 1 through 8.
    % * In pc_t, wi is the window_item (bitmap, rectangle, etc.) for
    %   the square on the board.  Filename represents the name of the
    %   file in the window_item, and fg and bg are the foreground and
    %   colors of window_item.  If filename is an empty string, the
    %   square is just a rectangle and not a bitmap.

    % Abstraction function

    % * The window to display in is "w".
    % * x0, y0, piece_width, and piece_height together determine piece
    %   position:
    %      (x0 + ((piece_width + 2*ShadowWidth) * (col - 1)),
    %       y0 + ((piece_height + 2*ShadowHeight) * (row - 1)))
    %   is the upper-left corner of the piece at col, row.  Note that
    %   the piece_width and piece_height are adjusted for any 3D
    %   effects around the bitmaps.
    % * The structure in bd[row][col] should specify the window_item
    %   at col, row, its foreground and background colors, and the
    %   filename of the bitmap displayed there.  If the filename is
    %   the empty string, then the window_item represents an
    %   unoccupied square, and is hence, merely a rectangle.
    
    % Random equates:
    label_width = 40     % Width of the labels
    label_height = 40    % Height of the labels
    
    extra_width = 5      % Space around the board
    extra_height = 5     % Space around the board
    
    letter_width = 10    % Approximate width of a letter
    letter_height = 18   % Approximate height of a letter
    
    % Colors

    % Colors are stored in an own variable called "color_scheme", with
    %   the following structure:
    
    Xboard_color_scheme_t = record[background: three_d_color_t, 
				   squares: color_array_t[three_d_color_t], 
				   highlight_backgrounds: array[color_array_t[color]], 
				   label_color: color,
				   pieces: color_array_t[color]]
    
    % Abbreviations for initialization
    ca = color_array_t[td]
    cs = Xboard_color_scheme_t
    td = three_d_color_t
    acb = array[cac]
    cac = color_array_t[color]
    
    cac_cwb = cac$create_white_black
    ca_cwbn = ca$create_white_black_none
    
%    own color_scheme: cs := 
%    cs${background: td$create("gray50", "gray75", "Black"),
%	squares: ca_cwbn(td$create("Yellow", "LightYellow", "Brown"),
%			 td$create("Red", "Pink", "IndianRed"),
%			 td$create("gray50", "gray75", "Black")),
%	highlight_backgrounds: acb$[0: cac_cwb("Yellow", "Red"),
%				    cac_cwb("gray50", "gray50"),
%				    cac_cwb("gray75", "gray25")],
%	label_color: "White",
%	pieces: cac_cwb("White", "Black")}
    own color_scheme: cs := 
    cs${background: td$create("gray50", "gray75", "Black"),
	squares: ca_cwbn(td$create("goldenrod", "LightYellow", "Brown"),
			 td$create("DarkOrange4", "Pink", "IndianRed"),
			 td$create("gray50", "gray75", "Black")),
	highlight_backgrounds: acb$[0: cac_cwb("goldenrod", "DarkOrange4"),
				    cac_cwb("gray50", "gray50"),
				    cac_cwb("gray75", "gray25")],
	label_color: "White",
	pieces: cac_cwb("White", "Black")}
	
    % Color Representation Invariants

    % * Squares has entries for black, white, and none.
    % * Highlight_backgrounds has color_array_t entries for black and
    %   white, and the array has entries for all highlight_level
    %   indexes.
    % * highlight_background[highlight_none][c] =
    %   squares[c].background for c=black, white.
    % * Pieces has entries for black and white.

    % Color Abstraction Function

    % * An inset rectangle that surroundeds the Xboard_t; its colors
    %   are given by "background"
    % * Squares on the board are raised retangles with colors give by
    %   "squares".  The "tabs" on the left and bottom or given by the
    %   "squares" "none" values.
    % * Highlights are formed by changing the background color of a
    %   square to the value in
    %   highlight_background[highlight_level][square_color].
    % * Piece colors are determined from "pieces".
    
    create = proc(w: window, board: board_t, piece_width, piece_height: pixel,
		  filename_prefix: string, x0, y0: Pixel) 
	returns (cvt, pixel, pixel) signals (not_possible, bad_color, 
					     file_not_found)
	% requires: x0, y0 are valid coordinates in window w.
	%           Piece_width and piece_height are the width &
	%           height of the bitmaps whose filename is
	%           filename_prefix || "ol_?.bm" and "s_?.bm".
	% modifies: w
	% effects: Creates a new Xboard_t for window "w", creating all
	%           the bitmaps, rectangles, etc., for the board, and
	%           returning the new Xboard_t, together with the
	%           total width & height of all the user interface
	%           objects for the Xboard_t.  Signals not_possible if
	%           errors occur while creating the interface.
	%           Signals bad_color if an invalid color name is used
	%           within Xboard_t.  Signals leave the window in an
	%           indeterminant state.  The width and height should
	%           agree with board$get_size for the same piece_width
	%           and piece_height.
	
	% Create the basic rep
	r: rep := rep${w: w,
		       bd: bd_t$create(1),
		       piece_width: piece_width + 2*three_d_bevel_width,
		       piece_height: piece_height + 2*three_d_bevel_width,
		       file_prefix: filename_prefix,
		       x0: x0 + 3 * three_d_bevel_width + label_width + extra_width,
		       y0: y0 + three_d_bevel_width + extra_height}
	% Create the inset background
	width, height: int := get_size(piece_width, piece_height)
	three_d_color_t$make_rectangle(w, color_scheme.background, false,
				       x0, y0, x0 + width - 1, y0 + height - 1)
	    resignal bad_color
	% Create the board labels
	% First the row labels
	for row: int in int$from_to(1, 8) do
	    % Compute coordinates
	    x1: pixel := x0 + extra_width
	    x2: pixel := x1 + three_d_bevel_width + label_width
	    y1: pixel := y0 + r.piece_height * (row - 1) + extra_height
	    y2: pixel := y1 + r.piece_height - three_d_bevel_width
	    % Create the beveled rectangle
	    three_d_color_t$make_rectangle(w,
					   color_scheme.squares[color_t$make_none()],
					   true,
					   x1, y1, x2, y2)
		resignal bad_color, file_not_found
	    % Put the text in the rectangle
	    text: string := int$unparse(9-row)
	    window$create_text(w, text, (x2 + x1 - letter_width)/2,
			       (y2 + y1 - letter_height)/2,
			       color_scheme.label_color)
		resignal bad_color
	  end % for
	% Then the column labels
	for col: int in int$from_to(1, 8) do
	    % Compute coordinates
	    x1: pixel := r.x0 - three_d_bevel_width + r.piece_width * (col - 1)
	    x2: pixel := x1 + r.piece_width - three_d_bevel_width
	    y1: pixel := y0 + r.piece_height * 8 + extra_height
	    y2: pixel := y1 + label_height - three_d_bevel_width
	    % Create the beveled rectangle
	    three_d_color_t$make_rectangle(w,
					   color_scheme.squares[color_t$make_none()],
					   true,
					   x1, y1, x2, y2)
		resignal bad_color, file_not_found
	    % Put the text in the rectangle
	    text: string := string$c2s(char$i2c(col - 1 + char$c2i('a')))
	    window$create_text(w, text, (x2 + x1 - letter_width)/2,
			       (y2 + y1 - letter_height)/2,
			       color_scheme.label_color)
		resignal bad_color
	  end % for
	% Create the board interface elements & fill out the array
	for row: int in int$from_to(1, 8) do
	    % Add a row to the rep
	    bd_row: apc_t := apc_t$create(1)
	    bd_t$addh(r.bd, bd_row)
	    for col: int in int$from_to(1, 8) do
		% Compute bevel coordinates
		x1: pixel := r.x0 - three_d_bevel_width + r.piece_width*(col-1)
		x2: pixel := x1 + r.piece_width - three_d_bevel_width
		y1: pixel := r.y0 - three_d_bevel_width + r.piece_height*(8-row)
		y2: pixel := y1 + r.piece_height - three_d_bevel_width
		% Create bevels
		pl: place_t := place_t$create(row, col)
		sq: color_t := get_square_color(pl)
		three_d_color_t$make_highlight_lines(w,
						     color_scheme.squares[sq],
						     true, x1, y1, x2, y2)
		    resignal bad_color
		% Create the piece bitmap/rectangle and put it in the rep
		wi: window_item
		filename: string
		x, y: pixel
		fg, bg: color
		wi, filename, x, y, fg, bg := create_bitmap(up(r), pl,
							    board[pl],
							    highlight_none)
		    resignal bad_color, file_not_found
		pc: pc_t := pc_t${wi: wi, fg: fg, bg: bg, filename: filename}
		% Add the piece
		apc_t$addh(bd_row, pc)
	      end % for
	  end % for
	% Return the resulting rep
	return (r, width, height)
      end create
    
    get_size = proc(piece_width, piece_height: pixel) returns (pixel, pixel)
	% requires: Piece_width and piece_height are the width and
	%           height of the bitmaps to be used for an Xboard_t.
	% effects: Returns the total width and height of an Xboard_t
	%           with the given bitmap sizes.
	return (piece_width * 8 + three_d_bevel_width * 18 + label_width +
		2 * extra_width,
		piece_height * 8 + three_d_bevel_width * 18 + label_height +
		2 * extra_height)
      end get_size
      
    reset = proc(Xboard: Xboard_t, board: board_t) 
	signals (bad_color, file_not_found)
	% modifies: Xboard, window "w" passed into Xboard$create.
	% effects: Updates the board to reflect "board".  Signals
	%           bad_color if an error occurs reading or creating a
	%           bitmap; the state of the window is not guaranteed
	%           in this case.
	
	for r: int in int$from_to(1, 8) do
	    for c: int in int$from_to(1, 8) do
		pl: place_t := place_t$create(r, c)
		set_piece(Xboard, pl, board[pl], highlight_none)
	      end % for
	  end % for
	
      end reset
	
    set_piece = proc(Xboard: Xboard_t, place: place_t, piece: piece_t,
		     highlight_level: highlight_level_t) 
	signals (bad_color, file_not_found)
	% modifies: Xboard, window "w" passed into Xboard$create
	% effects: Replaces the bitmap (or other window_item) at
	%           "place" with "piece", using the appropriate
	%           background for the square and "highlight_level".
	%           Signals bad_color if the Xboard's internal colors
	%           are invalid.
	
	r: rep := down(Xboard)
	
	% Get bitmap info for the new piece
	filename: string
	x, y: pixel
	fg, bg: color
	
	filename, x, y, fg, bg := get_bitmap_info(Xboard, place, piece,
						  highlight_level)
	
	% Check the bitmap file
	pc: pc_t := r.bd[place.row][place.col]
	if pc.filename = filename then
	    % Same file: just adjust colors (if necessary).
	    if pc.fg ~= fg then
		window$set_item_fgcolor(r.w, pc.wi, fg)
		    resignal bad_color
	      end % if
	    if pc.bg ~= bg then
		window$set_item_bgcolor(r.w, pc.wi, bg)
		    resignal bad_color
	      end % if
	  else
	    wi: window_item
	    wi, filename, x, y, fg, bg := create_bitmap(Xboard, place,
							piece,
							highlight_level)
		resignal bad_color, file_not_found
	    window$destroy_item(r.w, pc.wi)
	    pc.wi := wi
	  end % if
	
	% Save the attributes of the window_item to the pc.  (wi is
	%   already up-to-date)
	
	pc.filename := filename
	pc.fg := fg
	pc.bg := bg
	
      end set_piece
    
    find_place = proc(Xboard: cvt, x, y: pixel) returns (place_t)
	signals (no_place)
	% effects: Returns the "place" that contains (x, y), or
	%           signals no_place if (x, y) is not on the board.
	
	x := x - Xboard.x0 + three_d_bevel_width
	y := y - Xboard.y0 + three_d_bevel_width
	
	% Check for off of top or left...
	if (x < 0 cor y < 0) then
	    signal no_place
	  end % if
	
	% Get place location
	x := x / Xboard.piece_width
	y := y / Xboard.piece_height
	
	% Check for off of bottom or right...
	if (x >= 8 cor y >= 8) then
	    signal no_place
	  end % if
	
	% On the board: make a place_t for the place
	
	return (place_t$create((7-y)+1, x+1))
	
      end find_place
    
    make_move = proc(Xboard: Xboard_t, place_from, place_to: place_t, 
		     piece_from, piece_to: piece_t)
	signals (bad_color, file_not_found)
	% modifies: Xboard, window "w" passed into Xboard$create.
	% effects: Moves a piece from place_from to place_to; the
	%           piece starts the motion as a piece_from piece, and
	%           arrives as a piece_to piece.  Place_from is left
	%           vacant, and the piece in place_to is replaced by
	%           piece_to.  Signals bad_color if any internal
	%           colors are invalid.
	
	  begin
	    set_piece(Xboard, place_from, 
		      piece_t$create(none, color_t$make_none()),
		      highlight_none)
	    set_piece(Xboard, place_to, piece_to, highlight_none)
	  end % begin
	    resignal bad_color, file_not_found
	
      end make_move
    
    % Internal procedures

    get_bitmap_info = proc(Xboard: cvt, place: place_t, 
			   piece: piece_t, highlight_level: highlight_level_t)
	returns (string, pixel, pixel, color, color)
	% effects: Returns the name of the bitmap, or the empty string
	%           for a rectangle, its coordinates, and the
	%           foreground and background colors to represent
	%           "piece" at "place".  Does no creation, and does
	%           not depend on the current state of the displayed
	%           board.
	square_color: color_t := get_square_color(place)
	x, y: pixel := get_place_location(up(Xboard), place)
	fg: color
	bg: color := color_scheme.highlight_backgrounds[highlight_level][square_color]
	filename: string
	if color_t$is_none(piece.color) then
	    fg := bg
	    filename := ""
	  else
	    fg := color_scheme.pieces[piece.color]
	    filename := string$c2s(piece_t$unparse(piece_t$create(piece.piecetype, color_t$make_black())))
	    if (fg = bg) then
		% Make a rudimentary effort to fix up color-on-same-color
		if (fg = "Black") then
		    fg := "Black"
		  else
		    fg := "White"
		  end % if
		filename := "ol_" || filename
	      else
		filename := "s_" || filename
	      end % if
	    filename := Xboard.file_prefix || filename || ".bm"
	  end % if
	return (filename, x, y, fg, bg)
      end get_bitmap_info
    
    create_bitmap = proc(Xboard: Xboard_t, place: place_t, 
			 piece: piece_t, highlight_level: highlight_level_t)
	returns (window_item, string, pixel, pixel, color, color)
	signals (bad_color, file_not_found)
	% modifies: Window "w" passed into Xboard$create
	% effects: Creates and returns a bitmap for "piece" at "place"
	%           with highlight "highlight_level".  Does not
	%           destroy the bitmap underneath and does not keep
	%           any knowledge of having created the bitmap; the
	%           ownership of the bitmap is transferred entirely to
	%           the caller of this procedure.  Note that a
	%           rectangle may be returned.  Signals bad_color as
	%           appropriate, not changing the state of "w" if
	%           anything is signalled.  Also returns the
	%           coordinates of the upper-left corner of the bitmap
	%           and the foreground and background colors.
	filename: string
	x, y: pixel
	fg, bg: color
	filename, x, y, fg, bg := get_bitmap_info(Xboard, place, piece,
						  highlight_level)
	wi: window_item
	if filename = "" then
	    wi := window$create_rectangle(down(Xboard).w, x, y,
					  x + down(Xboard).piece_width -
					  2*three_d_bevel_width - 1,
					  y + down(Xboard).piece_height -
					  2*three_d_bevel_width - 1,
					  bg, bg)
		resignal bad_color
	  else
	    wi := window$create_bitmap(down(Xboard).w, filename, x, y, fg, bg)
		resignal bad_color
	     except when bad_file:
		signal file_not_found
	      end % except
	  end % if
	return (wi, filename, x, y, fg, bg)
      end create_bitmap
    
    get_square_color = proc(place: place_t) returns (color_t)
	% effects: Returns the background color of the square at place.
	if int$mod(place.row + place.col, 2) = 1 then
	    return (color_t$make_white())
	  else
	    return (color_t$make_black())
	  end % if
      end get_square_color
    
    get_place_location = proc(Xboard: cvt, place: place_t)
	returns (pixel, pixel)
	% effects: Returns the upper-left corner coordinates of the
	%           bitmap for "place".
	
	x: pixel := Xboard.x0 + Xboard.piece_width * (place.col - 1)
	y: pixel := Xboard.y0 + Xboard.piece_height * (8 - place.row)
	
	return (x, y)
	
      end get_place_location
    
  end Xboard_t
