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

#ifdef JA2EDITOR

#ifndef PRECOMPILEDHEADERS
	#include "tiledef.h"
	#include "edit_sys.h"
	#include "sysutil.h"
	#include "Font Control.h"
	#include "renderworld.h"	//ClearRenderFlags();
	#include "worldman.h"			//HideStructOfGivenType()
	#include "EditorDefines.h"
	#include "EditorBuildings.h"
	#include "EditorTerrain.h" //for access to TerrainTileDrawMode
	#include "Render Fun.h"
	#include "newsmooth.h"
	#include "Editor Undo.h"
	#include "Editor Taskbar Utils.h"
	#include "Editor Modes.h"
	#include "Smoothing Utils.h"
	#include "Text Input.h"
	#include "keys.h"
	#include "environment.h"
	#include "selectwin.h"
	#include "Simple Render Utils.h"
#endif

BOOLEAN fBuildingShowRoofs, fBuildingShowWalls, fBuildingShowRoomInfo;
UINT16 usCurrentMode;
UINT8 gubCurrRoomNumber;
UINT8 gubMaxRoomNumber;
BOOLEAN	gfEditingDoor;

//BEGINNNING OF BUILDING INITIALIZATION FUNCTIONS
void GameInitEditorBuildingInfo()
{
	fBuildingShowRoofs = TRUE;
	fBuildingShowWalls = TRUE;
	fBuildingShowRoomInfo = FALSE;
	usCurrentMode = BUILDING_PLACE_WALLS;
	gubCurrRoomNumber = gubMaxRoomNumber = 1;
}

//BEGINNING OF BUILDING UTILITY FUNCTIONS
void UpdateRoofsView()
{
	INT32 x;
	UINT16 usType;
	for ( x = 0; x < WORLD_MAX; x++ )
	{
		for ( usType = FIRSTROOF; usType <= LASTSLANTROOF; usType++ )
		{
			HideStructOfGivenType( x, usType, (BOOLEAN)(!fBuildingShowRoofs) );
		}
	}
	gfRenderWorld = TRUE;
}

void UpdateWallsView()
{
	INT32 cnt;
	for ( cnt = 0; cnt < WORLD_MAX; cnt++ )
	{
		if ( fBuildingShowWalls )
		{
			RemoveWallLevelnodeFlags( (INT16)cnt, LEVELNODE_HIDDEN );
		}
		else
		{
			SetWallLevelnodeFlags( (INT16)cnt, LEVELNODE_HIDDEN );
		}
	}
	gfRenderWorld = TRUE;
}

void UpdateBuildingsInfo()
{
	//print the headers on top of the columns
	SetFont( SMALLCOMPFONT );
	SetFontForeground( FONT_RED );
	mprintfEditor( 112, 362, L"TOGGLE");
	mprintfEditor( 114, 372, L"VIEWS");
	SetFontForeground( FONT_YELLOW );
	mprintfEditor( 185, 362, L"SELECTION METHOD");
	SetFontForeground( FONT_LTGREEN );
	mprintfEditor( 290, 362, L"SMART METHOD");
	SetFontForeground( FONT_LTBLUE );
	mprintfEditor( 390, 362, L"BUILDING METHOD");
	SetFontForeground( FONT_GRAY2 );
	mprintfEditor( 437, 404, L"Room#" );
}

//Uses a recursive method to elimate adjacent tiles of structure information.
//The code will attempt to delete the current mapindex, then search using this method:
//1) if there isn't structure info here, return.
//2) if there is structure info here, delete it now.
//3) KillBuilding at x-1, y.
//4) KillBuilding at x  , y-1.
//5) KillBuilding at x+1, y.
//6) KillBuilding at x  , y+1.
void KillBuilding( UINT32 iMapIndex )
{
	BOOLEAN fFound = FALSE;

	if( !gfBasement )
		fFound |= RemoveAllRoofsOfTypeRange( iMapIndex, FIRSTTEXTURE, LASTITEM );	
	fFound |= RemoveAllLandsOfTypeRange( iMapIndex, FIRSTFLOOR, LASTFLOOR );

	EraseBuilding( iMapIndex );
	gubWorldRoomInfo[ iMapIndex ] = 0;

	if( !fFound )
	{
		if( gfBasement )
			RebuildRoof( iMapIndex, 0 );
		return;
	}

	if( GridNoOnVisibleWorldTile( (UINT16)( iMapIndex - WORLD_COLS ) ) )
		KillBuilding( iMapIndex - WORLD_COLS );
	if( GridNoOnVisibleWorldTile( (UINT16)( iMapIndex + WORLD_COLS ) ) )
		KillBuilding( iMapIndex + WORLD_COLS );
	if( GridNoOnVisibleWorldTile( (UINT16)( iMapIndex + 1 ) ) )
		KillBuilding( iMapIndex + 1 );
	if( GridNoOnVisibleWorldTile( (UINT16)( iMapIndex - 1 ) ) )
		KillBuilding( iMapIndex - 1 );

	if( gfBasement )
		RebuildRoof( iMapIndex, 0 );
}

BUILDINGLAYOUTNODE *gpBuildingLayoutList = NULL;
INT16 gsBuildingLayoutAnchorGridNo = -1;
extern void RemoveBuildingLayout();

void DeleteBuildingLayout()
{
	BUILDINGLAYOUTNODE *curr;
	//Erases the cursors associated with them.
	RemoveBuildingLayout();
	while( gpBuildingLayoutList )
	{
		curr = gpBuildingLayoutList;
		gpBuildingLayoutList = gpBuildingLayoutList->next;
		MemFree( curr );
	}
	gpBuildingLayoutList = NULL;
	gsBuildingLayoutAnchorGridNo = -1;	
}

void BuildLayout( INT32 iMapIndex, INT32 iOffset )
{
	BUILDINGLAYOUTNODE *curr;
	//First, validate the gridno
	iMapIndex += iOffset;
	if( iMapIndex < 0 && iMapIndex >= WORLD_COLS*WORLD_ROWS )
		return;
	//Now, check if there is a building here
	if( !BuildingAtGridNo( iMapIndex ) )
	{
		if( iOffset == 1 && !BuildingAtGridNo( iMapIndex - 1 ) )
			return;
		if( iOffset == WORLD_COLS && !BuildingAtGridNo( iMapIndex - WORLD_COLS ) )
			return;
		if( iOffset == -1 && !GetVerticalWall( iMapIndex ) )
			return;
		if( iOffset == -WORLD_COLS && !GetHorizontalWall( iMapIndex ) )
			return;
	}
	//Now, check to make sure this gridno hasn't already been processed.
	curr = gpBuildingLayoutList;
	while( curr )
	{
		if( (INT16)iMapIndex == curr->sGridNo )
			return;
		curr = curr->next;
	}
	//Good, it hasn't, so process it and add it to the head of the list.
	curr = (BUILDINGLAYOUTNODE*)MemAlloc( sizeof( BUILDINGLAYOUTNODE ) );
	Assert( curr );
	curr->sGridNo = (INT16)iMapIndex;
	curr->next = gpBuildingLayoutList;
	gpBuildingLayoutList = curr;

	//Use recursion to process the remainder.
	BuildLayout( iMapIndex, -WORLD_COLS );
	BuildLayout( iMapIndex, -1 );
	BuildLayout( iMapIndex, 1 );
	BuildLayout( iMapIndex, WORLD_COLS );
}

//The first step is copying a building.  After that, it either must be pasted or moved.
void CopyBuilding( INT32 iMapIndex )
{
	AssertMsg( !gpBuildingLayoutList, "Error:  Attempting to copy building multiple times." );

	//First step is to determine if we have a building in the area that we click.  If not, do nothing.
	if( !BuildingAtGridNo( iMapIndex ) )
		return;
	//Okay, a building does exist here to some undetermined capacity.  
	//Allocate the basic structure, then calculate the layout.  The head node is
	gpBuildingLayoutList = (BUILDINGLAYOUTNODE*)MemAlloc( sizeof( BUILDINGLAYOUTNODE ) );
	Assert( gpBuildingLayoutList );
	gpBuildingLayoutList->sGridNo = (INT16)iMapIndex;
	gpBuildingLayoutList->next = NULL;

	//Set the anchor point for this building -- this is where the user clicked.
	gsBuildingLayoutAnchorGridNo = (INT16)iMapIndex;
		
	//Now, recursively expand out while adding unique gridnos to our list.  The recursion will
	//terminate when complete.
	BuildLayout( iMapIndex, -WORLD_COLS );
	BuildLayout( iMapIndex, -1 );
	BuildLayout( iMapIndex, 1 );
	BuildLayout( iMapIndex, WORLD_COLS );

	//We have our layout.  Now depending on the mode, we will either move the building or
	//copy it.  The layout automatically gets deleted as soon as the user releases the mouse
	//button.
}

//depending on the offset, we will either sort in increasing order, or decreasing order.
//This will prevent overlapping problems.
void SortBuildingLayout( iMapIndex )
{
	BUILDINGLAYOUTNODE *head, *curr, *prev, *prevBest, *best;
	INT32 iBestIndex;
	head = NULL;
	if( iMapIndex < gsBuildingLayoutAnchorGridNo )
	{	//Forward sort (in increasing order)
		while( gpBuildingLayoutList )
		{
			iBestIndex = -1;
			curr = gpBuildingLayoutList;
			prev = NULL;
			while( curr )
			{
				if( iBestIndex < curr->sGridNo )
				{
					iBestIndex = curr->sGridNo;
					prevBest = prev;
					best = curr;
				}
				prev = curr;
				curr = curr->next;
			}
			//detach node from real list
			if( prevBest )
				prevBest->next = best->next;
			if( best == gpBuildingLayoutList )
				gpBuildingLayoutList = gpBuildingLayoutList->next;
			//insert node into temp sorted list
			best->next = head;
			head = best;
		}
	}
	else
	{ //Reverse sort (in decreasing order)
		while( gpBuildingLayoutList )
		{
			iBestIndex = 100000;
			curr = gpBuildingLayoutList;
			prev = NULL;
			while( curr )
			{
				if( iBestIndex > curr->sGridNo )
				{
					iBestIndex = curr->sGridNo;
					prevBest = prev;
					best = curr;
				}
				prev = curr;
				curr = curr->next;
			}
			//detach node from real list
			if( prevBest )
				prevBest->next = best->next;
			if( best == gpBuildingLayoutList )
				gpBuildingLayoutList = gpBuildingLayoutList->next;
			//insert node into temp sorted list
			best->next = head;
			head = best;
		}
	}
	//Now assign the newly sorted list back to the real list.
	gpBuildingLayoutList = head;
}

void PasteMapElementToNewMapElement( INT32 iSrcGridNo, INT32 iDstGridNo )
{
	MAP_ELEMENT			*pSrcMapElement;
	LEVELNODE				*pNode;
	UINT16					usType;
	
	DeleteStuffFromMapTile( iDstGridNo );
	DeleteAllLandLayers( iDstGridNo );

	//Get a pointer to the src mapelement
	pSrcMapElement = &gpWorldLevelData[ iSrcGridNo ];

	//Go through each levelnode, and paste the info into the new gridno
	pNode = pSrcMapElement->pLandHead;
	while( pNode )
	{
		if( pNode == pSrcMapElement->pLandStart )
			gpWorldLevelData[ iDstGridNo ].pLandStart = AddLandToTail( iDstGridNo, pNode->usIndex );
		else
			AddLandToTail( iDstGridNo, pNode->usIndex );
		pNode = pNode->pNext;
	}
	pNode = pSrcMapElement->pObjectHead;
	while( pNode )
	{
		AddObjectToTail( iDstGridNo, pNode->usIndex );
		pNode = pNode->pNext;
	}
	pNode = pSrcMapElement->pStructHead;
	while( pNode )
	{
		AddStructToTail( iDstGridNo, pNode->usIndex );
		pNode = pNode->pNext;
	}
	pNode = pSrcMapElement->pShadowHead;
	while( pNode )
	{
		AddShadowToTail( iDstGridNo, pNode->usIndex );
		pNode = pNode->pNext;
	}
	pNode = pSrcMapElement->pRoofHead;
	while( pNode )
	{
		AddRoofToTail( iDstGridNo, pNode->usIndex );
		pNode = pNode->pNext;
	}
	pNode = pSrcMapElement->pOnRoofHead;
	while( pNode )
	{
		AddOnRoofToTail( iDstGridNo, pNode->usIndex );
		pNode = pNode->pNext;
	}
	pNode = pSrcMapElement->pTopmostHead;
	while( pNode )
	{
		if( pNode->usIndex != FIRSTPOINTERS1 )
			AddTopmostToTail( iDstGridNo, pNode->usIndex );
		pNode = pNode->pNext;
	}
	for ( usType = FIRSTROOF; usType <= LASTSLANTROOF; usType++ )
	{
		HideStructOfGivenType( iDstGridNo, usType, (BOOLEAN)(!fBuildingShowRoofs) );
	}
}

void MoveBuilding( INT32 iMapIndex )
{
	BUILDINGLAYOUTNODE *curr;
	INT32 iOffset;
	if( !gpBuildingLayoutList )
		return;
	SortBuildingLayout( iMapIndex );
	iOffset = iMapIndex - gsBuildingLayoutAnchorGridNo;
	//First time, set the undo gridnos to everything effected.
	curr = gpBuildingLayoutList;
	while( curr )
	{
		AddToUndoList( curr->sGridNo );
		AddToUndoList( curr->sGridNo + iOffset );
		curr = curr->next;
	}
	//Now, move the building
	curr = gpBuildingLayoutList;
	while( curr )
	{
		PasteMapElementToNewMapElement( curr->sGridNo, curr->sGridNo + iOffset );
		DeleteStuffFromMapTile( curr->sGridNo );
		curr = curr->next;
	}
	MarkWorldDirty();
}

void PasteBuilding( INT32 iMapIndex )
{
	BUILDINGLAYOUTNODE *curr;
	INT32 iOffset;
	if( !gpBuildingLayoutList )
		return;
	SortBuildingLayout( iMapIndex );
	iOffset = iMapIndex - gsBuildingLayoutAnchorGridNo;
	curr = gpBuildingLayoutList;
	//First time, set the undo gridnos to everything effected.
	while( curr )
	{
		AddToUndoList( curr->sGridNo );
		AddToUndoList( curr->sGridNo + iOffset );
		curr = curr->next;
	}
	//Now, paste the building (no smoothing)
	curr = gpBuildingLayoutList;
	while( curr )
	{
		PasteMapElementToNewMapElement( curr->sGridNo, curr->sGridNo + iOffset );
		curr = curr->next;
	}
	MarkWorldDirty();
}

typedef struct ROOFNODE
{
	INT32 iMapIndex;
	struct ROOFNODE *next;
}ROOFNODE;

ROOFNODE *gpRoofList = NULL;

void ReplaceRoof( INT32 iMapIndex, UINT16 usRoofType )
{
	ROOFNODE *curr;
	//First, validate the gridno
	if( iMapIndex < 0 && iMapIndex >= WORLD_COLS*WORLD_ROWS )
		return;
	//Now, check if there is a floor here
	if( !FloorAtGridNo( iMapIndex ) )
		return;
	//Now, check to make sure this gridno hasn't already been processed.
	curr = gpRoofList;
	while( curr )
	{
		if( iMapIndex == curr->iMapIndex )
			return;
		curr = curr->next;
	}
	//Good, it hasn't, so process it and add it to the head of the list.
	curr = (ROOFNODE*)MemAlloc( sizeof( ROOFNODE ) );
	Assert( curr );
	curr->iMapIndex = iMapIndex;
	curr->next = gpRoofList;
	gpRoofList = curr;

	RebuildRoofUsingFloorInfo( iMapIndex, usRoofType );

	//Use recursion to process the remainder.
	ReplaceRoof( iMapIndex - WORLD_COLS, usRoofType );
	ReplaceRoof( iMapIndex + WORLD_COLS, usRoofType );
	ReplaceRoof( iMapIndex - 1, usRoofType );
	ReplaceRoof( iMapIndex + 1, usRoofType );
}

void ReplaceBuildingWithNewRoof( INT32 iMapIndex )
{
	UINT16 usRoofType;
	ROOFNODE *curr;
	//Not in normal editor mode, then can't do it.
	if( gfBasement || gfCaves )
		return;
	//if we don't have a floor here, then we can't replace the roof!
	if( !FloorAtGridNo( iMapIndex ) )
		return;
	//Extract the selected roof type.
	usRoofType = (UINT16)SelSingleNewRoof[ iCurBank ].uiObject;

	//now start building a linked list of all nodes visited -- start the first node.
	gpRoofList = (ROOFNODE*)MemAlloc( sizeof( ROOFNODE ) );
	Assert( gpRoofList );
	gpRoofList->iMapIndex = iMapIndex;
	gpRoofList->next = 0;
	RebuildRoofUsingFloorInfo( iMapIndex, usRoofType );

	//Use recursion to process the remainder.
	ReplaceRoof( iMapIndex - WORLD_COLS, usRoofType );
	ReplaceRoof( iMapIndex + WORLD_COLS, usRoofType );
	ReplaceRoof( iMapIndex - 1, usRoofType );
	ReplaceRoof( iMapIndex + 1, usRoofType );

	//Done, so delete the list.
	while( gpRoofList )
	{
		curr = gpRoofList;
		gpRoofList = gpRoofList->next;
		MemFree( curr );
	}
	gpRoofList = NULL;
}

//internal door editing vars.
INT32 iDoorMapIndex = 0;
enum{
	DOOR_BACKGROUND,
	DOOR_OKAY,
	DOOR_CANCEL,
	DOOR_LOCKED,
	NUM_DOOR_BUTTONS
};
INT32 iDoorButton[ NUM_DOOR_BUTTONS ];
MOUSE_REGION DoorRegion;
void DoorOkayCallback( GUI_BUTTON *btn, INT32 reason );
void DoorCancelCallback( GUI_BUTTON *btn, INT32 reason );
void DoorToggleLockedCallback( GUI_BUTTON *btn, INT32 reason );

extern BOOLEAN OpenableAtGridNo( UINT32 iMapIndex );

void InitDoorEditing( INT32 iMapIndex )
{
	DOOR *pDoor;
	if( !DoorAtGridNo( iMapIndex ) && !OpenableAtGridNo( iMapIndex ) )
		return;
	gfEditingDoor = TRUE;
	iDoorMapIndex = iMapIndex;
	DisableEditorTaskbar();
	MSYS_DefineRegion( &DoorRegion, 0, 0, 640, 480, MSYS_PRIORITY_HIGH-2, 0, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK );
	iDoorButton[ DOOR_BACKGROUND ] =
		CreateTextButton( 0, 0, 0, 0, BUTTON_USE_DEFAULT, 200, 130, 240, 100, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH - 1, 
		BUTTON_NO_CALLBACK, BUTTON_NO_CALLBACK );
	DisableButton( iDoorButton[ DOOR_BACKGROUND ] );
	SpecifyDisabledButtonStyle( iDoorButton[ DOOR_BACKGROUND ], DISABLED_STYLE_NONE );
	iDoorButton[ DOOR_OKAY ] =
		CreateTextButton(L"Okay", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT,
		330, 195, 50, 30, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, 
		DoorOkayCallback );
	iDoorButton[ DOOR_CANCEL ] =
		CreateTextButton(L"Cancel", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT,
		385, 195, 50, 30, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, 
		DoorCancelCallback );
	InitTextInputModeWithScheme( DEFAULT_SCHEME );
	AddTextInputField( 210, 155, 25, 16, MSYS_PRIORITY_HIGH, L"0", 3, INPUTTYPE_NUMERICSTRICT );
	AddTextInputField( 210, 175, 25, 16, MSYS_PRIORITY_HIGH, L"0", 2, INPUTTYPE_NUMERICSTRICT );
	AddTextInputField( 210, 195, 25, 16, MSYS_PRIORITY_HIGH, L"0", 2, INPUTTYPE_NUMERICSTRICT );
	iDoorButton[ DOOR_LOCKED ] = 
		CreateCheckBoxButton(	210, 215, "EDITOR//SmCheckbox.sti", MSYS_PRIORITY_HIGH, DoorToggleLockedCallback );

	pDoor = FindDoorInfoAtGridNo( iDoorMapIndex );
	if( pDoor )
	{
		if( pDoor->fLocked )
		{
			ButtonList[ iDoorButton[ DOOR_LOCKED ] ]->uiFlags |= BUTTON_CLICKED_ON;
		}
		SetInputFieldStringWithNumericStrictValue( 0, pDoor->ubLockID );
		SetInputFieldStringWithNumericStrictValue( 1, pDoor->ubTrapID );
		SetInputFieldStringWithNumericStrictValue( 2, pDoor->ubTrapLevel );
	}
	else
	{
		ButtonList[ iDoorButton[ DOOR_LOCKED ] ]->uiFlags |= BUTTON_CLICKED_ON;
	}
}

void ExtractAndUpdateDoorInfo()
{
	LEVELNODE* pNode;
	INT32 num;
	DOOR door;
	BOOLEAN fCursor = FALSE;
	BOOLEAN fCursorExists = FALSE;

	memset( &door, 0, sizeof( DOOR ) );

	door.sGridNo = (INT16)iDoorMapIndex;

	num = min( GetNumericStrictValueFromField( 0 ), NUM_LOCKS-1 );
	door.ubLockID = (UINT8)num;
	SetInputFieldStringWithNumericStrictValue( 0, num );
	if( num >= 0 )
		fCursor = TRUE;

	num = min( max( GetNumericStrictValueFromField( 1 ), 0 ), 10 );
	door.ubTrapID = (UINT8)num;
	SetInputFieldStringWithNumericStrictValue( 1, num );
	if( num ) 
		fCursor = TRUE;

	num = min( max( GetNumericStrictValueFromField( 2 ), 0 ), 20 );
	if( door.ubTrapID && !num )
		num = 1;  //Can't have a trap without a traplevel!
	door.ubTrapLevel = (UINT8)num;
	SetInputFieldStringWithNumericStrictValue( 2, num );
	if( num ) 
		fCursor = TRUE;

	if( ButtonList[ iDoorButton[ DOOR_LOCKED ] ]->uiFlags & BUTTON_CLICKED_ON )
	{
		door.fLocked = TRUE;		
	}
	else
	{
		door.fLocked = FALSE;
	}

	//Find out if we have a rotating key cursor (we will either add one or remove one)
	pNode = gpWorldLevelData[ iDoorMapIndex ].pTopmostHead;
	while( pNode )
	{
		if( pNode->usIndex == ROTATINGKEY1 )
		{
			fCursorExists = TRUE;
			break;
		}
		pNode = pNode->pNext;
	}
	if( fCursor )
	{ //we have a valid door, so add it (or replace existing)
		if( !fCursorExists )
			AddTopmostToHead( iDoorMapIndex, ROTATINGKEY1 );
		//If the door already exists, the new information will replace it.
		AddDoorInfoToTable( &door );
	}
	else
	{ //if a door exists here, remove it.
		if( fCursorExists )
			RemoveAllTopmostsOfTypeRange( iDoorMapIndex, ROTATINGKEY, ROTATINGKEY );
		RemoveDoorInfoFromTable( iDoorMapIndex );
	}

}

void FindNextLockedDoor()
{
	DOOR *pDoor;
	INT32 i;
	for( i = iDoorMapIndex + 1; i < WORLD_MAX; i++ )
	{
		pDoor = FindDoorInfoAtGridNo( i );
		if( pDoor )
		{
			CenterScreenAtMapIndex( i );
			iDoorMapIndex = i;
			return;
		}
	}
	for( i = 0; i <= iDoorMapIndex; i++ )
	{
		pDoor = FindDoorInfoAtGridNo( i );
		if( pDoor )
		{
			CenterScreenAtMapIndex( i );
			iDoorMapIndex = i;
			return;
		}
	}
}

void RenderDoorEditingWindow()
{
	InvalidateRegion( 200, 130, 440, 230 );
	SetFont( FONT10ARIAL );
	SetFontForeground( FONT_YELLOW );
	SetFontShadow( FONT_NEARBLACK );
	SetFontBackground( 0 );
	mprintf( 210, 140, L"Editing lock attributes at map index %d.", iDoorMapIndex );

	SetFontForeground( FONT_GRAY2 );
	mprintf( 238, 160, L"Lock ID" );
	mprintf( 238, 180, L"Trap Type" );
	mprintf( 238, 200, L"Trap Level" );
	mprintf( 238, 218, L"Locked" );
}

void KillDoorEditing()
{
	INT32 i;
	EnableEditorTaskbar();
	MSYS_RemoveRegion( &DoorRegion );
	for( i = 0; i < NUM_DOOR_BUTTONS; i++ )
		RemoveButton( iDoorButton[ i ] );
	gfEditingDoor = FALSE;
	KillTextInputMode();
}

void DoorOkayCallback( GUI_BUTTON *btn, INT32 reason )
{
	if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		ExtractAndUpdateDoorInfo();
		KillDoorEditing();
	}
}

void DoorCancelCallback( GUI_BUTTON *btn, INT32 reason )
{
	if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		KillDoorEditing();
	}
}

void DoorToggleLockedCallback( GUI_BUTTON *btn, INT32 reason )
{
	//handled in ExtractAndUpdateDoorInfo();
}

void AddLockedDoorCursors()
{
	DOOR *pDoor;
	INT i;
	for( i = 0; i < gubNumDoors; i++ )
	{
		pDoor = &DoorTable[ i ];
		AddTopmostToHead( pDoor->sGridNo, ROTATINGKEY1 );
	}
}

void RemoveLockedDoorCursors()
{
	DOOR *pDoor;
	INT i;
	LEVELNODE* pNode;
	LEVELNODE* pTemp;
	for( i = 0; i < gubNumDoors; i++ )
	{
		pDoor = &DoorTable[ i ];
		pNode = gpWorldLevelData[ pDoor->sGridNo ].pTopmostHead;
		while( pNode )
		{
			if( pNode->usIndex == ROTATINGKEY1 )
			{
				pTemp = pNode;
				pNode = pNode->pNext;
				RemoveTopmost( pDoor->sGridNo, pTemp->usIndex );
			}
			else
				pNode = pNode->pNext;
		}
	}
}

void SetupTextInputForBuildings()
{
	UINT16 str[4];
	InitTextInputModeWithScheme( DEFAULT_SCHEME );
	AddUserInputField( NULL );  //just so we can use short cut keys while not typing.
	swprintf( str, L"%d", gubMaxRoomNumber );
	AddTextInputField( 410, 400, 25, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
}

void ExtractAndUpdateBuildingInfo()
{
	UINT16 str[4];
	INT32 temp;
	//extract light1 colors
	temp = min( GetNumericStrictValueFromField( 1 ), 255 );
	if( temp != -1 )
	{
		gubCurrRoomNumber = (UINT8)temp;
	}
	else
	{
		gubCurrRoomNumber = 0;
	}
	swprintf( str, L"%d", gubCurrRoomNumber );
	SetInputFieldStringWith16BitString( 1, str );
	SetActiveField( 0 );
}



#endif