#ifdef PRECOMPILEDHEADERS
	#include "Editor All.h"
#else
	#include "builddefines.h"
#endif

#ifdef JA2EDITOR

#ifndef PRECOMPILEDHEADERS
	#include "worlddef.h"
	#include "worldman.h"
	#include "smooth.h"
	#include "input.h"
	#include "debug.h"
	#include "Isometric Utils.h"

	#include "editscreen.h"
	#include "Editor Undo.h"
	#include "Render Fun.h"  //for access to gubWorldRoomInfo;
	#include "Cursor Modes.h"
	#include "Exit Grids.h"
#endif

/*
Kris -- Notes on how the undo code works:

At the bottom of the hierarchy, we need to determine the state of the undo command.  The idea
is that we want to separate undo commands by separating them by new mouse clicks.  By holding a mouse
down and painting various objects in the world would all constitute a single undo command.  As soon as
the mouse is release, then a new undo command is setup.  So, to automate this, there is a call every 
frame to DetermineUndoState().

At the next level, there is a binary tree that keeps track of what map indices have been backup up in 
the current undo command.  The whole reason to maintain this list, is to avoid multiple map elements of
the same map index from being saved.  In the outer code, everytime something is changed, a call to 
AddToUndoList() is called, so there are many cases (especially with building/terrain smoothing) that the
same mapindex is added to the undo list.  This is also completely transparent, and doesn't need to be
maintained.

In the outer code, there are several calls to AddToUndoList( iMapIndex ).  This function basically looks
in the binary tree for an existing entry, and if there isn't, then the entire mapelement is saved (with
the exception of the merc level ).  Lights are also supported, but there is a totally different methodology
for accomplishing this.  The equivalent function is AddLightToUndoList( iMapIndex ).  In this case, only the
light is saved, along with internal maintanance of several flags.

The actual mapelement copy code, is very complex.  The mapelement is copied in parallel with a new one which
has to allocate several nodes of several types because a mapelement contains over a dozen separate lists, and
all of them need to be saved.  The structure information of certain mapelements may be multitiled and must 
also save the affected gridno's as well.  This is also done internally.  Basically, your call to 
AddToUndoList() for any particular gridno, may actually save several entries (like for a car which could be 6+
tiles)

MERCS
Mercs are not supported in the undo code.  Because they are so dynamic, and stats could change, they could
move, etc., it doesn't need to be in the undo code.  The editor has its own way of dealing with mercs which
doesn't use the undo methodology.

*/


BOOLEAN gfUndoEnabled = FALSE;

void EnableUndo()
{
	gfUndoEnabled = TRUE;
}

void DisableUndo()
{
	gfUndoEnabled = FALSE;
}

// undo node data element
typedef struct 
{
	INT32						iMapIndex;
	MAP_ELEMENT		*pMapTile;
	BOOLEAN				fLightSaved;	 //determines that a light has been saved
	UINT8					ubLightRadius; //the radius of the light to build if undo is called
	UINT8					ubLightID;		 //only applies if a light was saved.
	UINT8					ubRoomNum;
} undo_struct;

// Undo stack node
typedef struct TAG_undo_stack
{
	INT32										iCmdCount;
	undo_struct							*pData;
	struct TAG_undo_stack		*pNext;
	INT32										iUndoType;
} undo_stack;
undo_stack			*gpTileUndoStack = NULL;

// Map tile element manipulation functions
BOOLEAN CopyMapElementFromWorld( MAP_ELEMENT *pMapTile, INT32 iMapIndex );
BOOLEAN SwapMapElementWithWorld( INT32 iDestMapTileIndex, MAP_ELEMENT *pMapTile );

// Undo structure functions
BOOLEAN DeleteTopStackNode( void );
undo_stack *DeleteThisStackNode( undo_stack *pThisNode );
BOOLEAN DeleteStackNodeContents( undo_stack *pCurrent );
BOOLEAN AddToUndoListCmd( INT32 iMapIndex, INT32 iCmdCount );
void CropStackToMaxLength( INT32 iMaxCmds );
void SmoothUndoMapTileTerrain( INT32 iWorldTile, MAP_ELEMENT *pUndoTile );


BOOLEAN fNewUndoCmd = TRUE;
BOOLEAN gfIgnoreUndoCmdsForLights = FALSE;

//New pre-undo binary tree stuff
//With this, new undo commands will not duplicate saves in the same command.  This will
//increase speed, and save memory.
typedef struct MapIndexBinaryTree
{
	struct MapIndexBinaryTree *left, *right;
	UINT16 usMapIndex;
}MapIndexBinaryTree;

MapIndexBinaryTree *top = NULL;
void ClearUndoMapIndexTree();
BOOLEAN AddMapIndexToTree( UINT16 usMapIndex );
void CheckMapIndexForMultiTileStructures( UINT16 usMapIndex );
void CheckForMultiTilesInTreeAndAddToUndoList( MapIndexBinaryTree *node );


//Recursively deletes all nodes below the node passed including itself.
void DeleteTreeNode( MapIndexBinaryTree **node )
{
	if( (*node)->left ) 
		DeleteTreeNode( &((*node)->left) );
	if( (*node)->right ) 
		DeleteTreeNode( &((*node)->right) );
	MemFree( *node );
	*node = NULL;
}

//Recursively delete all nodes (from the top down).
void ClearUndoMapIndexTree()
{
	if( top )
		DeleteTreeNode( &top );
}

BOOLEAN AddMapIndexToTree( UINT16 usMapIndex )
{
	MapIndexBinaryTree *curr, *parent;
	if( !top )
	{
		top = (MapIndexBinaryTree*)MemAlloc( sizeof( MapIndexBinaryTree ) );
		Assert( top );
		top->usMapIndex = usMapIndex;
		top->left = NULL;
		top->right = NULL;
		return TRUE;
	}
	curr = top;
	parent = NULL;
	//Traverse down the tree and attempt to find a matching mapindex.
	//If one is encountered, then we fail, and don't add the mapindex to the
	//tree.
	while( curr )
	{
		parent = curr;
		if( curr->usMapIndex == usMapIndex ) //found a match, so stop
			return FALSE;
		//if the mapIndex is < node's mapIndex, then go left, else right
		curr = ( usMapIndex < curr->usMapIndex ) ? curr->left : curr->right;
	}
	//if we made it this far, then curr is null and parent is pointing
	//directly above.
	//Create the new node and fill in the information.
	curr = (MapIndexBinaryTree*)MemAlloc( sizeof( MapIndexBinaryTree ) );
	Assert( curr );
	curr->usMapIndex = usMapIndex;
	curr->left = NULL;
	curr->right = NULL;
	//Now link the new node to the parent.
	if( curr->usMapIndex < parent->usMapIndex )
		parent->left = curr;
	else
		parent->right = curr;
	return TRUE;
}	
//*************************************************************************
//
//	Start of Undo code
//
//*************************************************************************

BOOLEAN DeleteTopStackNode( void )
{
	undo_stack		*pCurrent;

	pCurrent = gpTileUndoStack;

	DeleteStackNodeContents( pCurrent );

	// Remove node from stack, and free it's memory
	gpTileUndoStack	= gpTileUndoStack->pNext;
	MemFree( pCurrent );

	return( TRUE );
}


undo_stack *DeleteThisStackNode( undo_stack *pThisNode )
{
	undo_stack		*pCurrent;
	undo_stack		*pNextNode;

	pCurrent = pThisNode;
	pNextNode	= pThisNode->pNext;

	// Remove node from stack, and free it's memory
	DeleteStackNodeContents( pCurrent );
	MemFree( pCurrent );

	return( pNextNode );
}


BOOLEAN DeleteStackNodeContents( undo_stack *pCurrent )
{
	undo_struct		*pData;
	MAP_ELEMENT		*pMapTile;
	LEVELNODE			*pLandNode;
	LEVELNODE			*pObjectNode;
	LEVELNODE			*pStructNode;
	LEVELNODE			*pShadowNode;
	LEVELNODE			*pMercNode;
	LEVELNODE			*pTopmostNode;
	LEVELNODE			*pRoofNode;
	LEVELNODE			*pOnRoofNode;
	STRUCTURE			*pStructureNode;

	pData = pCurrent->pData;
	pMapTile = pData->pMapTile;

	if( !pMapTile ) 
		return TRUE;		//light was saved -- mapelement wasn't saved.

	// Free the memory associated with the map tile liked lists
	pLandNode = pMapTile->pLandHead;
	while ( pLandNode != NULL )
	{
		pMapTile->pLandHead = pLandNode->pNext;
		MemFree( pLandNode );
		pLandNode = pMapTile->pLandHead;
	}

	pObjectNode = pMapTile->pObjectHead;
	while ( pObjectNode != NULL )
	{
		pMapTile->pObjectHead = pObjectNode->pNext;
		MemFree( pObjectNode );
		pObjectNode = pMapTile->pObjectHead;
	}

	pStructNode = pMapTile->pStructHead;
	while ( pStructNode != NULL )
	{
		pMapTile->pStructHead = pStructNode->pNext;
		MemFree( pStructNode );
		pStructNode = pMapTile->pStructHead;
	}

	pShadowNode = pMapTile->pShadowHead;
	while ( pShadowNode != NULL )
	{
		pMapTile->pShadowHead = pShadowNode->pNext;
		MemFree( pShadowNode );
		pShadowNode = pMapTile->pShadowHead;
	}

	pMercNode = pMapTile->pMercHead;
	while ( pMercNode != NULL )
	{
		pMapTile->pMercHead = pMercNode->pNext;
		MemFree( pMercNode );
		pMercNode = pMapTile->pMercHead;
	}

	pRoofNode = pMapTile->pRoofHead;
	while ( pRoofNode != NULL )
	{
		pMapTile->pRoofHead = pRoofNode->pNext;
		MemFree( pRoofNode );
		pRoofNode = pMapTile->pRoofHead;
	}

	pOnRoofNode = pMapTile->pOnRoofHead;
	while ( pOnRoofNode != NULL )
	{
		pMapTile->pOnRoofHead = pOnRoofNode->pNext;
		MemFree( pOnRoofNode );
		pOnRoofNode = pMapTile->pOnRoofHead;
	}

	pTopmostNode = pMapTile->pTopmostHead;
	while ( pTopmostNode != NULL )
	{
		pMapTile->pTopmostHead = pTopmostNode->pNext;
		MemFree( pTopmostNode );
		pTopmostNode = pMapTile->pTopmostHead;
	}

	pStructureNode = pMapTile->pStructureHead;
	while( pStructureNode )
	{
		pMapTile->pStructureHead = pStructureNode->pNext;
		if( pStructureNode->usStructureID > INVALID_STRUCTURE_ID )
		{ //Okay to delete the structure data -- otherwise, this would be 
			//merc structure data that we DON'T want to delete, because the merc node
			//that hasn't been modified will still use this structure data!
			MemFree( pStructureNode );
		}
		pStructureNode = pMapTile->pStructureHead;
	}
	
	// Free the map tile structure itself
	MemFree( pMapTile );

	// Free the undo struct
	MemFree( pData );

	return( TRUE );
}


void CropStackToMaxLength( INT32 iMaxCmds )
{
	INT32				iCmdCount;
	undo_stack	*pCurrent;

	iCmdCount = 0;
	pCurrent = gpTileUndoStack;

	// If stack is empty, leave
	if ( pCurrent == NULL )
		return;

	while ( (iCmdCount <= (iMaxCmds - 1)) && (pCurrent != NULL) )
	{
		if ( pCurrent->iCmdCount == 1 )
			iCmdCount++;
		pCurrent = pCurrent->pNext;
	}

	// If the max number of commands was reached, and there is something
	// to crop, from the rest of the stack, remove it.
	if ( (iCmdCount >= iMaxCmds) && pCurrent != NULL )
	{
		while ( pCurrent->pNext != NULL )
			pCurrent->pNext = DeleteThisStackNode( pCurrent->pNext );
	}
}


//We are adding a light to the undo list.  We won't save the mapelement, nor will
//we validate the gridno in the binary tree.  This works differently than a mapelement, 
//because lights work on a different system.  By setting the fLightSaved flag to TRUE,
//this will handle the way the undo command is handled.  If there is no lightradius in
//our saved light, then we intend on erasing the light upon undo execution, otherwise, we
//save the light radius and light ID, so that we place it during undo execution.
void AddLightToUndoList( INT32 iMapIndex, INT32 iLightRadius, UINT8 ubLightID )
{
	undo_stack		*pNode;
	undo_struct		*pUndoInfo;
	
	if(	!gfUndoEnabled )
		return;
	//When executing an undo command (by adding a light or removing one), that command
	//actually tries to add it to the undo list.  So we wrap the execution of the undo
	//command by temporarily setting this flag, so it'll ignore, and not place a new undo
	//command.  When finished, the flag is cleared, and lights are again allowed to be saved
	//in the undo list.
	if( gfIgnoreUndoCmdsForLights )
		return;

	pNode = (undo_stack*)MemAlloc( sizeof( undo_stack ) );
	if( !pNode )
		return;

	pUndoInfo = (undo_struct *)MemAlloc( sizeof( undo_struct ) );
	if( !pUndoInfo )
	{
		MemFree( pNode );
		return;
	}

	pUndoInfo->fLightSaved = TRUE;
	//if ubLightRadius is 0, then we don't need to save the light information because we 
	//will erase it when it comes time to execute the undo command.
	pUndoInfo->ubLightRadius = (UINT8)iLightRadius;
	pUndoInfo->ubLightID = ubLightID;
	pUndoInfo->iMapIndex = iMapIndex;
	pUndoInfo->pMapTile = NULL;

	//Add to undo stack
	pNode->iCmdCount = 1;
	pNode->pData = pUndoInfo;
	pNode->pNext = gpTileUndoStack;
	gpTileUndoStack = pNode;

	CropStackToMaxLength( MAX_UNDO_COMMAND_LENGTH );
}

BOOLEAN AddToUndoList( INT32 iMapIndex )
{
	static INT32 iCount = 1;

	if(	!gfUndoEnabled )
		return FALSE;
	if ( fNewUndoCmd )
	{
		iCount = 0;
		fNewUndoCmd = FALSE;
	}

	//Check to see if the tile in question is even on the visible map, then
	//if that is true, then check to make sure we don't already have the mapindex
	//saved in the new binary tree (which only holds unique mapindex values).
	if( GridNoOnVisibleWorldTile( (INT16)iMapIndex ) && AddMapIndexToTree( (UINT16)iMapIndex ) )

	{
		if( AddToUndoListCmd( iMapIndex, ++iCount ) )
			return TRUE;
		iCount--;
	}
	return FALSE;
}


BOOLEAN AddToUndoListCmd( INT32 iMapIndex, INT32 iCmdCount )
{
	undo_stack		*pNode;
	undo_struct		*pUndoInfo;
	MAP_ELEMENT		*pData;
	STRUCTURE			*pStructure;
	INT32					iCoveredMapIndex;
	UINT8					ubLoop;
	
	if ( (pNode = (undo_stack *)MemAlloc(sizeof( undo_stack )) ) == NULL )
	{
		return( FALSE );
	}

	if ( (pUndoInfo = (undo_struct *)MemAlloc(sizeof( undo_struct )) ) == NULL )
	{
		MemFree( pNode );
		return( FALSE );
	}

	if ( (pData = (MAP_ELEMENT *)MemAlloc(sizeof( MAP_ELEMENT )) ) == NULL )
	{
		MemFree( pNode );
		MemFree( pUndoInfo );
		return( FALSE );
	}

	// Init map element struct
	pData->pLandHead = pData->pLandStart = NULL;
	pData->pObjectHead = NULL;
	pData->pStructHead = NULL;
	pData->pShadowHead = NULL;
	pData->pMercHead = NULL;
	pData->pRoofHead = NULL;
	pData->pOnRoofHead = NULL;
	pData->pTopmostHead = NULL;
	pData->pStructureHead = pData->pStructureTail = NULL;
	pData->sHeight = 0;


	// Copy the world map's tile
	if ( CopyMapElementFromWorld( pData, iMapIndex ) == FALSE )
	{
		MemFree( pNode );
		MemFree( pUndoInfo );
		MemFree( pData );
		return( FALSE );
	}

	// copy the room number information (it's not in the mapelement structure)
	pUndoInfo->ubRoomNum = gubWorldRoomInfo[ iMapIndex ];
	
	pUndoInfo->fLightSaved = FALSE;
	pUndoInfo->ubLightRadius = 0;
	pUndoInfo->ubLightID = 0;
	pUndoInfo->pMapTile = pData;
	pUndoInfo->iMapIndex = iMapIndex;

	pNode->pData = pUndoInfo;
	pNode->iCmdCount = iCmdCount;
	pNode->pNext = gpTileUndoStack;
	gpTileUndoStack = pNode;

	// loop through pData->pStructureHead list
	// for each structure
	//   find the base tile
	//   reference the db structure
	//   if number of tiles > 1
	//     add all covered tiles to undo list
	pStructure = pData->pStructureHead;
	while (pStructure)
	{
		for (ubLoop = 1; ubLoop < pStructure->pDBStructureRef->pDBStructure->ubNumberOfTiles; ubLoop++)
		{
			// this loop won't execute for single-tile structures; for multi-tile structures, we have to
			// add to the undo list all the other tiles covered by the structure
			iCoveredMapIndex = pStructure->sBaseGridNo + pStructure->pDBStructureRef->ppTile[ubLoop]->sPosRelToBase;
			AddToUndoList( iCoveredMapIndex );
		}
		pStructure = pStructure->pNext;
	}

	CropStackToMaxLength( MAX_UNDO_COMMAND_LENGTH );

	return( TRUE );
}


void CheckMapIndexForMultiTileStructures( UINT16 usMapIndex )
{
	STRUCTURE *		pStructure;
	UINT8					ubLoop;
	INT32					iCoveredMapIndex;

	pStructure = gpWorldLevelData[usMapIndex].pStructureHead;
	while (pStructure)
	{
		if (pStructure->pDBStructureRef->pDBStructure->ubNumberOfTiles > 1)
		{
			for (ubLoop = 0; ubLoop < pStructure->pDBStructureRef->pDBStructure->ubNumberOfTiles; ubLoop++)
			{
				// for multi-tile structures we have to add, to the undo list, all the other tiles covered by the structure
				if (pStructure->fFlags & STRUCTURE_BASE_TILE)
				{
					iCoveredMapIndex = usMapIndex + pStructure->pDBStructureRef->ppTile[ubLoop]->sPosRelToBase;
				}
				else
				{
					iCoveredMapIndex = pStructure->sBaseGridNo + pStructure->pDBStructureRef->ppTile[ubLoop]->sPosRelToBase;
				}
				AddToUndoList( iCoveredMapIndex );
			}
		}
		pStructure = pStructure->pNext;
	}
}

void CheckForMultiTilesInTreeAndAddToUndoList( MapIndexBinaryTree *node )
{
	CheckMapIndexForMultiTileStructures( node->usMapIndex );
	if( node->left ) 
		CheckForMultiTilesInTreeAndAddToUndoList( node->left );
	if( node->right ) 
		CheckForMultiTilesInTreeAndAddToUndoList( node->right );
}

BOOLEAN RemoveAllFromUndoList( void )
{
	ClearUndoMapIndexTree();

	while ( gpTileUndoStack != NULL )
		DeleteTopStackNode();

	return( TRUE );
}


BOOLEAN ExecuteUndoList( void )
{
	INT32				iCmdCount, iCurCount;
	INT32				iUndoMapIndex;
	BOOLEAN			fExitGrid;

	if(	!gfUndoEnabled )
		return FALSE;

	// Is there something on the undo stack?
	if ( gpTileUndoStack == NULL )
		return( TRUE );

	// Get number of stack entries for this command (top node will tell this)
	iCmdCount = gpTileUndoStack->iCmdCount;

	// Execute each stack node in command, and remove each from stack.
	iCurCount = 0;
	while ( (iCurCount < iCmdCount) && (gpTileUndoStack != NULL) )
	{
		iUndoMapIndex = gpTileUndoStack->pData->iMapIndex;

		// Find which map tile we are to "undo"
		if( gpTileUndoStack->pData->fLightSaved )
		{ //We saved a light, so delete that light
			INT16 sX, sY;
			//Turn on this flag so that the following code, when executed, doesn't attempt to
			//add lights to the undo list.  That would cause problems...
			gfIgnoreUndoCmdsForLights = TRUE;
			ConvertGridNoToXY( (INT16)iUndoMapIndex, &sX, &sY );
			if( !gpTileUndoStack->pData->ubLightRadius )
				RemoveLight( sX, sY );
			else
				PlaceLight( gpTileUndoStack->pData->ubLightRadius, sX, sY, gpTileUndoStack->pData->ubLightID );
			//Turn off the flag so lights can again be added to the undo list.
			gfIgnoreUndoCmdsForLights = FALSE;
		}
		else
		{	// We execute the undo command node by simply swapping the contents
			// of the undo's MAP_ELEMENT with the world's element.
			fExitGrid = ExitGridAtGridNo( (UINT16)iUndoMapIndex );
			SwapMapElementWithWorld( iUndoMapIndex, gpTileUndoStack->pData->pMapTile );

			// copy the room number information back 
			gubWorldRoomInfo[ iUndoMapIndex ] = gpTileUndoStack->pData->ubRoomNum;

			// Now we smooth out the changes...
			//SmoothUndoMapTileTerrain( iUndoMapIndex, gpTileUndoStack->pData->pMapTile );
			SmoothAllTerrainTypeRadius( iUndoMapIndex, 1, TRUE );
		}

		// ...trash the top element of the stack...
		DeleteTopStackNode( );

		// ...and bump the command counter up by 1
		iCurCount++;

		//Kris:
		//The new cursor system is somehow interfering with the undo stuff.  When
		//an undo is called, the item is erased, but a cursor is added!  I'm quickly
		//hacking around this by erasing all cursors here.
		RemoveAllTopmostsOfTypeRange( iUndoMapIndex, FIRSTPOINTERS, FIRSTPOINTERS );

		if( fExitGrid && !ExitGridAtGridNo( (UINT16)iUndoMapIndex ) )
		{ //An exitgrid has been removed, so get rid of the associated indicator.
			RemoveTopmost( (UINT16)iUndoMapIndex, FIRSTPOINTERS8 );
		}
		else if( !fExitGrid && ExitGridAtGridNo( (UINT16)iUndoMapIndex ) )
		{ //An exitgrid has been added, so add the associated indicator
			AddTopmostToTail( (UINT16)iUndoMapIndex, FIRSTPOINTERS8 );
		}
	}

	return( TRUE );
}


void SmoothUndoMapTileTerrain( INT32 iWorldTile, MAP_ELEMENT *pUndoTile )
{
	LEVELNODE	*pWorldLand;
	LEVELNODE *pUndoLand;
	LEVELNODE *pLand;
	LEVELNODE *pWLand;
	UINT32		uiCheckType, uiWCheckType;
	BOOLEAN		fFound;

	pUndoLand = pUndoTile->pLandHead;
	pWorldLand = gpWorldLevelData[ iWorldTile ].pLandHead;

	if ( pUndoLand == NULL )
	{
		// nothing in the old tile, so smooth the entire land in world's tile
		pLand = gpWorldLevelData[ iWorldTile ].pLandHead;
		while( pLand != NULL )
		{
			GetTileType( pLand->usIndex, &uiCheckType );
			SmoothTerrainRadius( iWorldTile, uiCheckType, 1, TRUE );
			pLand = pLand->pNext;
		}
	}
	else if ( gpWorldLevelData[ iWorldTile ].pLandHead == NULL ) 
	{
		// Nothing in world's tile, so smooth out the land in the old tile.
		pLand = pUndoLand;
		while( pLand != NULL )
		{
			GetTileType( pLand->usIndex, &uiCheckType );
			SmoothTerrainRadius( iWorldTile, uiCheckType, 1, TRUE );
			pLand = pLand->pNext;
		}
	}
	else
	{
		pLand = pUndoLand;
		while( pLand != NULL )
		{
			GetTileType( pLand->usIndex, &uiCheckType);

			fFound = FALSE;
			pWLand = pWorldLand;
			while( pWLand != NULL && !fFound )
			{
				GetTileType( pWLand->usIndex, &uiWCheckType);
				
				if ( uiCheckType == uiWCheckType )
					fFound = TRUE;

				pWLand = pWLand->pNext;
			}

			if ( !fFound )
				SmoothTerrainRadius( iWorldTile, uiCheckType, 1, TRUE );

			pLand = pLand->pNext;
		}

		pWLand = pWorldLand;
		while( pWLand != NULL )
		{
			GetTileType( pWLand->usIndex, &uiWCheckType);

			fFound = FALSE;
			pLand = pUndoLand;
			while( pLand != NULL && !fFound )
			{
				GetTileType( pLand->usIndex, &uiCheckType);
				
				if ( uiCheckType == uiWCheckType )
					fFound = TRUE;

				pLand = pLand->pNext;
			}

			if ( !fFound )
				SmoothTerrainRadius( iWorldTile, uiWCheckType, 1, TRUE );

			pWLand = pWLand->pNext;
		}
	}
}

//Because of the potentially huge amounts of memory that can be allocated due to the inefficient
//undo methods coded by Bret, it is feasible that it could fail.  Instead of using assertions to
//terminate the program, destroy the memory allocated thusfar.
void DeleteMapElementContentsAfterCreationFail( MAP_ELEMENT *pNewMapElement )
{
	LEVELNODE *pLevelNode;
	STRUCTURE *pStructure;
	INT32 x;
	for( x = 0; x < 9; x++ )
	{
		if( x == 1 )
			continue;
		pLevelNode = pNewMapElement->pLevelNodes[ x ];
		while( pLevelNode )
		{
			LEVELNODE *temp;
			temp = pLevelNode;
			pLevelNode = pLevelNode->pNext;
			MemFree( temp );
		}
	}
	pStructure = pNewMapElement->pStructureHead;
	while( pStructure )
	{
		STRUCTURE *temp;
		temp = pStructure;
		pStructure = pStructure->pNext;
		MemFree( temp );
	}
}

/*
	union 
	{
		struct TAG_level_node				*pPrevNode;					// FOR LAND, GOING BACKWARDS POINTER
		ITEM_POOL										*pItemPool;					// ITEM POOLS
		STRUCTURE										*pStructureData;		// STRUCTURE DATA
		INT32												iPhysicsObjectID;		// ID FOR PHYSICS ITEM
		INT32												uiAPCost;						// FOR AP DISPLAY
	}; // ( 4 byte union )
	union 
	{
		struct
		{
			UINT16										usIndex;							// TILE DATABASE INDEX
			INT16											sCurrentFrame;				// Stuff for animated tiles for a given tile location ( doors, etc )
		};
		struct
		{
			SOLDIERTYPE								*pSoldier;					// POINTER TO SOLDIER
		};
	}; // ( 4 byte union )
*/
BOOLEAN CopyMapElementFromWorld( MAP_ELEMENT *pNewMapElement, INT32 iMapIndex )
{
	MAP_ELEMENT			*pOldMapElement;
	LEVELNODE				*pOldLevelNode;
	LEVELNODE			  *pLevelNode;
	LEVELNODE				*pNewLevelNode;
	LEVELNODE				*tail;
	INT32						x;

	STRUCTURE				*pOldStructure;

	//Get a pointer to the current map index
	pOldMapElement = &gpWorldLevelData[ iMapIndex ];

	//Save the structure information from the mapelement
	pOldStructure = pOldMapElement->pStructureHead;
	if( pOldStructure )
	{
		STRUCTURE				*pNewStructure;
		STRUCTURE				*pStructure;
		STRUCTURE				*tail;
		tail = NULL;
		pNewStructure = NULL;
		while( pOldStructure )
		{
			pStructure = (STRUCTURE*)MemAlloc( sizeof( STRUCTURE ) );
			if( !pStructure )
			{
				DeleteMapElementContentsAfterCreationFail( pNewMapElement );
				return FALSE;
			}
			if( !tail )
			{ //first node in structure list
				tail = pStructure;
				*tail = *pOldStructure;
				tail->pPrev = NULL;
				tail->pNext = NULL;
			}
			else
			{ //add to the end of the levelnode list
				tail->pNext = pStructure;
				*pStructure = *pOldStructure;
				pStructure->pPrev = tail;
				pStructure->pNext = NULL;
				tail = tail->pNext;
			}
			//place the new node inside of the new map element
			if( !pNewStructure )
			{
			  pNewMapElement->pStructureHead = pStructure;
				pNewStructure = pStructure;
			}
			else
			{
				pNewStructure->pNext = pStructure;
				pNewStructure = pNewStructure->pNext;
			}
			pOldStructure = pOldStructure->pNext;
		}
		if( tail )
		{
			pNewMapElement->pStructureTail = tail;
		}
	}

	//For each of the 9 levelnodes, save each one
	//except for levelnode[1] which is a pointer to the first land to render.
	for( x = 0; x < 9; x++ )
	{
		if( x == 1 || x == 5 ) //skip the pLandStart and pMercLevel LEVELNODES
			continue;
		tail = NULL;
		pOldLevelNode = pOldMapElement->pLevelNodes[ x ];
		pNewLevelNode = NULL;
		while( pOldLevelNode )
		{
			//copy the level node
			pLevelNode = ( LEVELNODE* )MemAlloc( sizeof( LEVELNODE ) );
			if( !pLevelNode )
			{
				DeleteMapElementContentsAfterCreationFail( pNewMapElement );
				return FALSE;
			}
			if( !tail )
			{ //first node in levelnode list
				tail = pLevelNode;
				*tail = *pOldLevelNode;
				if( !x ) //land layer only
					tail->pPrevNode = NULL;
				tail->pNext = NULL;
			}
			else
			{ //add to the end of the levelnode list
				tail->pNext = pLevelNode;
				*pLevelNode = *pOldLevelNode;
				if( !x ) //land layer only
					pLevelNode->pPrevNode = tail;
				pLevelNode->pNext = NULL;
				tail = tail->pNext;
			}
			//place the new node inside of the new map element
			if( !pNewLevelNode )
			{
			  pNewMapElement->pLevelNodes[ x ] = pLevelNode;
				pNewLevelNode = pLevelNode;
			}
			else
			{
				pNewLevelNode->pNext = pLevelNode;
				pNewLevelNode = pNewLevelNode->pNext;
			}
			//Handle levelnode layer specific stuff
			switch( x )
			{
				case 0: //LAND LAYER
					if( pOldLevelNode == pOldMapElement->pLandStart )
					{ //set the new landstart to point to the new levelnode.
						pNewMapElement->pLandStart = pNewLevelNode;
					}
					break;
				case 2: //OBJECT LAYER
					if( pOldLevelNode->pItemPool )
					{ //save the item pool?
						//pNewLevelNode->pItemPool = (ITEM_POOL*)MemAlloc( sizeof( ITEM_POOL ) );
					}
					break;
				case 3: //STRUCT LAYER
				case 6:	//ROOF LAYER
				case 7: //ON ROOF LAYER
					if( pOldLevelNode->pStructureData )
					{ //make sure the structuredata pointer points to the parallel structure
						STRUCTURE *pOld, *pNew;
						//both lists are exactly the same size and contain the same information,
						//but the addresses are different.  We will traverse the old list until
						//we find the match, then
						pOld = pOldMapElement->pStructureHead;
						pNew = pNewMapElement->pStructureHead;
						while( pOld )
						{
							Assert( pNew );
							if( pOld == pOldLevelNode->pStructureData )
							{
								pNewLevelNode->pStructureData = pNew;
								break;
							}
							pOld = pOld->pNext;
							pNew = pNew->pNext;
						}
						//Kris:
						//If this assert should fail, that means there is something wrong with
						//the preservation of the structure data within the mapelement.  
						if( pOld != pOldLevelNode->pStructureData )
						{
							//OUCH!!! THIS IS HAPPENING.  DISABLED IT FOR LINDA'S SAKE
							Assert( 1 );
						}
					}
					break;
			}
			//Done, go to next node in this level
			pOldLevelNode = pOldLevelNode->pNext;
		}
		//Done, go to next level
	}

	//Save the rest of the information in the mapelement.
	pNewMapElement->uiFlags							= pOldMapElement->uiFlags;
	pNewMapElement->sSumRealLights[0]		= pOldMapElement->sSumRealLights[0];
	pNewMapElement->sSumRealLights[1]		= pOldMapElement->sSumRealLights[1];
	pNewMapElement->sHeight							= pOldMapElement->sHeight;
	pNewMapElement->ubTerrainID					= pOldMapElement->ubTerrainID;
	pNewMapElement->ubReservedSoldierID = pOldMapElement->ubReservedSoldierID;

	return TRUE;
}


BOOLEAN SwapMapElementWithWorld( INT32 iMapIndex, MAP_ELEMENT *pUndoMapElement )
{
	MAP_ELEMENT			*pCurrentMapElement;
	MAP_ELEMENT			TempMapElement;

	pCurrentMapElement = &gpWorldLevelData[ iMapIndex ];

	//Transfer the merc level node from the current world to the undo mapelement
	//that will replace it.  We do this, because mercs aren't associated with
	//undo commands.
	pUndoMapElement->pMercHead = gpWorldLevelData[ iMapIndex ].pMercHead;
	gpWorldLevelData[ iMapIndex ].pMercHead = NULL;
	
	//Swap the mapelements
	TempMapElement = *pCurrentMapElement;
	*pCurrentMapElement = *pUndoMapElement;
	*pUndoMapElement = TempMapElement;

	return ( TRUE );
}

void DetermineUndoState()
{
	// Reset the undo command mode if we released the left button.
	if( !fNewUndoCmd )
	{
		if( !gfLeftButtonState  && !gfCurrentSelectionWithRightButton || 
			  !gfRightButtonState &&  gfCurrentSelectionWithRightButton )
		{
			//Clear the mapindex binary tree list, and set up flag for new undo command.
			fNewUndoCmd = TRUE;
			ClearUndoMapIndexTree();
		}
	}
}


#endif