
//=================================================================================================
//	MouseSystem.c
//
//	Routines for handling prioritized mouse regions. The system as setup below allows the use of
//	callback functions for each region, as well as allowing a different cursor to be defined for
//	each region.
//
//	Written by Bret Rowdon, Jan 30 '97
//	Re-Written by Kris Morness, since...
//
//=================================================================================================

#ifdef JA2_PRECOMPILED_HEADERS
	#include "JA2 SGP ALL.H"
#elif defined( WIZ8_PRECOMPILED_HEADERS )
	#include "WIZ8 SGP ALL.H"
#else
	#include "types.h"
	#include <windows.h>
	#include <stdio.h>
	#include <memory.h>
	#include "debug.h"
	#include "input.h"
	#include "memman.h"
	#include "line.h"
	#if (defined( JA2 ) || defined( UTIL ))
		#include "video.h"
		#define BASE_REGION_FLAGS		(MSYS_REGION_ENABLED | MSYS_SET_CURSOR)
	#else
		#include "video2.h"
		#define BASE_REGION_FLAGS		MSYS_REGION_ENABLED				// Wiz doesn't ever want MSYS_SET_CURSOR to be on...
	#endif
	#ifdef _JA2_RENDER_DIRTY
		#include "render dirty.h"
		#include "Font Control.h"
	#endif
	#include "english.h"
	// Include mouse system defs and macros
	#include "mousesystem.h"
	#include "Cursor Control.h"
	#include "Button System.h"
	///***ddd
	#include "GameSettings.h"
#endif

#ifdef JA2_PRECOMPILED_HEADERS
	#define BASE_REGION_FLAGS		(MSYS_REGION_ENABLED | MSYS_SET_CURSOR)
#elif defined( WIZ8_PRECOMPILED_HEADERS )
	#define BASE_REGION_FLAGS		MSYS_REGION_ENABLED				// Wiz doesn't ever want MSYS_SET_CURSOR to be on...
#endif



//Kris:	Nov 31, 1999 -- Added support for double clicking
//
//Max double click delay (in milliseconds) to be considered a double click
#define MSYS_DOUBLECLICK_DELAY		400
//
//Records and stores the last place the user clicked.	These values are compared to the current
//click to determine if a double click event has been detected.
MOUSE_REGION	*gpRegionLastLButtonDown = NULL;
MOUSE_REGION	*gpRegionLastLButtonUp = NULL;
UINT32				guiRegionLastLButtonDownTime = 0;




extern void ReleaseAnchorMode();	//private function used here (implemented in Button System.c)

// number of lines in height help text will be
INT16 GetNumberOfLinesInHeight( const STR16 pStringA );
INT16 GetWidthOfString( const STR16 pStringA );
void DisplayHelpTokenizedString( const STR16 pStringA, INT16 sX, INT16 sY );



INT32 MSYS_ScanForID=FALSE;
INT32 MSYS_CurrentID=MSYS_ID_SYSTEM;

INT16 MSYS_CurrentMX=0;
INT16 MSYS_CurrentMY=0;
INT16 MSYS_CurrentButtons=0;
INT16 MSYS_Action=MSYS_NO_ACTION;
INT16 MSYS_Wheel=0;

BOOLEAN	MSYS_SystemInitialized=FALSE;
BOOLEAN MSYS_UseMouseHandlerHook=FALSE;

BOOLEAN MSYS_Mouse_Grabbed=FALSE;
MOUSE_REGION *MSYS_GrabRegion = NULL;

UINT16				gusClickedIDNumber;
BOOLEAN				gfClickedModeOn = FALSE;

MOUSE_REGION *MSYS_RegList = NULL;

MOUSE_REGION *MSYS_PrevRegion = NULL;
MOUSE_REGION *MSYS_CurrRegion = NULL;

//When set, the fast help text will be instantaneous, if consecutive regions with help text are
//hilighted.	It is set, whenever the timer for the first help button expires, and the mode is
//cleared as soon as the cursor moves into no region or a region with no helptext.
BOOLEAN gfPersistantFastHelpMode;

INT16	gsFastHelpDelay = 600; // In timer ticks
BOOLEAN gfShowFastHelp	= TRUE;

// help text is done, now execute callback, if there is one
void ExecuteMouseHelpEndCallBack( MOUSE_REGION *region );



//Kris:
//NOTE:	This doesn't really need to be here, however, it is a good indication that
//when an error appears here, that you need to go below to the init code and initialize the
//values there as well.	That's the only reason why I left this here.
MOUSE_REGION MSYS_SystemBaseRegion = {
								MSYS_ID_SYSTEM, MSYS_PRIORITY_SYSTEM, BASE_REGION_FLAGS,
								-32767, -32767, 32767, 32767, 0, 0, 0, 0, 0, 0, 0,
								MSYS_NO_CALLBACK, MSYS_NO_CALLBACK, { 0,0,0,0 },
								0, 0, -1, MSYS_NO_CALLBACK, NULL, NULL };

BOOLEAN					gfRefreshUpdate = FALSE;

//Kris:	December 3, 1997
//Special internal debugging utilities that will ensure that you don't attempt to delete
//an already deleted region.	It will also ensure that you don't create an identical region
//that already exists.
//TO REMOVE ALL DEBUG FUNCTIONALITY:	simply comment out MOUSESYSTEM_DEBUGGING definition
#ifdef JA2
	#ifdef _DEBUG
	#ifndef BOUNDS_CHECKER
	#define MOUSESYSTEM_DEBUGGING
	#endif
	#endif
#endif

#ifdef MOUSESYSTEM_DEBUGGING
extern BOOLEAN gfIgnoreShutdownAssertions;
#endif

//======================================================================================================
//	MSYS_Init
//
//	Initialize the mouse system.
//
INT32 MSYS_Init(void)
{
	RegisterDebugTopic(TOPIC_MOUSE_SYSTEM, "Mouse Region System");

	#ifdef MOUSESYSTEM_DEBUGGING
		gfIgnoreShutdownAssertions = FALSE;
	#endif
	if(MSYS_RegList!=NULL)
		MSYS_TrashRegList();

	MSYS_CurrentID = MSYS_ID_SYSTEM;
	MSYS_ScanForID = FALSE;

	MSYS_CurrentMX = 0;
	MSYS_CurrentMY = 0;
	MSYS_CurrentButtons = 0;
	MSYS_Wheel = 0;
	MSYS_Action=MSYS_NO_ACTION;

	MSYS_PrevRegion = NULL;
	MSYS_SystemInitialized = TRUE;
	MSYS_UseMouseHandlerHook = FALSE;

	MSYS_Mouse_Grabbed=FALSE;
	MSYS_GrabRegion = NULL;

	// Setup the system's background region
	MSYS_SystemBaseRegion.IDNumber				= MSYS_ID_SYSTEM;
	MSYS_SystemBaseRegion.PriorityLevel			= MSYS_PRIORITY_SYSTEM;
	MSYS_SystemBaseRegion.uiFlags				= BASE_REGION_FLAGS;
	MSYS_SystemBaseRegion.RegionTopLeftX		= -32767;
	MSYS_SystemBaseRegion.RegionTopLeftY		= -32767;
	MSYS_SystemBaseRegion.RegionBottomRightX	= 32767;
	MSYS_SystemBaseRegion.RegionBottomRightY	= 32767;
	MSYS_SystemBaseRegion.MouseXPos				= 0;
	MSYS_SystemBaseRegion.MouseYPos				= 0;
	MSYS_SystemBaseRegion.RelativeXPos			= 0;
	MSYS_SystemBaseRegion.RelativeYPos			= 0;
	MSYS_SystemBaseRegion.ButtonState			= 0;
	MSYS_SystemBaseRegion.Cursor				= 0;
	MSYS_SystemBaseRegion.WheelState			= 0;
	MSYS_SystemBaseRegion.UserData[0]			= 0;
	MSYS_SystemBaseRegion.UserData[1]			= 0;
	MSYS_SystemBaseRegion.UserData[2]			= 0;
	MSYS_SystemBaseRegion.UserData[3]			= 0;
	MSYS_SystemBaseRegion.MovementCallback		= MSYS_NO_CALLBACK;
	MSYS_SystemBaseRegion.ButtonCallback		= MSYS_NO_CALLBACK;

	MSYS_SystemBaseRegion.FastHelpTimer				= 0;
	MSYS_SystemBaseRegion.FastHelpText				= 0;
	MSYS_SystemBaseRegion.FastHelpRect				= -1;

	MSYS_SystemBaseRegion.next								= NULL;
	MSYS_SystemBaseRegion.prev								= NULL;

	// Add the base region to the list
	MSYS_AddRegionToList(&MSYS_SystemBaseRegion);

#ifdef _MOUSE_SYSTEM_HOOK_
	MSYS_UseMouseHandlerHook = TRUE;
#endif

	return(1);
}
/*
LONG WINAPI UEFilter(PEXCEPTION_POINTERS pEI)
{
    MessageBox(NULL, ("Exception"), ("Oops !!!"), MB_ICONSTOP);
    TerminateProcess(GetCurrentProcess(), -1);    
    return -1;
}
	SetUnhandledExceptionFilter(UEFilter);
 
*(char*)0 = 0;
*/


//======================================================================================================
//	MSYS_Shutdown
//
//	De-inits the "mousesystem" mouse region handling code.
//
void MSYS_Shutdown(void)
{
	#ifdef MOUSESYSTEM_DEBUGGING
		gfIgnoreShutdownAssertions = TRUE;
	#endif
	MSYS_SystemInitialized = FALSE;
	MSYS_UseMouseHandlerHook = FALSE;
	MSYS_TrashRegList();
	UnRegisterDebugTopic(TOPIC_MOUSE_SYSTEM, "Mouse Region System");
}



//======================================================================================================
//	MSYS_SGP_Mouse_Handler_Hook
//
//	Hook to the SGP's mouse handler
//
void MSYS_SGP_Mouse_Handler_Hook(UINT16 Type,UINT16 Xcoord, UINT16 Ycoord, BOOLEAN LeftButton, BOOLEAN RightButton)
{


	// If the mouse system isn't initialized, get out o' here
	if(!MSYS_SystemInitialized)
			return;

	// If we're not using the handler stuff, ignore this call
	if(!MSYS_UseMouseHandlerHook)
			return;

	if(!gGameExternalOptions.fExtMouseKeyEnabled &&
		(Type == MIDDLE_BUTTON_UP || Type == MIDDLE_BUTTON_DOWN || 	Type == X1_BUTTON_DOWN || 
		Type == X1_BUTTON_UP || Type == X2_BUTTON_DOWN || Type == X2_BUTTON_UP || Type == X1_BUTTON_REPEAT || 
		Type == X2_BUTTON_REPEAT || Type == MOUSE_WHEEL_UP || Type == MOUSE_WHEEL_DOWN  )		)
		return;

	MSYS_Action=MSYS_NO_ACTION;
	switch(Type)
	{
		case LEFT_BUTTON_DOWN:
		case LEFT_BUTTON_UP:
		case RIGHT_BUTTON_DOWN:
		case RIGHT_BUTTON_UP:
		case MIDDLE_BUTTON_UP:
		case MIDDLE_BUTTON_DOWN:
		case X1_BUTTON_DOWN:
		case X1_BUTTON_UP:
		case X2_BUTTON_DOWN:
		case X2_BUTTON_UP:

			//MSYS_Action|=MSYS_DO_BUTTONS;
			if(Type == LEFT_BUTTON_DOWN)
			{
				MSYS_Action |= MSYS_DO_LBUTTON_DWN;
				MSYS_CurrentButtons|=MSYS_LEFT_BUTTON;
			}
			else if(Type == LEFT_BUTTON_UP)
			{
				MSYS_Action |= MSYS_DO_LBUTTON_UP;
				MSYS_CurrentButtons&=(~MSYS_LEFT_BUTTON);
				//Kris:
				//Used only if applicable.	This is used for that special button that is locked with the
				//mouse press -- just like windows.	When you release the button, the previous state
				//of the button is restored if you released the mouse outside of it's boundaries.	If
				//you release inside of the button, the action is selected -- but later in the code.
				//NOTE:	It has to be here, because the mouse can be released anywhere regardless of
				//regions, buttons, etc.
				#ifdef JA2
					ReleaseAnchorMode();
				#endif
			}
			else if(Type == RIGHT_BUTTON_DOWN)
			{
				MSYS_Action |= MSYS_DO_RBUTTON_DWN;
				MSYS_CurrentButtons|=MSYS_RIGHT_BUTTON;
			}
			else if(Type == RIGHT_BUTTON_UP)
			{
				MSYS_Action |= MSYS_DO_RBUTTON_UP;
				MSYS_CurrentButtons&=(~MSYS_RIGHT_BUTTON);
			}
			else if(Type == MIDDLE_BUTTON_DOWN)
			{
				MSYS_Action |= MSYS_DO_MBUTTON_DWN;
				MSYS_CurrentButtons|=MSYS_MIDDLE_BUTTON;
			}
			else if(Type == MIDDLE_BUTTON_UP)
			{
				MSYS_Action |= MSYS_DO_MBUTTON_UP;
				MSYS_CurrentButtons&=(~MSYS_MIDDLE_BUTTON);
			}
			else if(Type == X1_BUTTON_DOWN)
			{
				MSYS_Action |= MSYS_DO_X1BUTTON_DWN;
				MSYS_CurrentButtons|=MSYS_X1_BUTTON;
			}
			else if(Type == X1_BUTTON_UP)
			{
				MSYS_Action |= MSYS_DO_X1BUTTON_UP;
				MSYS_CurrentButtons&=(~MSYS_X1_BUTTON);
			}
			else if(Type == X2_BUTTON_DOWN)
			{
				MSYS_Action |= MSYS_DO_X2BUTTON_DWN;
				MSYS_CurrentButtons|=MSYS_X2_BUTTON;
			}
			else if(Type == X2_BUTTON_UP)
			{
				MSYS_Action |= MSYS_DO_X2BUTTON_UP;
				MSYS_CurrentButtons&=(~MSYS_X2_BUTTON);
			}

			if(LeftButton)
				MSYS_CurrentButtons|=MSYS_LEFT_BUTTON;
			else
				MSYS_CurrentButtons&=(~MSYS_LEFT_BUTTON);

			if(RightButton)
				MSYS_CurrentButtons|=MSYS_RIGHT_BUTTON;
			else
				MSYS_CurrentButtons&=(~MSYS_RIGHT_BUTTON);

			if((Xcoord != MSYS_CurrentMX) || (Ycoord != MSYS_CurrentMY))
			{
				MSYS_Action|=MSYS_DO_MOVE;
				MSYS_CurrentMX = Xcoord;
				MSYS_CurrentMY = Ycoord;
			}

			MSYS_UpdateMouseRegion();
			break;

		// ATE: Checks here for mouse button repeats.....
		// Call mouse region with new reason
		case LEFT_BUTTON_REPEAT:
		case RIGHT_BUTTON_REPEAT:
		case MIDDLE_BUTTON_REPEAT:
		case X1_BUTTON_REPEAT:
		case X2_BUTTON_REPEAT:

			if(Type == LEFT_BUTTON_REPEAT)
				MSYS_Action |= MSYS_DO_LBUTTON_REPEAT;
			else if(Type == RIGHT_BUTTON_REPEAT)
				MSYS_Action |= MSYS_DO_RBUTTON_REPEAT;
			else if(Type == MIDDLE_BUTTON_REPEAT)
				MSYS_Action |= MSYS_DO_MBUTTON_REPEAT;
			else if(Type == X1_BUTTON_REPEAT)
				MSYS_Action |= MSYS_DO_X1BUTTON_REPEAT;
			else if(Type == X2_BUTTON_REPEAT)
				MSYS_Action |= MSYS_DO_X2BUTTON_REPEAT;

			if((Xcoord != MSYS_CurrentMX) || (Ycoord != MSYS_CurrentMY))
			{
				MSYS_Action|=MSYS_DO_MOVE;
				MSYS_CurrentMX = Xcoord;
				MSYS_CurrentMY = Ycoord;
			}

			MSYS_UpdateMouseRegion();
			break;
		case MOUSE_WHEEL_UP:
		case MOUSE_WHEEL_DOWN:
			if(Type == MOUSE_WHEEL_UP)
			{
				MSYS_Action |= MSYS_DO_WHEEL_UP;
				MSYS_Wheel++;
			}
			else if(Type == MOUSE_WHEEL_DOWN)
			{
				MSYS_Action |= MSYS_DO_WHEEL_DOWN;
				MSYS_Wheel--;
			}
			MSYS_UpdateMouseRegion();
			break;

		case MOUSE_POS:
			if((Xcoord != MSYS_CurrentMX) || (Ycoord != MSYS_CurrentMY) || gfRefreshUpdate )
			{
				MSYS_Action|=MSYS_DO_MOVE;
				MSYS_CurrentMX = Xcoord;
				MSYS_CurrentMY = Ycoord;

				gfRefreshUpdate = FALSE;

				MSYS_UpdateMouseRegion();
			}
			break;

		default:
		DbgMessage(TOPIC_MOUSE_SYSTEM, DBG_LEVEL_0, "ERROR -- MSYS 2 SGP Mouse Hook got bad type");
			break;
	}
	// check for moved mouse
	if((Xcoord != MSYS_CurrentMX) || (Ycoord != MSYS_CurrentMY) || gfRefreshUpdate )
	{
		MSYS_Action|=MSYS_DO_MOVE;
		MSYS_CurrentMX = Xcoord;
		MSYS_CurrentMY = Ycoord;
	}
	
	// update if something happened
	if (MSYS_Action != MSYS_NO_ACTION)
		MSYS_UpdateMouseRegion();
}



//======================================================================================================
//	MSYS_GetNewID
//
//	Returns a unique ID number for region nodes. If no new ID numbers can be found, the MAX value
//	is returned.
//
INT32 MSYS_GetNewID(void)
{
	INT32 retID;
	INT32 Current,found,done;
	MOUSE_REGION *node;

	retID = MSYS_CurrentID;
	MSYS_CurrentID++;

	// Crapy scan for an unused ID
	if((MSYS_CurrentID >= MSYS_ID_MAX) || MSYS_ScanForID)
	{
		MSYS_ScanForID = TRUE;
		Current = MSYS_ID_BASE;
		done=found=FALSE;
		while(!done)
		{
			found=FALSE;
			node=MSYS_RegList;
			while(node!=NULL && !found)
			{
				if(node->IDNumber == Current)
					found=TRUE;
			}

			if(found && Current < MSYS_ID_MAX)	// Current ID is in use, and their are more to scan
				Current++;
			else
			{
				done=TRUE;						// Got an ID to use.
				if(found)
					Current=MSYS_ID_MAX;		// Ooops, ran out of IDs, use MAX value!
			}
		}
		MSYS_CurrentID = Current;
	}

	return(retID);
}



//======================================================================================================
//	MSYS_TrashRegList
//
//	Deletes the entire region list.
//
void MSYS_TrashRegList(void)
{
	while( MSYS_RegList )
	{
		if( MSYS_RegList->uiFlags & MSYS_REGION_EXISTS )
		{
			MSYS_RemoveRegion(MSYS_RegList);
		}
		else
		{
			MSYS_RegList = MSYS_RegList->next;
		}
	} 
}



//======================================================================================================
//	MSYS_AddRegionToList
//
//	Add a region struct to the current list. The list is sorted by priority levels. If two entries
//	have the same priority level, then the latest to enter the list gets the higher priority.
//
void MSYS_AddRegionToList(MOUSE_REGION *region)
{
	MOUSE_REGION *curr;
	INT32 done;


	// If region seems to already be in list, delete it so we can
	// re-insert the region.
	if( region->next || region->prev )
	{ // if it wasn't actually there, then call does nothing!
		MSYS_DeleteRegionFromList(region);
	}

	// Set an ID number!
	region->IDNumber = (UINT16)MSYS_GetNewID();

	region->next = NULL;
	region->prev = NULL;

	if( !MSYS_RegList )
	{ // Null list, so add it straight up.
		MSYS_RegList = region;
	}
	else
	{
		// Walk down list until we find place to insert (or at end of list)
		curr=MSYS_RegList;
		done=FALSE;
		while((curr->next != NULL) && !done)
		{
			if(curr->PriorityLevel <= region->PriorityLevel)
				done=TRUE;
			else
				curr = curr->next;
		}

		if(curr->PriorityLevel > region->PriorityLevel)
		{
			// Add after curr node
			region->next = curr->next;
			curr->next = region;
			region->prev = curr;
			if(region->next != NULL)
				region->next->prev = region;
		}
		else
		{
			// Add before curr node
			region->next = curr;
			region->prev = curr->prev;

			curr->prev = region;
			if(region->prev != NULL)
				region->prev->next = region;

			if(MSYS_RegList==curr)	// Make sure if adding at start, to adjust the list pointer
				MSYS_RegList=region;
		}
	}
}



//======================================================================================================
//	MSYS_RegionInList
//
//	Scan region list for presence of a node with the same region ID number
//
INT32 MSYS_RegionInList( MOUSE_REGION *region )
{
	MOUSE_REGION *Current;
	INT32 found;

	found = FALSE;
	Current = MSYS_RegList;
	while( Current && !found )
	{
		if(Current->IDNumber == region->IDNumber)
			found=TRUE;
		Current = Current->next;
	}
	return(found);
}



//======================================================================================================
//	MSYS_DeleteRegionFromList
//
//	Removes a region from the current list.
//
void MSYS_DeleteRegionFromList(MOUSE_REGION *region)
{
	// If no list present, there's nothin' to do.
	if( !MSYS_RegList )
		return;

	// Check if region in list
	if(!MSYS_RegionInList(region))
		return;

	// Remove a node from the list
	if(MSYS_RegList == region)
	{ // First node on list, adjust main pointer.
		MSYS_RegList = region->next;
		if(MSYS_RegList!=NULL)
			MSYS_RegList->prev = NULL;
		region->next = region->prev = NULL;
	}
	else
	{
		if(region->prev)
			region->prev->next = region->next;
		// If not last node in list, adjust following node's->prev entry.
		if( region->next )
			region->next->prev = region->prev;
		region->prev = region->next = NULL;
	}

	// Did we delete a grabbed region?
	if(MSYS_Mouse_Grabbed)
	{
		if(MSYS_GrabRegion == region)
		{
			MSYS_Mouse_Grabbed = FALSE;
			MSYS_GrabRegion = NULL;
		}
	}

	// Is only the system background region remaining?
	if(MSYS_RegList == &MSYS_SystemBaseRegion)
	{
		// Yup, so let's reset the ID values!
		MSYS_CurrentID = MSYS_ID_BASE;
		MSYS_ScanForID = FALSE;
	}
	else if(MSYS_RegList == NULL)
	{
		// Ack, we actually emptied the list, so let's reset for re-init possibilities
		MSYS_CurrentID = MSYS_ID_SYSTEM;
		MSYS_ScanForID = FALSE;
	}
}



//======================================================================================================
//	MSYS_UpdateMouseRegion
//
//	Searches the list for the highest priority region and updates it's info. It also dispatches
//	the callback functions
//
void MSYS_UpdateMouseRegion(void)
{
	INT32 found;
	UINT32 ButtonReason;
	MOUSE_REGION *pTempRegion;
	BOOLEAN fFound=FALSE;
	found=FALSE;

	// WANNE: Error handling Bugfix by birdflu (to avoid Stack Overflow)
	if(guiCurrentScreen == ERROR_SCREEN)
	{
		return;
	}

	// Check previous region!
	if(MSYS_Mouse_Grabbed)
	{
		MSYS_CurrRegion = MSYS_GrabRegion;
		found = TRUE;
	}
	if(!found)
		MSYS_CurrRegion = MSYS_RegList;

	while( !found && MSYS_CurrRegion )
	{
		if( MSYS_CurrRegion->uiFlags & (MSYS_REGION_ENABLED | MSYS_ALLOW_DISABLED_FASTHELP) &&
		(MSYS_CurrRegion->RegionTopLeftX <= MSYS_CurrentMX) &&		// Check boundaries
		(MSYS_CurrRegion->RegionTopLeftY <= MSYS_CurrentMY) &&
		(MSYS_CurrRegion->RegionBottomRightX >= MSYS_CurrentMX) &&
		(MSYS_CurrRegion->RegionBottomRightY >= MSYS_CurrentMY))
		{
			// We got the right region. We don't need to check for priorities 'cause
			// the whole list is sorted the right way!
			found=TRUE;
		}
		else
			MSYS_CurrRegion = MSYS_CurrRegion->next;
	}

	if( MSYS_PrevRegion )
	{
		MSYS_PrevRegion->uiFlags &= (~MSYS_MOUSE_IN_AREA);

		if ( MSYS_PrevRegion != MSYS_CurrRegion )
		{
			//Remove the help text for the previous region if one is currently being displayed.
			if( MSYS_PrevRegion->FastHelpText )
			{
				//ExecuteMouseHelpEndCallBack( MSYS_PrevRegion );

				#ifdef _JA2_RENDER_DIRTY
					if( MSYS_PrevRegion->uiFlags & MSYS_GOT_BACKGROUND )
						FreeBackgroundRectPending( MSYS_PrevRegion->FastHelpRect );
				#endif
				MSYS_PrevRegion->uiFlags &= (~MSYS_GOT_BACKGROUND);
				MSYS_PrevRegion->uiFlags &= (~MSYS_FASTHELP_RESET);

				#ifndef UTIL 
					// dirty buttons, need a re-render
//DEF: Nov 30 98
//				PausedMarkButtonsDirty( );
				#endif

				//if( region->uiFlags & MSYS_REGION_ENABLED )
				//	region->uiFlags |= BUTTON_DIRTY;
#ifndef JA2			
							VideoRemoveToolTip();
#endif
			}

			if (MSYS_CurrRegion)
			MSYS_CurrRegion->FastHelpTimer = gsFastHelpDelay;

			// Force a callbacks to happen on previous region to indicate that
			// the mouse has left the old region
			if ( MSYS_PrevRegion->uiFlags & MSYS_MOVE_CALLBACK && MSYS_PrevRegion->uiFlags & MSYS_REGION_ENABLED )
				(*(MSYS_PrevRegion->MovementCallback))(MSYS_PrevRegion,MSYS_CALLBACK_REASON_LOST_MOUSE);
		}
	}

	// If a region was found in the list, update it's data
	if(found)
	{
		if(MSYS_CurrRegion != MSYS_PrevRegion)
		{
			//Kris -- October 27, 1997
			//Implemented gain mouse region
			if ( MSYS_CurrRegion->uiFlags & MSYS_MOVE_CALLBACK )
			{
				if( MSYS_CurrRegion->FastHelpText && !( MSYS_CurrRegion->uiFlags & MSYS_FASTHELP_RESET ) )
				{
				//ExecuteMouseHelpEndCallBack( MSYS_CurrRegion );
					MSYS_CurrRegion->FastHelpTimer = gsFastHelpDelay;
					#ifdef _JA2_RENDER_DIRTY
						if( MSYS_CurrRegion->uiFlags & MSYS_GOT_BACKGROUND )
							FreeBackgroundRectPending( MSYS_CurrRegion->FastHelpRect );
					#endif
					MSYS_CurrRegion->uiFlags &= (~MSYS_GOT_BACKGROUND);
					MSYS_CurrRegion->uiFlags |= MSYS_FASTHELP_RESET;

#ifndef JA2			
							VideoRemoveToolTip();
#endif

					//if( b->uiFlags & BUTTON_ENABLED )
					//	b->uiFlags |= BUTTON_DIRTY;
				}
				if( MSYS_CurrRegion->uiFlags & MSYS_REGION_ENABLED )
				{
					(*(MSYS_CurrRegion->MovementCallback))(MSYS_CurrRegion,MSYS_CALLBACK_REASON_GAIN_MOUSE);
				}
			}

			// if the cursor is set and is not set to no cursor
		if( MSYS_CurrRegion->uiFlags & MSYS_REGION_ENABLED && 
					MSYS_CurrRegion->uiFlags & MSYS_SET_CURSOR && 
					MSYS_CurrRegion->Cursor != MSYS_NO_CURSOR )
			{
				MSYS_SetCurrentCursor(MSYS_CurrRegion->Cursor);
			}
			else
			{
		// Addition Oct 10/1997 Carter, patch for mouse cursor
			// start at region and find another region encompassing
			pTempRegion=MSYS_CurrRegion->next;
			while((pTempRegion!=NULL)&&(!fFound))
			{
		if((pTempRegion->uiFlags & MSYS_REGION_ENABLED) &&
			(pTempRegion->RegionTopLeftX <= MSYS_CurrentMX) &&
			(pTempRegion->RegionTopLeftY <= MSYS_CurrentMY) &&
			(pTempRegion->RegionBottomRightX >= MSYS_CurrentMX) &&
			(pTempRegion->RegionBottomRightY >= MSYS_CurrentMY)&&(pTempRegion->uiFlags & MSYS_SET_CURSOR))
				{
		fFound=TRUE;
				if( pTempRegion->Cursor != MSYS_NO_CURSOR )
				{
					MSYS_SetCurrentCursor(pTempRegion->Cursor);
				}
				}
		pTempRegion=pTempRegion->next;
			}
		}
		}

		// OK, if we do not have a button down, any button is game!
		if ( !gfClickedModeOn || ( gfClickedModeOn && gusClickedIDNumber == MSYS_CurrRegion->IDNumber ) )
		{
			MSYS_CurrRegion->uiFlags |= MSYS_MOUSE_IN_AREA;

			MSYS_CurrRegion->MouseXPos = MSYS_CurrentMX;
			MSYS_CurrRegion->MouseYPos = MSYS_CurrentMY;
			MSYS_CurrRegion->RelativeXPos = MSYS_CurrentMX - MSYS_CurrRegion->RegionTopLeftX;
			MSYS_CurrRegion->RelativeYPos = MSYS_CurrentMY - MSYS_CurrRegion->RegionTopLeftY;

			MSYS_CurrRegion->ButtonState = MSYS_CurrentButtons;
			MSYS_CurrRegion->WheelState  = MSYS_Wheel;
			MSYS_Wheel = 0;

			if( MSYS_CurrRegion->uiFlags & MSYS_REGION_ENABLED &&
					MSYS_CurrRegion->uiFlags & MSYS_MOVE_CALLBACK && MSYS_Action & MSYS_DO_MOVE )
			{
				(*(MSYS_CurrRegion->MovementCallback))(MSYS_CurrRegion,MSYS_CALLBACK_REASON_MOVE);
			}

			//ExecuteMouseHelpEndCallBack( MSYS_CurrRegion );
			//MSYS_CurrRegion->FastHelpTimer = gsFastHelpDelay;

			MSYS_Action &= (~MSYS_DO_MOVE);

			if((MSYS_CurrRegion->uiFlags & MSYS_BUTTON_CALLBACK) && (MSYS_Action & MSYS_DO_BUTTONS))
			{
				if( MSYS_CurrRegion->uiFlags & MSYS_REGION_ENABLED )
				{
					ButtonReason=MSYS_CALLBACK_REASON_NONE;
					if(MSYS_Action & MSYS_DO_LBUTTON_DWN)
					{
						ButtonReason|=MSYS_CALLBACK_REASON_LBUTTON_DWN;
						gfClickedModeOn = TRUE;
						// Set global ID
						gusClickedIDNumber = MSYS_CurrRegion->IDNumber;
					}

					if(MSYS_Action & MSYS_DO_LBUTTON_UP )
					{
						ButtonReason|=MSYS_CALLBACK_REASON_LBUTTON_UP;
						gfClickedModeOn = FALSE;
					}

					if(MSYS_Action & MSYS_DO_RBUTTON_DWN)
					{
						ButtonReason|=MSYS_CALLBACK_REASON_RBUTTON_DWN;
						gfClickedModeOn = TRUE;
						// Set global ID
						gusClickedIDNumber = MSYS_CurrRegion->IDNumber;
					}

					if(MSYS_Action & MSYS_DO_RBUTTON_UP	)
					{
						ButtonReason|=MSYS_CALLBACK_REASON_RBUTTON_UP;
						gfClickedModeOn = FALSE;
					}

					// ATE: Added repeat resons....
					if(MSYS_Action & MSYS_DO_LBUTTON_REPEAT	)
					{
						ButtonReason|=MSYS_CALLBACK_REASON_LBUTTON_REPEAT;
					}

					if(MSYS_Action & MSYS_DO_RBUTTON_REPEAT	)
					{
						ButtonReason|=MSYS_CALLBACK_REASON_RBUTTON_REPEAT;
					}

					if( ButtonReason != MSYS_CALLBACK_REASON_NONE )
					{
						if( MSYS_CurrRegion->uiFlags & MSYS_FASTHELP )
						{
							// Button was clicked so remove any FastHelp text
							MSYS_CurrRegion->uiFlags &= (~MSYS_FASTHELP);
							#ifdef _JA2_RENDER_DIRTY
								if( MSYS_CurrRegion->uiFlags & MSYS_GOT_BACKGROUND )
									FreeBackgroundRectPending( MSYS_CurrRegion->FastHelpRect );
							#endif
							MSYS_CurrRegion->uiFlags &= (~MSYS_GOT_BACKGROUND);

							//ExecuteMouseHelpEndCallBack( MSYS_CurrRegion );
							MSYS_CurrRegion->FastHelpTimer = gsFastHelpDelay;
							MSYS_CurrRegion->uiFlags &= (~MSYS_FASTHELP_RESET);

							//if( b->uiFlags & BUTTON_ENABLED )
							//	b->uiFlags |= BUTTON_DIRTY;
#ifndef JA2
							VideoRemoveToolTip();
#endif
						}

						//Kris: Nov 31, 1999 -- Added support for double click events.
						//This is where double clicks are checked and passed down.
						if( ButtonReason == MSYS_CALLBACK_REASON_LBUTTON_DWN )
						{
							UINT32 uiCurrTime = GetClock();
							if( gpRegionLastLButtonDown == MSYS_CurrRegion &&
									gpRegionLastLButtonUp == MSYS_CurrRegion &&
									uiCurrTime <= guiRegionLastLButtonDownTime + MSYS_DOUBLECLICK_DELAY )
							{ //Sequential left click on same button within the maximum time allowed for a double click
								//Double click check succeeded, set flag and reset double click globals.
								ButtonReason |= MSYS_CALLBACK_REASON_LBUTTON_DOUBLECLICK;
								gpRegionLastLButtonDown = NULL;
								gpRegionLastLButtonUp = NULL;
								guiRegionLastLButtonDownTime = 0;
							}
							else 
							{ //First click, record time and region pointer (to check if 2nd click detected later)
								gpRegionLastLButtonDown = MSYS_CurrRegion;
								guiRegionLastLButtonDownTime = GetClock();
							}
						}
						else if( ButtonReason == MSYS_CALLBACK_REASON_LBUTTON_UP )
						{
							UINT32 uiCurrTime = GetClock();
							if( gpRegionLastLButtonDown == MSYS_CurrRegion &&
									uiCurrTime <= guiRegionLastLButtonDownTime + MSYS_DOUBLECLICK_DELAY )
							{ //Double click is Left down, then left up, then left down.	We have just detected the left up here (step 2).
								gpRegionLastLButtonUp = MSYS_CurrRegion;
							}
							else 
							{ //User released mouse outside of current button, so kill any chance of a double click happening.
								gpRegionLastLButtonDown = NULL;
								gpRegionLastLButtonUp = NULL;
								guiRegionLastLButtonDownTime = 0;
							}
						}

						(*(MSYS_CurrRegion->ButtonCallback))(MSYS_CurrRegion,ButtonReason);
					}
				}
			}

			MSYS_Action &= (~MSYS_DO_BUTTONS);
		}
		else if( MSYS_CurrRegion->uiFlags & MSYS_REGION_ENABLED )
		{
			// OK here, if we have release a button, UNSET LOCK wherever you are....
			// Just don't give this button the message....
			if(MSYS_Action & MSYS_DO_RBUTTON_UP	)
			{
				gfClickedModeOn = FALSE;
			}
			if(MSYS_Action & MSYS_DO_LBUTTON_UP	)
			{
				gfClickedModeOn = FALSE;
			}

			// OK, you still want move messages however....
			MSYS_CurrRegion->uiFlags |= MSYS_MOUSE_IN_AREA;
			MSYS_CurrRegion->MouseXPos = MSYS_CurrentMX;
			MSYS_CurrRegion->MouseYPos = MSYS_CurrentMY;
			MSYS_CurrRegion->RelativeXPos = MSYS_CurrentMX - MSYS_CurrRegion->RegionTopLeftX;
			MSYS_CurrRegion->RelativeYPos = MSYS_CurrentMY - MSYS_CurrRegion->RegionTopLeftY;

			if((MSYS_CurrRegion->uiFlags & MSYS_MOVE_CALLBACK) && (MSYS_Action & MSYS_DO_MOVE))
			{
				(*(MSYS_CurrRegion->MovementCallback))(MSYS_CurrRegion,MSYS_CALLBACK_REASON_MOVE);
			}

			MSYS_Action &= (~MSYS_DO_MOVE);
		}
		MSYS_PrevRegion = MSYS_CurrRegion;
	}
	else
		MSYS_PrevRegion = NULL;
}



//=================================================================================================
//	MSYS_DefineRegion
//
//	Inits a MOUSE_REGION structure for use with the mouse system
//
void MSYS_DefineRegion(MOUSE_REGION *region,UINT16 tlx,UINT16 tly,UINT16 brx,UINT16 bry,INT8 priority,
					UINT16 crsr,MOUSE_CALLBACK movecallback,MOUSE_CALLBACK buttoncallback)
{
	#ifdef MOUSESYSTEM_DEBUGGING
		if( region->uiFlags & MSYS_REGION_EXISTS )
			AssertMsg( 0, "Attempting to define a region that already exists." );
	#endif

	region->IDNumber = MSYS_ID_BASE;

	if(priority == MSYS_PRIORITY_AUTO)
		priority = MSYS_PRIORITY_BASE;
	else if(priority <= MSYS_PRIORITY_LOWEST)
		priority = MSYS_PRIORITY_LOWEST;
	else if(priority >= MSYS_PRIORITY_HIGHEST)
		priority = MSYS_PRIORITY_HIGHEST;

	region->PriorityLevel = priority;

	region->uiFlags = MSYS_NO_FLAGS;

	region->MovementCallback = movecallback;
	if(movecallback != MSYS_NO_CALLBACK)
		region->uiFlags |= MSYS_MOVE_CALLBACK;

	region->ButtonCallback = buttoncallback;
	if(buttoncallback != MSYS_NO_CALLBACK)
		region->uiFlags |= MSYS_BUTTON_CALLBACK;

	region->Cursor = crsr;
	if(crsr != MSYS_NO_CURSOR)
		region->uiFlags |= MSYS_SET_CURSOR;

	region->RegionTopLeftX = tlx;
	region->RegionTopLeftY = tly;
	region->RegionBottomRightX = brx;
	region->RegionBottomRightY = bry;

	region->MouseXPos = 0;
	region->MouseYPos = 0;
	region->RelativeXPos = 0;
	region->RelativeYPos = 0;
	region->ButtonState	= 0;

	//Init fasthelp
	region->FastHelpText = NULL;
	region->FastHelpTimer = 0;

	region->next = NULL;
	region->prev = NULL;
	region->HelpDoneCallback = NULL;

	//Add region to system list
	MSYS_AddRegionToList(region);
	region->uiFlags|= MSYS_REGION_ENABLED | MSYS_REGION_EXISTS;


	// Dirty our update flag
	gfRefreshUpdate = TRUE;
}



//=================================================================================================
//	MSYS_ChangeRegionCursor
//
void MSYS_ChangeRegionCursor(MOUSE_REGION *region,UINT16 crsr)
{
	region->uiFlags &= (~MSYS_SET_CURSOR);
	region->Cursor = crsr;
	if(crsr != MSYS_NO_CURSOR)
	{
		region->uiFlags |= MSYS_SET_CURSOR;

		// If we are not in the region, donot update!
		if ( !( region->uiFlags & MSYS_MOUSE_IN_AREA ) )
		{
			return;
		}

		// Update cursor
		MSYS_SetCurrentCursor( crsr );
	}
}



//=================================================================================================
//	MSYS_AddRegion
//
//	Adds a defined mouse region to the system list. Once inserted, it enables the region then
//	calls the callback functions, if any, for initialization.
//
INT32 MSYS_AddRegion(MOUSE_REGION *region)
{
	return(1);
}



//=================================================================================================
//	MSYS_RemoveRegion
//
//	Removes a region from the list, disables it, then calls the callback functions for
//	de-initialization.
//
void MSYS_RemoveRegion(MOUSE_REGION *region)
{
	if( !region )
	{
		#ifdef MOUSESYSTEM_DEBUGGING
		if( gfIgnoreShutdownAssertions )
		#endif
			return;
		AssertMsg( 0, "Attempting to remove a NULL region.");
	}
	//CHRISL: Obviously, we should never run this function if a region hasn't been setup, but a failsafe would be nice
	if(!(region->uiFlags & MSYS_REGION_EXISTS))
		return;

	#ifdef MOUSESYSTEM_DEBUGGING
	if( !(region->uiFlags & MSYS_REGION_EXISTS) )
		AssertMsg( 0, "Attempting to remove an already removed region." );
	#endif

#ifdef _JA2_RENDER_DIRTY
	if( region->uiFlags & MSYS_HAS_BACKRECT )
	{
		FreeBackgroundRectPending( region->FastHelpRect );
		region->uiFlags &= (~MSYS_HAS_BACKRECT);


	}
#endif

	// Get rid of the FastHelp text (if applicable)
	if( region->FastHelpText )
	{
#ifndef JA2
		if(region->uiFlags & MSYS_FASTHELP)
			VideoRemoveToolTip();
#endif
		MemFree( region->FastHelpText );
	}
	region->FastHelpText = NULL;

	MSYS_DeleteRegionFromList(region);

	//if the previous region is the one that we are deleting, reset the previous region
	if ( MSYS_PrevRegion == region )
		MSYS_PrevRegion = NULL;
	//if the current region is the one that we are deleting, then clear it.
	if( MSYS_CurrRegion == region )
		MSYS_CurrRegion = NULL;

	//dirty our update flag
	gfRefreshUpdate = TRUE;

	// Check if this is a locked region, and unlock if so
	if ( gfClickedModeOn )
	{
		// Set global ID
		if ( gusClickedIDNumber == region->IDNumber )
		{
			gfClickedModeOn = FALSE;
		}
	}

	//clear all internal values (including the region exists flag)
	memset( region, 0, sizeof( MOUSE_REGION ) );
}



//=================================================================================================
//	MSYS_EnableRegion
//
//	Enables a mouse region.
//
void MSYS_EnableRegion(MOUSE_REGION *region)
{
	Assert( region );

	region->uiFlags |= MSYS_REGION_ENABLED;
}



//=================================================================================================
//	MSYS_DisableRegion
//
//	Disables a mouse region without removing it from the system list.
//
void MSYS_DisableRegion(MOUSE_REGION *region)
{
	region->uiFlags &= (~MSYS_REGION_ENABLED);
}


//=================================================================================================
//	MSYS_SetCurrentCursor
//
//	Sets the mouse cursor to the regions defined value.
//
void MSYS_SetCurrentCursor(UINT16 Cursor)
{
	SGP_TRYCATCH_RETHROW(SetCurrentCursorFromDatabase( Cursor ), "Could not set Cursor");
}



//=================================================================================================
//	MSYS_ChangeRegionPriority
//
//	Set the priority of a mouse region
//
void MSYS_ChangeRegionPriority(MOUSE_REGION *region,INT8 priority)
{
	if(priority==MSYS_PRIORITY_AUTO)
		priority=MSYS_PRIORITY_NORMAL;

	region->PriorityLevel = priority;
}



//=================================================================================================
//	MSYS_SetRegionUserData
//
//	Sets one of the four user data entries in a mouse region
//
void MSYS_SetRegionUserData(MOUSE_REGION *region,INT32 index,INT32 userdata)
{
	if(index < 0 || index > 3)
	{
		CHAR8 str[80];
		#ifdef MOUSESYSTEM_DEBUGGING
		if( gfIgnoreShutdownAssertions )
		#endif
			return;
		sprintf( str, "Attempting MSYS_SetRegionUserData() with out of range index %d.", index );
		AssertMsg( 0, str );
	}
	region->UserData[index]=userdata;
}



//=================================================================================================
//	MSYS_GetRegionUserData
//
//	Retrieves one of the four user data entries in a mouse region
//
INT32 MSYS_GetRegionUserData(MOUSE_REGION *region,INT32 index)
{
	if(index < 0 || index > 3)
	{
		CHAR8 str[80];
		#ifdef MOUSESYSTEM_DEBUGGING
		if( gfIgnoreShutdownAssertions )
		#endif
			return 0;
		sprintf( str, "Attempting MSYS_GetRegionUserData() with out of range index %d", index );
		AssertMsg( 0, str );
	}
	return(region->UserData[index]);
}



//=================================================================================================
//	MSYS_GrabMouse
//
//	Assigns all mouse activity to a region, effectively blocking any other region from having
//	control.
//
INT32 MSYS_GrabMouse(MOUSE_REGION *region)
{
	if(!MSYS_RegionInList(region))
		return(MSYS_REGION_NOT_IN_LIST);

	if(MSYS_Mouse_Grabbed == TRUE)
		return(MSYS_ALREADY_GRABBED);

	MSYS_Mouse_Grabbed = TRUE;
	MSYS_GrabRegion = region;
	return(MSYS_GRABBED_OK);
}



//=================================================================================================
//	MSYS_ReleaseMouse
//
//	Releases a previously grabbed mouse region
//
void MSYS_ReleaseMouse(MOUSE_REGION *region)
{
	if(MSYS_GrabRegion != region)
		return;

	if(MSYS_Mouse_Grabbed == TRUE)
	{
		MSYS_Mouse_Grabbed = FALSE;
		MSYS_GrabRegion = NULL;
		MSYS_UpdateMouseRegion();
	}
}


/* ==================================================================================
	MSYS_MoveMouseRegionTo( MOUSE_REGION *region, INT16 sX, INT16 sY)

	Moves a Mouse region to X Y on the screen

*/

void MSYS_MoveMouseRegionTo( MOUSE_REGION *region, INT16 sX, INT16 sY)
{

	INT16 sWidth;
	INT16 sHeight;


	sWidth =	region->RegionBottomRightX - region->RegionTopLeftX;
	sHeight = region->RegionBottomRightY - region->RegionTopLeftY;


	// move top left
	region->RegionTopLeftX = sX;
	region->RegionTopLeftY = sY;

	// now move bottom right based on topleft + width or height
	region->RegionBottomRightX = sX + sWidth;
	region->RegionBottomRightY = sY + sHeight;

	return;
}

/* ==================================================================================
	MSYS_MoveMouseRegionBy( MOUSE_REGION *region, INT16 sDeltaX, INT16 sDeltaY)

	Moves a Mouse region by sDeltaX sDeltaY on the screen

*/

void MSYS_MoveMouseRegionBy( MOUSE_REGION *region, INT16 sDeltaX, INT16 sDeltaY)
{

	// move top left
	region->RegionTopLeftX = region->RegionTopLeftX + sDeltaX;
	region->RegionTopLeftY = region->RegionTopLeftY + sDeltaY;

	// now move bottom right
	region->RegionBottomRightX = region->RegionBottomRightX + sDeltaX;
	region->RegionBottomRightY = region->RegionBottomRightY + sDeltaY;

	return;
}


// This function will force a re-evaluation of mouse regions
// Usually used to force change of mouse cursor if panels switch, etc
void RefreshMouseRegions( )
{
	MSYS_Action|=MSYS_DO_MOVE;

	MSYS_UpdateMouseRegion( );

}

void SetRegionFastHelpText( MOUSE_REGION *region, const STR16 szText )
{
	Assert( region );

	if( region->FastHelpText )
		MemFree( region->FastHelpText );

	region->FastHelpText = NULL;
//	region->FastHelpTimer = 0;
	if( !(region->uiFlags & MSYS_REGION_EXISTS) )
	{
		return;
		//AssertMsg( 0, String( "Attempting to set fast help text, \"%S\" to an inactive region.", szText ) );
	}

	if( !szText || !wcslen( szText ) )
		return; //blank (or clear)

	// Allocate memory for the button's FastHelp text string...
	region->FastHelpText = (STR16) MemAlloc( (wcslen( szText ) + 1) * sizeof(CHAR16) );
	Assert( region->FastHelpText );

	wcscpy( region->FastHelpText, szText );

	// ATE: We could be replacing already existing, active text
	// so let's remove the region so it be rebuilt...

	#ifdef JA2
	if ( guiCurrentScreen != MAP_SCREEN )
	{
	#endif

	#ifdef _JA2_RENDER_DIRTY
	if( region->uiFlags & MSYS_GOT_BACKGROUND )
		FreeBackgroundRectPending( region->FastHelpRect );
	#endif

	region->uiFlags &= (~MSYS_GOT_BACKGROUND);
	region->uiFlags &= (~MSYS_FASTHELP_RESET);

	#ifdef JA2
	}
	#endif

	//region->FastHelpTimer = gsFastHelpDelay;
}

INT16 GetNumberOfLinesInHeight( const STR16 pStringA )
{
	STR16 pToken;
	INT16 sCounter = 0;
	CHAR16 pString[ 4096 ];

	wcscpy( pString, pStringA );

	// tokenize
	pToken = wcstok( pString, L"\n" );

	while( pToken != NULL )
	{
		// WANNE: Fix by Headrock
		if ( (sCounter+1) * (GetFontHeight(FONT10ARIAL)+1) > (SCREEN_HEIGHT - 10) )
        {
            break;
        }
        pToken = wcstok( NULL, L"\n" );
        sCounter++;

		/*pToken = wcstok( NULL, L"\n" );
		sCounter++;*/
	}

	return( sCounter );
}



#ifdef _JA2_RENDER_DIRTY
//=============================================================================
//	DisplayFastHelp
//
//
void DisplayFastHelp( MOUSE_REGION *region )
{
	UINT16 usFillColor;
	INT32 iX,iY,iW,iH;

	if ( region->uiFlags & MSYS_FASTHELP )
	{
		usFillColor = Get16BPPColor(FROMRGB(250, 240, 188));

		iW = (INT32)GetWidthOfString( region->FastHelpText ) + 10;
		iH = (INT32)( GetNumberOfLinesInHeight( region->FastHelpText ) * (GetFontHeight(FONT10ARIAL)+1) + 8 );

		iX = (INT32)region->RegionTopLeftX + 10;

		if (iX < 0)
			iX = 0;

		if ( (iX + iW) >= SCREEN_WIDTH )
			iX = (SCREEN_WIDTH - iW - 4);

		// HEADROCK HAM 3.6: This formula causes the tooltip to appear UNDERNEATH the cursor if
		// the tooltip's parent region is small. That makes reading the tooltip very difficult. Not desirable.
		//iY = (INT32)region->RegionTopLeftY - (iH * 3 / 4);
		iY = (INT32)region->RegionTopLeftY - iH;
		if (iY < 0)
			iY = 0;

		if ( (iY + iH) >= SCREEN_HEIGHT )
			iY = (SCREEN_HEIGHT - iH - 15);

		if ( !(region->uiFlags & MSYS_GOT_BACKGROUND) )
		{
			region->FastHelpRect = RegisterBackgroundRect(BGND_FLAG_PERMANENT | BGND_FLAG_SAVERECT, NULL, (INT16)iX, (INT16)iY,
						(INT16)(iX + iW), (INT16)(iY + iH) );
			region->uiFlags |= MSYS_GOT_BACKGROUND;
			region->uiFlags |= MSYS_HAS_BACKRECT;
		}
		else
		{
			UINT8 *pDestBuf;
			UINT32 uiDestPitchBYTES;
			pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES );
			SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
			RectangleDraw( TRUE, iX + 1, iY + 1, iX + iW - 1, iY + iH - 1, Get16BPPColor( FROMRGB( 65, 57, 15 ) ), pDestBuf );
			RectangleDraw( TRUE, iX, iY, iX + iW - 2, iY + iH - 2, Get16BPPColor( FROMRGB( 227, 198, 88 ) ), pDestBuf );
			UnLockVideoSurface( FRAME_BUFFER );
			ShadowVideoSurfaceRect( FRAME_BUFFER, iX + 2, iY + 2, iX + iW - 3, iY + iH - 3 );
			ShadowVideoSurfaceRect( FRAME_BUFFER, iX + 2, iY + 2, iX + iW - 3, iY + iH - 3 );

			SetFont( FONT10ARIAL );
			SetFontShadow( FONT_NEARBLACK );
			DisplayHelpTokenizedString( region->FastHelpText ,( INT16 )( iX + 5 ), ( INT16 )( iY + 5 ) );
			InvalidateRegion(	iX, iY, (iX + iW) , (iY + iH) );
		}
	}
}


INT16 GetWidthOfString( const STR16 pStringA )
{
	CHAR16 pString[ 4096 ];
	STR16 pToken;
	INT16 sWidth = 0;
	wcscpy( pString, pStringA );

	// tokenize
	pToken = wcstok( pString, L"\n" );

	while( pToken != NULL )
	{
		if( sWidth < StringPixLength( pToken, FONT10ARIAL ) )
		{
			sWidth = StringPixLength( pToken, FONT10ARIAL );
		}

		pToken = wcstok( NULL, L"\n" );
	}

	return( sWidth );

}

void DisplayHelpTokenizedString( const STR16 pStringA, INT16 sX, INT16 sY )
{
	STR16 pToken;
	INT32 iCounter = 0, i;
	UINT32 uiCursorXPos;
	CHAR16 pString[ 4096 ];
	INT32 iLength;

	wcscpy( pString, pStringA );

	// tokenize
	pToken = wcstok( pString, L"\n" );

	while( pToken != NULL )
	{
		// WANNE: Fix by Headrock
		if ( (iCounter+2) * (GetFontHeight(FONT10ARIAL)+1) > (SCREEN_HEIGHT - 10) )
        {
            mprintf( sX, sY + iCounter * (GetFontHeight(FONT10ARIAL)+1), L"..." );
            break;
        }
        iLength = (INT32)wcslen( pToken );

		//iLength = (INT32)wcslen( pToken );
		for( i = 0; i < iLength; i++ )
		{
			uiCursorXPos = StringPixLengthArgFastHelp( FONT10ARIAL, FONT10ARIALBOLD, i, pToken );
			if( pToken[ i ] == '|' )
			{
				i++;
				SetFont( FONT10ARIALBOLD );
				SetFontForeground( 146 );
			}
			else
			{
				SetFont( FONT10ARIAL );
				SetFontForeground( FONT_BEIGE );
			}
			mprintf( sX + uiCursorXPos, sY + iCounter * (GetFontHeight(FONT10ARIAL)+1), L"%c", pToken[ i ] );
		}
		pToken = wcstok( NULL, L"\n" );
		iCounter++;
	}
	SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );
}

void RenderFastHelp()
{
	static INT32 iLastClock;
	INT32 iTimeDifferential, iCurrentClock;

	if( !gfRenderHilights )
		return;

	iCurrentClock = GetClock();
	iTimeDifferential = iCurrentClock - iLastClock;
	if (iTimeDifferential < 0)
		iTimeDifferential += 0x7fffffff;
	iLastClock = iCurrentClock;

	if( MSYS_CurrRegion && MSYS_CurrRegion->FastHelpText )
	{
		if( !MSYS_CurrRegion->FastHelpTimer )
		{
			if( MSYS_CurrRegion->uiFlags & ( MSYS_ALLOW_DISABLED_FASTHELP | MSYS_REGION_ENABLED ) )
			{
				if( MSYS_CurrRegion->uiFlags & MSYS_MOUSE_IN_AREA )
					MSYS_CurrRegion->uiFlags |= MSYS_FASTHELP;
				else
				{
					MSYS_CurrRegion->uiFlags &= ( ~( MSYS_FASTHELP | MSYS_FASTHELP_RESET ) );
				}
				//Do I really need this?
				//MSYS_CurrRegion->uiFlags |= REGION_DIRTY;
				DisplayFastHelp( MSYS_CurrRegion );
			}
		}
		else
		{
			if( MSYS_CurrRegion->uiFlags & ( MSYS_ALLOW_DISABLED_FASTHELP | MSYS_REGION_ENABLED ) )
			{
				if ( MSYS_CurrRegion->uiFlags & MSYS_MOUSE_IN_AREA &&
						!MSYS_CurrRegion->ButtonState)// & (MSYS_LEFT_BUTTON|MSYS_RIGHT_BUTTON)) )
				{
					MSYS_CurrRegion->FastHelpTimer -= (INT16)max( iTimeDifferential, 0 );

					if( MSYS_CurrRegion->FastHelpTimer < 0 )
					{
						MSYS_CurrRegion->FastHelpTimer = 0;
					}
				}
			}
		}
	}
}
#else

// **********Wiz8 Versions**************************************************************************

INT16 GetWidthOfString( const STR16 pStringA )
{
	CHAR16 pString[ 512 ];
	STR16 pToken;
	INT16 sWidth = 0;
	wcscpy( pString, pStringA );

	// tokenize
	pToken = wcstok( pString, L"\n" );

	while( pToken != NULL )
	{
		if( sWidth < StringPixLength( pToken, ghTinyMonoFont ) )
		{
			sWidth = StringPixLength( pToken, ghTinyMonoFont );
		}

		pToken = wcstok( NULL, L"\n" );
	}

	return( sWidth );

}

void DisplayFastHelp( MOUSE_REGION *region )
{
	INT32 iX,iY,iW,iH;

	if ( region->uiFlags & MSYS_FASTHELP )
	{
		VideoToolTip(region->FastHelpText);

		iW = VideoGetToolTipWidth();
		iH = VideoGetToolTipHeight();

		iX = (INT32)region->RegionTopLeftX + 10;

		if (iX < 0)
			iX = 0;

		if ( (iX + iW) >= SCREEN_WIDTH )
			iX = (SCREEN_WIDTH - iW - 4);

		iY = (INT32)region->RegionTopLeftY - (iH * 3 / 4);
		if (iY < 0)
			iY = 0;

		if ( (iY + iH) >= SCREEN_HEIGHT )
			iY = (SCREEN_HEIGHT - iH - 15);

		VideoPositionToolTip(iX, iY);
	}
}

void DisplayHelpTokenizedString( const STR16 pStringA, INT16 sX, INT16 sY )
{
	STR16 pToken;
	INT32 iCounter = 0, i;
	UINT32 uiCursorXPos;
	CHAR16 pString[ 512 ];
	INT32 iLength;

	wcscpy( pString, pStringA );

	// tokenize
	pToken = wcstok( pString, L"\n" );

	while( pToken != NULL )
	{
		iLength = (INT32)wcslen( pToken );
		for( i = 0; i < iLength; i++ )
		{
			uiCursorXPos = StringPixLengthArgFastHelp( ghTinyMonoFont, ghTinyMonoFont, i, pToken );
			if( pToken[ i ] == '|' )
			{
				i++;
				SetFont(ghTinyMonoFont);
				SetFontForeground( 2 );
			}
			else
			{
				SetFont( ghTinyMonoFont );
				SetFontForeground( 2 );
			}
			mprintf( sX + uiCursorXPos, sY + iCounter * (GetFontHeight(ghTinyMonoFont)+1), L"%c", pToken[ i ] );
		}
		pToken = wcstok( NULL, L"\n" );
		iCounter++;
	}
}

void RenderFastHelp()
{
	static INT32 iLastClock;
	INT32 iTimeDifferential, iCurrentClock;

	if( !gfRenderHilights )
		return;

	iCurrentClock = GetClock();
	iTimeDifferential = iCurrentClock - iLastClock;
	if (iTimeDifferential < 0)
		iTimeDifferential += 0x7fffffff;
	iLastClock = iCurrentClock;

	if( MSYS_CurrRegion && MSYS_CurrRegion->FastHelpText && gfShowFastHelp )
	{
		if( !MSYS_CurrRegion->FastHelpTimer )
		{
			if( MSYS_CurrRegion->uiFlags & ( MSYS_ALLOW_DISABLED_FASTHELP | MSYS_REGION_ENABLED ) )
			{
				if( MSYS_CurrRegion->uiFlags & MSYS_MOUSE_IN_AREA )
				{
					MSYS_CurrRegion->uiFlags |= MSYS_FASTHELP;
					DisplayFastHelp( MSYS_CurrRegion );
				}
				else
				{
					MSYS_CurrRegion->uiFlags &= ( ~( MSYS_FASTHELP | MSYS_FASTHELP_RESET ) );
					VideoRemoveToolTip();
				}
			}
		}
		else
		{
			if( MSYS_CurrRegion->uiFlags & ( MSYS_ALLOW_DISABLED_FASTHELP | MSYS_REGION_ENABLED ) )
			{
				if ( MSYS_CurrRegion->uiFlags & MSYS_MOUSE_IN_AREA &&
						!MSYS_CurrRegion->ButtonState)// & (MSYS_LEFT_BUTTON|MSYS_RIGHT_BUTTON)) )
				{
					MSYS_CurrRegion->FastHelpTimer -= (INT16)max( iTimeDifferential, 0 );

					if( MSYS_CurrRegion->FastHelpTimer < 0 )
					{
						MSYS_CurrRegion->FastHelpTimer = 0;
					}
				}
			}
		}
	}
}
#endif

BOOLEAN	SetRegionSavedRect( MOUSE_REGION *region)
{
	return FALSE;
}

void FreeRegionSavedRect( MOUSE_REGION *region )
{

}

void MSYS_AllowDisabledRegionFastHelp( MOUSE_REGION *region, BOOLEAN fAllow )
{
	if( fAllow )
	{
		region->uiFlags |= MSYS_ALLOW_DISABLED_FASTHELP;
	}
	else
	{
		region->uiFlags &= ~MSYS_ALLOW_DISABLED_FASTHELP;
	}
}

// new stuff to allow mouse callbacks when help text finishes displaying

void SetRegionHelpEndCallback( MOUSE_REGION *region, MOUSE_HELPTEXT_DONE_CALLBACK CallbackFxn )
{
	// make sure region is non null
	if( region == NULL )
	{
		return;
	}

	// now set the region help text
	region->HelpDoneCallback = CallbackFxn;

	return;
}


void ExecuteMouseHelpEndCallBack( MOUSE_REGION *region )
{
	if( region == NULL )
	{
		return;
	}

	if( region->FastHelpTimer )
	{
		return;
	}
	// check if callback is non null
	if( region->HelpDoneCallback == NULL )
	{
		return;
	}

	// we have a callback, excecute
	// ATE: Disable these!
	//( *( region->HelpDoneCallback ) )( );

	return;
}


void SetFastHelpDelay( INT16 sFastHelpDelay )
{
	gsFastHelpDelay = sFastHelpDelay;
}

void EnableMouseFastHelp( void )
{
	gfShowFastHelp = TRUE;
}

void DisableMouseFastHelp( void )
{
	gfShowFastHelp = FALSE;
}

void ResetClickedMode(void)
{
	gfClickedModeOn = FALSE;
}

// arynn : render mouse regions : mouse region list iteration initial
MOUSE_REGION *get_first_entry_in_MSYS_RegList(void)
{
	return MSYS_RegList;
}

// arynn : render mouse regions : mouse region list iteration
MOUSE_REGION *get_next_entry_in_MSYS_RegList(MOUSE_REGION *current_region)
{
	return current_region->next;
}

void ResetWheelState( MOUSE_REGION *region )
{
	region->WheelState = 0;
}