///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//         This example code is from the book:
//
//           Object-Oriented Programming with C++ and OSF/Motif, 2nd Edition
//         by
//           Douglas Young
//           Prentice Hall, 1995
//           ISBN 0-13-20925507
//
//         Copyright 1995 by Prentice Hall
//         All Rights Reserved
//
//  Permission to use, copy, modify, and distribute this software for 
//  any purpose except publication and without fee is hereby granted, provided 
//  that the above copyright notice appear in all copies of the software.
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////
// GameBoard.C: A tic-tac-toe board
////////////////////////////////////////////////////////////
#include "TicTacToe.h"
#include "GameBoard.h"
#include "Engine.h"
#include <Xm/RowColumn.h>
#include <Xm/DrawnB.h>

GameBoard::GameBoard ( const char *name, 
		       Widget      parent,
		       TicTacToe  *game ) : UIComponent ( name )
{
    int       i;
    XGCValues values;
    
    _game     = game;	
    _gridSize = 100;
    
    // Create an XmRowColumn widget to manage a 3 X 3 
    // grid of widgets
    
    _w = XtVaCreateWidget ( name, xmRowColumnWidgetClass, parent, 
			    XmNnumColumns, 3,
			    XmNpacking,    XmPACK_COLUMN,
			    XmNorientation, XmHORIZONTAL,
			    XmNadjustLast, FALSE,
			    NULL );
    installDestroyHandler();
    
    // Create a grid of 9 XmDrawnButton widgets
    // Store the index of each widgetÕs position in the grid
    // so it can be used to identify the widgetÕs position later
    
    for ( i = 0; i < 9; i++ )
    {
	_grid[i] = XtVaCreateWidget ( "xo", 
				      xmDrawnButtonWidgetClass, _w, 
				      XmNuserData,          i,
				      XmNrecomputeSize,     FALSE,
				      XmNpushButtonEnabled, TRUE,
				      XmNshadowType,  XmSHADOW_OUT,
				      XmNwidth,       _gridSize,
				      XmNheight,      _gridSize,
				      NULL );
	
	// Get user input to mark a square
      	
	XtAddCallback ( _grid[i], 
			XmNactivateCallback, 
			&GameBoard::markCallback, 
			( XtPointer ) this );
    }
    
    XtManageChildren ( _grid, 9 );
    
    // Get the background color of the rowcolumn widget,
    // to be used to effectively shut off highlight-on-enter
    
    XtVaGetValues ( _w, XmNbackground, &_noHighlightColor, NULL );
    
    // Get the GC needed to display the Xs and Os
    // and retrieve and save the normal highlight color
    // Use the color of the first widget in the grid
    
    XtVaGetValues ( _grid[0], XmNforeground,     &values.foreground,
		    XmNhighlightColor, &_highlightColor,
		    XmNshadowThickness, &_shadowThickness,
		    NULL );
    
    _gc = XtGetGC ( _grid[0], GCForeground, &values );
}

GameBoard::~GameBoard()
{
    if ( _w != NULL )
	XtReleaseGC ( _w, _gc );
}

void GameBoard::markX ( int position )
{
    // Remove any previous callbacks, add one to draw 
    // an X and then trigger an Expose event to
    // display the X in this square
    
    XtRemoveAllCallbacks ( _grid[position], XmNexposeCallback );
    
    deactivateSquare ( position ); 
    
    XtAddCallback ( _grid[position], 
		    XmNexposeCallback, 
		    &GameBoard::drawXCallback, 
		    ( XtPointer ) this );
    
    if ( XtIsRealized ( _grid[position] ) )
	XClearArea ( XtDisplay ( _grid[position] ), 
		     XtWindow ( _grid[position] ), 
		     0, 0, 0, 0, TRUE );
}

void GameBoard::markO ( int position )
{
    // Remove any previous callbacks, add one to draw
    // an O and then trigger an Expose event to
    // display the O in this square
    
    XtRemoveAllCallbacks ( _grid[position], XmNexposeCallback );
    
    deactivateSquare ( position ); 
    
    XtAddCallback ( _grid[position], 
		    XmNexposeCallback, 
		    &GameBoard::drawOCallback, 
		    ( XtPointer ) this );	
    
    if ( XtIsRealized ( _grid[position] ) )
	XClearArea ( XtDisplay ( _grid[position] ), 
		     XtWindow ( _grid[position] ), 
		     0, 0, 0, 0, TRUE );
}

void GameBoard::drawXCallback ( Widget    w, 
				XtPointer clientData, 
				XtPointer )
{
    // Retrieve the GameBoard object
    
    GameBoard *obj = ( GameBoard * ) clientData;
    obj->drawX ( w );
}
void GameBoard::drawOCallback ( Widget    w, 
				XtPointer clientData, 
				XtPointer )
{
    GameBoard *obj = ( GameBoard * ) clientData;
    obj->drawO ( w );
}
void GameBoard::drawX ( Widget square )
{
    // Draw the X across the widget
    
    int left   = ( int ) ( 0.2 * _gridSize );
    int top    = ( int ) ( 0.2 * _gridSize );
    int right  = ( int ) ( 0.8 * _gridSize );
    int bottom = ( int ) ( 0.8 * _gridSize );
    
    XDrawLine ( XtDisplay ( square ), XtWindow ( square ), 
		_gc, left, top, right, bottom );
    XDrawLine ( XtDisplay ( square ), XtWindow ( square ), 
		_gc, right, top, left, bottom );
}
void GameBoard::drawO ( Widget square )
{
    // Draw a circle that occupies 80% of the widget
    
    int left   = ( int ) ( 0.2 * _gridSize );
    int top    = ( int ) ( 0.2 * _gridSize );
    int width  = ( int ) ( 0.6 * _gridSize );
    int height = ( int ) ( 0.6 * _gridSize );
    
    XDrawArc ( XtDisplay ( square ), 
	       XtWindow ( square ),
	       _gc, 
	       left, top, width, height, 0, 360 * 64 );
}
void GameBoard::clear()
{
    int i;
    
    // Each element of the grid may have a callback for an X 
    // or an O, so all callbacks must be removed
    
    for ( i = 0; i < 9; i++ ) 
    {
	XtRemoveAllCallbacks ( _grid[i], XmNexposeCallback );	
	activateSquare ( i );
  	
	// Use XClearArea with exposure events requested
	// so that the widgetÕs shadow is redrawn
	
	if ( XtIsRealized ( _grid[i] ) )
	    XClearArea ( XtDisplay ( _grid[i] ), 
			 XtWindow ( _grid[i] ),
			 0, 0, 0, 0, TRUE );
    }
}
void GameBoard::markCallback ( Widget    w, 
			       XtPointer clientData, 
			       XtPointer )
{
    GameBoard *obj = (GameBoard *) clientData;
    int index;
    
    XtVaGetValues ( w, XmNuserData, &index, NULL );
    
    obj->mark ( index );
}

void GameBoard::mark ( int index )
{
    _game->engine()->recordMove ( index );
}

void GameBoard::activateSquare ( int position )
{
    // Make the button look active by setting the shadow
    // type to normal, enabling pushbutton behavior, and
    // turning on highlights when the mouse enters the square
    
    XtVaSetValues( _grid[position],  
		   XmNpushButtonEnabled, TRUE,
		   XmNshadowType,        XmSHADOW_OUT,
		   XmNshadowThickness,   _shadowThickness,
		   XmNhighlightColor,    _highlightColor,
		   NULL );
}

void GameBoard::highlightSquare ( int position )
{
    // Emphasize a square by changing the shadow type
    
    XtVaSetValues ( _grid[position],
		    XmNshadowType,      XmSHADOW_ETCHED_OUT,
		    XmNshadowThickness, 2 * _shadowThickness,
		    NULL );
}
void GameBoard::deemphasizeSquare ( int position )
{
    // Make a square fade into the background by shutting
    // off the Motif 3-D effect
    
    XtVaSetValues ( _grid[position], XmNshadowThickness, 0, NULL );
}
void GameBoard::deactivateSquare ( int position )
{
    // Change a button to appear inactive by setting the shadow
    // type so the button is depressed, disabling pushbutton 
    // behavior, and turning off the highlight-on-enter feature
    
    XtVaSetValues ( _grid[position],
		    XmNpushButtonEnabled, FALSE,
		    XmNshadowType,        XmSHADOW_IN,
		    XmNhighlightColor,   _noHighlightColor,
		    NULL );
}








