/*
This function has the job of iterating the dirty list, erasing dirty
images and drawing new ones. Any Drawable that has changed since the
last refresh will have its dirty flag set. It will also be on the dirty
list of Drawables. This function makes three passes through the list.

The first pass erases the old image of the drawable. Not all of the
drawables will need to be erased. If the oldParent is null then the
object was not drawn before, and therefore there is no need to erase it.
If the oldParent is dirty itself then the oldParent will get erased, so
then the child does not need to be erased separatly. For the objects
that are erased the drawableErase function is called, and this in turn
will call the function appropriate to the individual shape (boxErase,
lineErase etc.). 

The second pass is to draw the new images. If an object has no parent
then we do not draw it since it is not attached to the main display. If
the parent is dirty then we do not draw it because the redrawing of the
parent will look after redrawing the child, and there is no point in
drawing it twice. 

The third pass resets the dirty flag on all of the
drawables in the list.

*/

void displayRefresh(void)
{
    Drawable *drawablePtr;  /* for iterating through lists */
    int offsetX;
    int offsetY;
/* 
First erase the old images of the dirty objects
*/
    for (drawablePtr = FS_dirtyList;
            drawablePtr != NULL;
            drawablePtr = drawablePtr->nextDirtyDrawablePtr)
    {
        /*
        We only erase the image if there was an old parent and it
        is not dirty. Strictly speaking we should check the parent's 
        parent and so on, but the extra work would probably not pay 
        off in terms of avoiding extra redraws.
        */
        if ( (drawablePtr->oldParentPtr != NULL) &&
                 (drawablePtr->oldParentPtr->visible) &&
                 (!drawablePtr->oldParentPtr->drawable.dirty) )
        {
            offsetX = drawablePtr->oldParentPtr->absoluteLeft;
            offsetY = drawablePtr->oldParentPtr->absoluteTop;
            drawableErase(drawablePtr, offsetX, offsetY);
            /*
            Once an object has been erased once, there will be
            no need to erase it again, even if it is changed off-screen.
            */
            drawablePtr->oldParentPtr = NULL;
        }
    } /* end for */	


/*
The next step is to draw all of the dirty objects 
*/
    for (drawablePtr = FS_dirtyList;
            drawablePtr != NULL;
            drawablePtr = drawablePtr->nextDirtyDrawablePtr)
    {
        /*
        We only draw the image if there is a parent and it is not
        dirty. Strictly speaking we should check the parent's parent
        and so on, but the extra work would probably not pay off.
        The else term here checks if this is the root container,
        which needs to be redrawn, even though it does not have
        a parent - note that we did not use this check in the erase above -
        that is because we know that the root container draw, will erase its
        own area anyway.
        */
        if ( (drawablePtr->parentPtr != NULL) &&
                 (drawablePtr->parentPtr->visible) &&
                 (!drawablePtr->parentPtr->drawable.dirty) )
        {
            offsetX = drawablePtr->parentPtr->absoluteLeft;
            offsetY = drawablePtr->parentPtr->absoluteTop;
            drawableDraw(drawablePtr, offsetX, offsetY);
        }
        else if (drawablePtr == &FS_rootContainerPtr->drawable)
        {
            containerDraw(FS_rootContainerPtr, 0, 0);
            /*
            Force an end to the loop, since we have just drawn everything,
            so there could be no reason to draw anymore.
            */
            break;
        } /* end if */
    } /* end for */

/* 
Now that the objects have been updated the dirty flag can be 
cleared.  These flags should not be cleared in one of the earlier 
loops because they are used for optimizations in the drawableDraw 
function.  Note that the links of the chain are not set to NULL. 
This does not matter because they will get overwritten if it is put
on the chain again.
*/
    drawablePtr = FS_dirtyList;
    for (drawablePtr = FS_dirtyList;
            drawablePtr != NULL;
            drawablePtr = drawablePtr->nextDirtyDrawablePtr)
    {
        drawablePtr->dirty = FALSE;
    }
    FS_dirtyList = NULL;
            
}

/*
Since the containers exist in a hierarchy, there has to be one container
at the top of the hierarchy with a special status. This is the root
container. The application can only call containerCreateRoot() once and
the width and height passed in must be the width and height of the
entire display. Its visible flag is set to TRUE. In its Drawable part,
it's parentPtr field will always be NULL.
*/
Container *containerCreateRoot(int width, int height)
{
    static Boolean calledOnce = FALSE;	
	assert(!calledOnce);
    calledOnce = TRUE;

    FS_rootContainerPtr = containerCreate(0, 0, width-1, height-1);
    FS_rootContainerPtr->visible = TRUE;
    return FS_rootContainerPtr;
}