#include "Script.hpp"
//===========================================================================//
// File:	 ScriptGlobals.cpp												 //
// Project:  GOSScript				                                         //
// Contents: Script global functions and variables							 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 02/27/98  AG		Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 2000, Microsoft, Inc.										//
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

// FONT RELATED
POSITION g_textXY = {0,0,0};
CUBE g_textMargins = {0,0,0,0,0,0};
HGOSSURFACE g_textDstSurf = NULL;
gosEnum_TextWrap g_textWrapMode = GOSWRAP_NONE;
HGOSFONT g_textFont = NULL;
HGOSFONT3D gScriptFont3d = NULL;
bool gIsDeclaration = false;
extern HGOSHEAP gScriptHeap[];
extern DWORD ScriptsDefaultFrameRate;
// SURFACE RELATED
LinkedList<HGOSSURFACE> AllSurfaces;

//
// List of all scripts currently loaded or being processed.
//
SCRIPT*			pListofScripts;		// All scripts loaded into memory
//
//
// Current line,line number,variables and pointer in current line.
//  These variables are used by all decoding and error message routines
//
VARIABLE*	pTempVariables;			// Pointer to temp variables defined in non GUI_CREATE messages
MESSAGES	CurrentMessage;			// Current message executing (variables only created in GUI_CREATE messages)
OBJECT*		pObject;				// Pointer to current object executing
SCRIPT*		pScript;				// Pointer to current running script
int			CurrentLineNumber;		// Line number
char*		pFileName;				// Pointer to current file name
char*		pLongFileName;			// Pointer to current FULL file name
char*		pLine;					// Pointer to current position on line
char*		pStartofLine;			// Pointer to the start of the current line		
long*		pLastVariable;			// Pointer to last variable.value found
DWORD		CurrentKeyword;			// Current keyword on line
DWORD*		pCurrentLineFlags;		// Pointer to current lines flags
BYTE*		pCurrentKeyword;

VARIABLE*	pLastVariableObject;	// Pointer to the last variable found
int pLastVariableObjectIndex;		// Pointer to the slot in the last variable found;
VAR_SCOPE	LastVariableScope;		// Where the last variable was found (in GetKeyword)
bool		bIgnoreMouseMessages = false;	// don't send any more mouse events to scripts this game loop 
OBJECT *	pInRegionTopmost = NULL;
int			ScriptMouseButton1 = 0;	// for use as variable within scripts
int			ScriptMouseButton2 = 0; // for use as variable within scripts
int			ScriptMouseButton3 = 0; // for use as variable within scripts
int			ScriptMouseButton4 = 0; // for use as variable within scripts
int			ScriptMouseButton5 = 0; // for use as variable within scripts
DWORD		gIgnoreFlags = 0;		// used to block messages;
int g_scriptPostedInteger[MAX_MAILSTACK][8];
OBJECT * g_scriptMailPoster[MAX_MAILSTACK];
DWORD g_mailStack = 0;

// global flags
bool gScript_boldOn = false;
bool gScript_italOn = false;
bool gScript_propOn = false;
bool gScript_wrapOn = false;
float gScript_sizeVal = 1.0;
DWORD gScript_colorVal = 0xff000000;
HGOSFONT3D gScript_lastFont = 0;
DWORD gFloatPrecision = 2;


#ifdef _ARMOR
//
// Debugging Scripts
//
char *ScriptMessageText[] =
{
	"GUI_INIT",
	"GUI_MAILBOX",
	"GUI_FOCUS",
	"GUI_UNFOCUS",
	"GUI_ACTIVATE",
	"GUI_DEACTIVATE",
	"REGION_ENTERED",
	"REGION_EXITED",
	"LBUTTON_UPDATE",
	"MBUTTON_UPDATE",
	"RBUTTON_UPDATE",
	"AUX1BUTTON_UPDATE",
	"AUX2BUTTON_UPDATE",
	"WHEEL_MOVED",
	"GUI_CREATE",
	"GUI_EXECUTE",
	"GUI_DESTROY",
	"GUI_DRAW",
	"GUI_CHAR",
	"GUI_PAUSE",
	"GUI_CONTINUE",
	"GUI_RESOLUTION"
};

#endif

//
// Script PreProcessing variables
//
PROCESS_SCRIPT*		pProcessScripts;		// Scripts being processed (only valid during processing)
IFSTATE				IFState;				// 0=Normal, 1=Do code till #else, 2=Do code till #endif, 3=Skip code till #else, 4=Skip code till #endif
int					IFNesting;
IFSTATE				IFStack[32];			// These are for #IF, #ELSE, #ENDIF
int					FirstIFLine;

//
// Decode variables
//
BYTE FirstKeywordChar[256];					// a-z _ NOT 0-9 AND @ AND $
BYTE KeywordChar[256];						// a-z _ AND 0-9
BYTE DefineChar[256];						// a-z _ AND 0-9 AND @ AND $


//
// Object array - used for sorting objects
//
REGVAR* pScriptRegisteredVariables = NULL;
OBJECT** pObjectArray = NULL;
int ObjectNumber;

//
// Script array - used for sorting scripts
//
SCRIPT** pScriptArray = NULL;
int ScriptNumber;

//
// Mouse position
//
POSITION*	ScriptMousePosition=NULL;
POSITION*	ScriptMousePositionPrev=NULL;
POSITION*	ScriptMousePositionLocal=NULL;
int			ScriptMouseButtons;
int			OldScriptMouseButtons;
int ScriptPointerDevice = -1;
// Keyboard focused object
OBJECT*		pFocusedObject = NULL;
SCRIPT*		pFocusedScript = NULL;
// Last keypress if object in focus;
int		scriptLastKey = 0;
bool gScript_InDraw = false;

// Current script-related localization DLL
DWORD gosScript_localizationFile = 0;

//
// Outputting information during processing (spews)
//
void ScriptOutput( char* Message )
{
	Message;
	SPEW(( GROUP_FROMSCRIPTS, Message));
}


//
// Make sure all global variables are reset.
//
void InitScriptGlobals()
{
	//
	// the pointer device is the mouse, by default.
	//
	ScriptPointerDevice = -1;
//
// Valid keyword characters are A-Z a-z _ 
//
	memset( FirstKeywordChar, 0, 256 );
	memset( KeywordChar, 0, 256 );
	memset( DefineChar, 0, 256 );

	for( char t1='a'; t1!=('z'+1); t1++ )
	{
		FirstKeywordChar[t1]=1;			// Lowercase letters
		KeywordChar[t1]=1;
		DefineChar[t1]=1;
	}

	for( t1='A'; t1!=('Z'+1); t1++ )
	{
		FirstKeywordChar[t1]=1;			// Uppercase letters
		KeywordChar[t1]=1;
		DefineChar[t1]=1;
	}

	FirstKeywordChar['_']=1;			// Underscore
	FirstKeywordChar['@']=1;			// for @@obj@@
	FirstKeywordChar['$']=1;			// for $$var$$

	DefineChar['_']=1;					// Underscore
	DefineChar['@']=1;					// for @@obj@@
	DefineChar['$']=1;					// for $$var$$

	KeywordChar['_']=1;

	for( t1='0'; t1!=('9'+1); t1++ )	// Numbers
	{
		KeywordChar[t1]=1;
		DefineChar[t1]=1;
	}

	pListofScripts=0;					// No Scripts loaded
//
// Init a memory pool for 32 objects running at once
//
	ObjectNumber=32;
	pObjectArray=(OBJECT**)gos_Malloc( ObjectNumber*sizeof(OBJECT*), gScriptHeap[0] );
//
//	Set the first object to NULL. Fixes restart with gamelogic paused
//
	pObjectArray[0] = NULL;
//
//	malloc space for the mouse position
//
	ScriptMousePosition = (POSITION *) gos_Malloc(sizeof(POSITION), gScriptHeap[0]);
	ScriptMousePositionPrev = (POSITION *) gos_Malloc(sizeof(POSITION), gScriptHeap[0]);
	ScriptMousePosition->x = 0;
	ScriptMousePosition->y = 0;
	ScriptMousePosition->z = 0;
	ScriptMousePositionLocal = (POSITION *) gos_Malloc(sizeof(POSITION), gScriptHeap[0]);
//
// Clear other variables
//
	pFocusedObject = NULL;
	pFocusedScript = NULL;
}

//
//
// Clean up all scripts that are running
//
//
void CleanUpScripts()
{
	HGOSSCRIPT itr = pListofScripts;
	HGOSSCRIPT next = NULL;
	while (itr)
	{
		next = itr->pNext;
		SPEW((GROUP_SCRIPTS,"Cleaning up script '%s'",itr->pMainObject->Name));
		gosScript_EndScript(itr);
		itr = next;
	}
	pListofScripts = NULL;

	if (pObjectArray) 
	{
		gos_Free( pObjectArray );					// Free up object memory pool
		pObjectArray = NULL;
	}
	if (pScriptArray)
	{
		gos_Free( pScriptArray );					// Free up object memory pool
		pScriptArray = NULL;
	}
	if( ScriptMousePosition)
	{
		gos_Free(ScriptMousePosition);
		ScriptMousePosition=NULL;	
	}
	if( ScriptMousePositionPrev)
	{
		gos_Free(ScriptMousePositionPrev);
		ScriptMousePositionPrev=NULL;	
	}
	if( ScriptMousePositionLocal)
	{
		gos_Free(ScriptMousePositionLocal);
		ScriptMousePositionLocal=NULL;	
	}

	// run thru all the registered variables and release them with notification

	REGVAR * rv = pScriptRegisteredVariables;
	if (rv)
	{
		SPEW((GROUP_SCRIPTS,"GOSSCRIPT ALERT: Registered Variable still remain"));
	}
	REGVAR * tmp = NULL;
	while (rv)
	{
		SPEW((GROUP_SCRIPTS,"    %s",rv->Name));
		tmp = rv;
		rv = rv->pNext;
		gos_Free(tmp);
	}
	pScriptRegisteredVariables=0;
}


//
//
//
#if (DISTRIBUTED_GOSSCRIPT == 0)
#undef STRICT
#include "..\GameOS\pch.hpp"
#include "..\GameOS\Debugger.hpp"
#include "..\GameOS\DXRasterizer.hpp"
#include "..\GameOS\DirectX.hpp"
#include "..\GameOS\3DRasterizer.hpp"
#include "..\GameOS\Texture Manager.hpp"
#include "..\GameOS\Time.hpp"
#endif

//
// Draw a square region using quads in a given color
//
void DrawRect( int x1, int y1, int x2, int y2, DWORD Color )
{
	FlushLineBuffer();

	if (x1 < 0) x1 = 0;
	if (x2 < 0) x2 = 0;
	if (y1 < 0) y1 = 0;
	if (y2 < 0) y2 = 0;
	if (x1 > Environment.screenWidth) x1 = Environment.screenWidth;
	if (x2 > Environment.screenWidth) x2 = Environment.screenWidth;
	if (y1 > Environment.screenHeight) y1 = Environment.screenHeight;
	if (y2 > Environment.screenHeight) y2 = Environment.screenHeight;
	if (x1 == x2 || y1 == y2) return;
//
// Fast path for blade? (Not implemented yet)
//
#if (DISTRIBUTED_GOSSCRIPT != 0)
	if( (!HardwareRenderer || 3==Environment.Renderer) &&
		  0==RenderMode &&
		  0==DontFastPath &&
		  EnableFastPath &&
		((Color&0xff000000)==0xff000000) )
	{
		if( x2<x1 )
		{
			int temp=x2;
			x2=x1;
			x1=temp;
		}
		if( y2<y1 )
		{
			int temp=y2;
			y2=y1;
			y1=temp;
		}

		WORD* Dest=(WORD*)BackBufferddsd.lpSurface+x1+(y1*BackBufferddsd.lPitch>>1);
		
		if( ShowFastRect && (((int)(gos_GetElapsedTime(1)*2.0f))&1) )
		{			
			BYTE FillValue=(BYTE)((int)(gos_GetElapsedTime(1)*4.0f)&1 ? 255 : 0);
			for( int y=0; y<y2-y1; y++ )
			{
				memset( Dest, FillValue, (x2-x1)*2 );
				Dest+=BackBufferddsd.lPitch>>1;
			}
		}
		else
		{
			DWORD Pixel=GetPixelColor(Color);
			Pixel=Pixel+(Pixel<<16);
			DWORD Height=y2-y1;
			DWORD FillLength=x2-x1;
			DWORD Pitch=BackBufferddsd.lPitch-FillLength*2;

			_asm
			{
				push edi
				push esi

				mov edi,Dest
				mov eax,Pixel
				mov edx,Height

				test edi,2
				jnz OddStart

EvenStart:
				mov ecx,FillLength
				shr ecx,1
				rep stosd
				rcl ecx,1
				rep stosw

				add edi,Pitch

				dec edx
				jnz EvenStart
				jmp done

OddStart:
				stosw

				mov ecx,FillLength
				dec ecx

				shr ecx,1
				rep stosd
				rcl ecx,1
				rep stosw

				add edi,Pitch

				dec edx
				jnz OddStart
done:
				pop esi
				pop edi
			}
		}
		return;
	}
#endif
	
	gos_VERTEX pVertices[4];

	memset( pVertices, 0, sizeof(pVertices) );
	pVertices[0].rhw	= 1.0;
	pVertices[1].rhw	= 1.0;
	pVertices[2].rhw	= 1.0;
	pVertices[3].rhw	= 1.0;

	pVertices[0].x		= (float)x1;
	pVertices[0].y		= (float)y1;
	pVertices[0].argb	= Color;

	pVertices[1].x		= (float)x1;
	pVertices[1].y		= (float)y2;
	pVertices[1].argb	= Color;

	pVertices[2].x		= (float)x2;
	pVertices[2].y		= (float)y2;
	pVertices[2].argb	= Color;

	pVertices[3].x		= (float)x2;
	pVertices[3].y		= (float)y1;
	pVertices[3].argb	= Color;

	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_MonoEnable, 0 );
	if( (Color&0xff000000)==0xff000000 )
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	else
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );

	gos_DrawQuads( pVertices, 4 );
}






const int VerticesInLineBuffer=64;
static gos_VERTEX LineBuffer[VerticesInLineBuffer];
static DWORD NumberBufferedLines=0;
static DWORD LineBufferAlpha=-1;
//
// If there are lines buffered up, flush them
//
void FlushLineBuffer()
{
	if( NumberBufferedLines )
	{
		gos_DrawLines( LineBuffer, NumberBufferedLines );
		NumberBufferedLines=0;
	}
}

//
// Setup line batch buffer
//
void InitLineBuffer()
{
	memset( LineBuffer, 0, sizeof(LineBuffer) );
	for( int t0=0; t0<VerticesInLineBuffer; t0++ )
	{
		LineBuffer[t0].rhw=1.0f;
	}
	NumberBufferedLines=0;
	LineBufferAlpha=-1;
}


//
// Draw a line in a given color
//
void ScriptDrawLine( int X0, int Y0, int X1, int Y1, DWORD Color )
{
//
// Offscreen? - Ignore it.
//
	if( (Y0<0 && Y1<0) || (Y0>Environment.screenHeight && Y1>Environment.screenHeight) || (X0<0 && X1<0) || (X0>Environment.screenWidth && X1>Environment.screenWidth) )
	{
		return;
	}
//
// Clipping (actually culling!)
//
	if (X0< 0)
		X0 = 0;
	if (X1 < 0)
		X1 = 0;
	if (Y0 < 0)
		Y0 = 0;
	if (Y1 < 0)
		Y1 = 0;
	if (X0 > Environment.screenWidth-1)
		X0 = Environment.screenWidth-1;
	if (X1 > Environment.screenWidth-1)
		X1 = Environment.screenWidth-1;
	if (Y0 > Environment.screenHeight-1)
		Y0 = Environment.screenHeight-1;
	if (Y1 > Environment.screenHeight-1)
		Y1 = Environment.screenHeight-1;
	if (X0 == X1 && Y0 == Y1) 
		return;
//
// If the alpha blend is different, flush old lines   (Solid or not)
//
	DWORD LineAlpha=gos_Alpha_OneZero;

	if( (Color&0xff000000)!=0xff000000 )
		LineAlpha=gos_Alpha_AlphaInvAlpha;

	if( LineAlpha!=LineBufferAlpha || NumberBufferedLines==VerticesInLineBuffer )
		FlushLineBuffer();

	LineBufferAlpha=LineAlpha;
//
// Setup current renderstates
//
	if( NumberBufferedLines==0 )
	{
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_Filter, gos_FilterNone );
		gos_SetRenderState( gos_State_ZCompare, 0 );
		gos_SetRenderState( gos_State_ZWrite, 0 );
		gos_SetRenderState( gos_State_AlphaTest, 0 );
		gos_SetRenderState( gos_State_Perspective, 0 );
		gos_SetRenderState( gos_State_Specular, 0 );
		gos_SetRenderState( gos_State_Dither, 0 );
		gos_SetRenderState( gos_State_Clipping, 0 );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
		gos_SetRenderState( gos_State_MonoEnable, 0 );
		gos_SetRenderState( gos_State_AlphaMode, LineAlpha );
	}
//
// Fill in line buffer array with line data
//
	LineBuffer[NumberBufferedLines+0].x=(float)X0;
	LineBuffer[NumberBufferedLines+0].y=(float)Y0;

	LineBuffer[NumberBufferedLines+1].x=(float)X1;
	LineBuffer[NumberBufferedLines+1].y=(float)Y1;

	LineBuffer[NumberBufferedLines+0].argb=Color;
	LineBuffer[NumberBufferedLines+1].argb=Color;

	NumberBufferedLines+=2;
}				




//
// This function is called whenever a script object is created. It fleshes
// out the standard member variables for an object and adds them
// to the current object's variable list
//
void CreateDefaultMemberVariables(OBJECT * NewObject, DWORD Priority)
{
	VARIABLE* newVariable2=(VARIABLE*)gos_Malloc(sizeof(VARIABLE)+strlen("location")+1, gScriptHeap[3]);
	newVariable2->Type=VAR_POSITION;						// setup the location
	newVariable2->ArraySize=0;								// Not an array element
	newVariable2->NameLength=(BYTE)strlen("location");		// Not an array element
	strcpy( (char*)newVariable2->Name, "location");
	POSITION * loc = (POSITION *) gos_Malloc(sizeof(POSITION), gScriptHeap[3]);
	loc->x = loc->y = loc->z = 0;
	newVariable2->pPosition = loc;
	AddVariable( NewObject, SCOPE_OBJECT, newVariable2 );	// Link variable into correct list
	NewObject->Coordinates = loc;
	
	newVariable2=(VARIABLE*)gos_Malloc(sizeof(VARIABLE)+strlen("framerate")+1, gScriptHeap[3]);
	newVariable2->Type=VAR_INT;								// setup the location
	newVariable2->ArraySize=0;								// Not an array element
	newVariable2->NameLength=(BYTE)strlen("framerate");		// Not an array element
	strcpy( (char*)newVariable2->Name, "framerate");
	newVariable2->Value = ScriptsDefaultFrameRate;			// default framerate
	AddVariable( NewObject, SCOPE_OBJECT, newVariable2 );	// Link variable into correct list
	NewObject->FrameRate = &newVariable2->Value;
	
	newVariable2=(VARIABLE*)gos_Malloc(sizeof(VARIABLE)+strlen("priority")+1, gScriptHeap[3]);
	newVariable2->Type=VAR_INT;								// setup the location
	newVariable2->ArraySize=0;								// Not an array element
	newVariable2->NameLength=(BYTE)strlen("priority");		// Not an array element
	strcpy( (char*)newVariable2->Name, "priority");
	newVariable2->Value = Priority;	
	AddVariable( NewObject, SCOPE_OBJECT, newVariable2 );	// Link variable into correct list
	NewObject->Priority= (long *)&newVariable2->Value;				
	
	newVariable2=(VARIABLE*)gos_Malloc(sizeof(VARIABLE)+strlen("region")+1, gScriptHeap[3]);
	newVariable2->Type=VAR_CUBE;							// setup the location
	newVariable2->ArraySize=0;								// Not an array element
	newVariable2->NameLength=(BYTE)strlen("region");			// Not an array element
	strcpy( (char*)newVariable2->Name, "region");
	CUBE * cube = (CUBE *) gos_Malloc(sizeof(CUBE), gScriptHeap[3]);
	cube->p1.x = 0; 
	cube->p1.y = 0; 
	cube->p1.z = 0;
	cube->p2.x = 0;
	cube->p2.y = 0;
	cube->p2.z = 0;
	newVariable2->pCube = cube;
	AddVariable( NewObject, SCOPE_OBJECT, newVariable2 );	// Link variable into correct list
	NewObject->Cube = cube;
	
	newVariable2=(VARIABLE*)gos_Malloc(sizeof(VARIABLE)+strlen("always_in_region")+1, gScriptHeap[3]);
	newVariable2->Type=VAR_INT;									// setup the location
	newVariable2->ArraySize=0;									// Not an array element
	newVariable2->NameLength=(BYTE)strlen("always_in_region");	// Not an array element
	strcpy( (char*)newVariable2->Name, "always_in_region");
	newVariable2->Value = 0;									
	AddVariable( NewObject, SCOPE_OBJECT, newVariable2 );		// Link variable into correct list
	NewObject->AlwaysInRegion = &newVariable2->Value;				
	
	newVariable2=(VARIABLE*)gos_Malloc(sizeof(VARIABLE)+strlen("relative")+1, gScriptHeap[3]);
	newVariable2->Type=VAR_INT;									// setup the location
	newVariable2->ArraySize=0;									// Not an array element
	newVariable2->NameLength=(BYTE)strlen("relative");		// Not an array element
	strcpy( (char*)newVariable2->Name, "relative");
	newVariable2->Value = 0;									
	AddVariable( NewObject, SCOPE_OBJECT, newVariable2 );		// Link variable into correct list
	NewObject->bPosRelative = &newVariable2->Value;				

	newVariable2=(VARIABLE*)gos_Malloc(sizeof(VARIABLE)+strlen("catchup")+1, gScriptHeap[3]);
	newVariable2->Type=VAR_INT;								// setup the location
	newVariable2->ArraySize=0;								// Not an array element
	newVariable2->NameLength=(BYTE)strlen("catchup");		// Not an array element
	strcpy( (char*)newVariable2->Name, "catchup");
	newVariable2->Value = 1;								// catchup is on!
	AddVariable( NewObject, SCOPE_OBJECT, newVariable2 );	// Link variable into correct list
	NewObject->Catchup = &newVariable2->Value;				

	NewObject->dwAvailIME = 0;
	NewObject->dwBlockingFlags = 0;
	NewObject->dwDblClicks = 0;
	NewObject->dwIMEState = 0;
}


