///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//         This example code is from the book:
//
//           Object-Oriented Programming with C++ and OSF/Motif
//         by
//           Douglas Young
//           Prentice Hall, 1992
//           ISBN 0-13-630252-1	
//
//         Copyright 1991 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.
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////
// Stage.C
//////////////////////////////////////////////////////////
#include "Application.h"
#include "Stage.h"
#include "Actor.h"
#include <Xm/DrawingA.h>

Stage::Stage ( Widget parent, char *name ) : UIComponent( name )
{
    XGCValues  gcv;
    int        eventBase;
    int        errorBase;

    // Check for extension

    if ( !XmbufQueryExtension ( XtDisplay ( parent ), &eventBase, &errorBase ) )
	XtError( "This server does not support the double buffer extension" );

    // Initialize all data members 
    
    _front   = -1;

    _nActors = 0;       
    _cast    = NULL;
    
    // Create the visible drawing canvas, and set 
    // up the destruction handler
    
    _w =  XtCreateWidget ( _name, 
			  xmDrawingAreaWidgetClass,
			  parent,
			  NULL, 0 );
    
    installDestroyHandler();
    
    // Add callbacks to handle resizing and exposures
    
    XtAddCallback ( _w, XmNresizeCallback, 
		   &Stage::resizeCallback,    
		   (XtPointer) this );
    XtAddCallback ( _w, XmNexposeCallback, 
		   &Stage::redisplayCallback, 
		   (XtPointer) this );
    
    // A graphics context is need for copying the pixmap buffers and
    // erasing the back pixmap. Use the background color of 
    // the base widget for the fill color.
    
    XtVaGetValues ( _w, 
		   XmNbackground, &gcv.foreground,
		   NULL );
    
    _gc = XtGetGC ( _w, GCForeground, &gcv ); 
    
    // Call resize to create the first pixmaps.
    
    resize();
}

Stage::~Stage()
{
    XmbufDestroyBuffers ( XtDisplay ( _w ), XtWindow ( _w ) );
  
    if ( _w && _gc )
	XtReleaseGC ( _w, _gc );
}

void Stage::resizeCallback ( Widget, 
			    XtPointer clientData, 
			    XtPointer )
{
    Stage *obj = (Stage *) clientData;
    obj->resize();
}    

void Stage::resize()
{
    // Get the current size of the drawing area
    
    XtVaGetValues ( _w, 
		   XmNwidth,  &_width,
		   XmNheight, &_height,
		   NULL );
    
}

void  Stage::nextFrame()
{
    
    // Set up double buffering the first time

    if ( _front == -1 && XtIsRealized ( _w ) )
    {
	if ( !XmbufCreateBuffers( XtDisplay ( _w ), XtWindow ( _w ), 
				 2, MultibufferUpdateActionBackground,
				 MultibufferUpdateHintFrequent, 
				 _buffers ) )
	    XtError ( "Error creating buffers" );

	_front = 0;
    }

    // For each new frame, simply swap buffers and
    // ask each Actor object to draw its next frame 
    // in the back buffer.
    
    swapBuffers();
    
    for ( int i = 0; i < _nActors; i++)
	_cast[i]->nextFrame ( XtWindow ( _w ), _width, _height );
}

void Stage::swapBuffers()
{
    // Switch the front and back buffers

     if(XtIsRealized(_w))
    {
	_front = !_front;
	XmbufDisplayBuffers ( XtDisplay ( _w ), 1, &_buffers[_front], 0, 0 );
    }
   
}

void Stage::redisplayCallback ( Widget, 
			        XtPointer clientData, 
			        XtPointer )
{
    Stage *obj = (Stage *) clientData;
    obj->redisplay();
}    

void Stage::redisplay ( )
{
    for ( int i = 0; i < _nActors; i++)
	_cast[i]->draw ( XtWindow ( _w ) );
}    

void Stage::addActor ( Actor *newActor )
{
    int i;
    
    Actor **newList;
    
    // Allocate a new list large enough to hold
    // one more object
    
    newList = new Actor*[_nActors + 1];
    
    // Copy the old list to the new
    
    for ( i = 0; i < _nActors; i++ )
	newList[i] = _cast[i];
    
    // Add the new object to the end of the list
    
    newList[_nActors] = newActor;
    
    // delete the old list and make _cast 
    // point to the new list
    
    delete _cast;
    _cast = newList;
    
    
    _nActors++;
}
void Stage::removeActor( Actor *oldActor )
{
    int     i, j;
    Boolean found = FALSE;
    Actor **newList;
    
    // remove the given Actor from the list
    
    for ( i = 0; i < _nActors; i++ )
	if ( _cast[i] == oldActor )
	{
	    found = TRUE;
	    for ( j = i; j < _nActors-1; j++ )
		_cast[j] = _cast[j+1];
	    break;
	}
    
    if ( !found )
	return;
    
    _nActors--;
    
    // Allocate a new, smaller list
    
    newList = new Actor*[_nActors];
    
    for ( i = 0; i < _nActors; i++ )
	newList[i] = _cast[i];
    
    delete _cast;
    _cast = newList;
}
