#include "pch.hpp"
//===========================================================================//
// File:	 AnalyzeGUI.cpp													 //
// Project:  GameOS					                                         //
// Contents: Lab Rat type analyzer GUI display								 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 11/03/99  AG		Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "MemoryManager.hpp"
#include "Profiler.hpp"
#include "Debugger.hpp"
#include "Time.hpp"


AnalyzeData*	pAnalyzeDataMax;
AnalyzeData*	pAnalyzeDataMaxEnd;

AnalyzeData* FindNextData( DWORD Handle, AnalyzeData* CurrentPosition );
AnalyzeData* FindLastData( DWORD Handle, AnalyzeData* CurrentPosition );
void GenerateTraceData( DWORD Handle );
void DisplayTrace( DWORD Xpos, DWORD Ypos, DWORD Handle );
void AnalyzeLine( DWORD X1, DWORD Y1, DWORD X2, DWORD Y2, DWORD Color=0xff000000 );
void AnalyzeFlushLines();

extern DWORD gosColorTable[];
extern DWORD DoubleChr;






//
// Points to first 'frame grabbed'
//
AnalyzeData* FirstFrame;
//
// Points to last 'frame grabbed'
//
AnalyzeData* LastFrame;

//
// Length of data grabbed
//
DWORD FramesGrabbed;
__int64 CyclesGrabbed;
float SecondsGrabbed;


//
// Current display
//
bool InitedAnalyze=0;					// Set to true once inited
AnalyzeData* DisplayFirst;
AnalyzeData* DisplayLast;
DWORD DisplayedFrames; 
double CyclesPerPixel;
DWORD WidthOfTraces;					// Width of traces
DWORD BottomOfLines;					// Bottom of traces


//
// Mouse / region information
//
__int64 MouseAboveTime;					// Time that mouse is above
AnalyzeData* pFrameBeforeMouse;			// Frame marker before mouse position
AnalyzeData* pFrameAfterMouse;			// Frame marker after mouse position
DWORD ThisTrace;						// Handle to trace mouse above (or -1)
AnalyzeStat* ThisStat;					// Pointer to stat structure of trace mouse above
DWORD Selecting;						// Selecting with the mouse?
DWORD SelectStartX;
DWORD LeftSelect;
DWORD RightSelect;						// Select region






//
// Stores the order of the handles currently displayed[0] and pointer into TraceBuffer data[1]
//
DWORD DisplayOrder[32][2];					// Up to 32 traces displayed at once

//
// 0=Off, 1=On, 2=Changed (for on/off traces) - or values
//
// One entry per pixel across the screen
//
DWORD* TraceBuffer;
DWORD* EndOfTraceBuffer;

gos_VERTEX* LineBuffer;
DWORD		CurrentLine;





//
// When leaving analyze mode, clean up data
//
void DestroyAnalyzeData()
{
	RestartAnalyze();					// Clear old data
	InitedAnalyze=0;
	if( TraceBuffer )
	{
		gos_Free( TraceBuffer );
		TraceBuffer=0;
	}
	if( LineBuffer )
	{
		gos_Free( LineBuffer );
		LineBuffer=0;
	}
}


AnalyzeData* GetNext( AnalyzeData* pThis )
{
	if( pThis==LastFrame )
		return pThis;
	pThis++;
	if( pThis->Stat==(AnalyzeStat*)TypeEndOfBlock )
	{
		pThis=(AnalyzeData*)pThis->Value;
	}
	return pThis;
}

AnalyzeData* GetPrevious( AnalyzeData* pThis )
{
	if( pThis==FirstFrame )
		return pThis;
	AnalyzeData* pTemp=pAnalyzeDataStart;
	AnalyzeData* pLast=pAnalyzeDataStart;
	while( pTemp!=pThis )
	{
		pLast=pTemp;
		pTemp++;
		if( pTemp->Stat==(AnalyzeStat*)TypeEndOfBlock )
		{
			pTemp=(AnalyzeData*)pTemp->Value;
		}
	}
	return pLast;
}








//	return (double)(GetCycles()-StartCount)/frequency;


void DoAnalyzeMode()
{
//
// Stop getting analyze data when in analyze mode!
//
	if( !InitedAnalyze )
	{
		pAnalyzeDataMax=pAnalyzeData;
		pAnalyzeDataMaxEnd=pAnalyzeDataEnd;
		gAnalyzeEnable=0;
//
// Find first/Last frame grabbed (ignore data after last frame - it's not a complete frame!)
//
		FirstFrame=FindNextData( AnalyzeFrameNumber, pAnalyzeDataStart );
		LastFrame=FindLastData( AnalyzeFrameNumber, pAnalyzeDataMax );
//
// No data grabbed?
//
		if( 0==FirstFrame || 0==LastFrame )
			STOP(( "Invalid data" ));
//
// Work out frames / times grabbed
//
		FramesGrabbed=LastFrame->Value-FirstFrame->Value;
		CyclesGrabbed=LastFrame->TimeStamp-FirstFrame->TimeStamp;
		SecondsGrabbed=(float)(CyclesGrabbed/frequency);
//
// Work out first frame to view
//
		DisplayFirst=FirstFrame;
		DisplayLast=LastFrame;
//
// Work out initial display order (from order of activated traces)
//
		memset( DisplayOrder, 0, sizeof(DisplayOrder) );

		DWORD Current=0;
		AnalyzeData* pThis=FirstFrame;

		while( Current<32 )											// Record first 32 traces to be activated
		{
			if( pThis==DisplayLast )
				break;
			DWORD CurrentHandle=(DWORD)pThis->Stat;
			
			if( CurrentHandle==TypeEndOfBlock )						// Moved to next analyze block of data?
			{
				pThis=(AnalyzeData*)pThis->Value;
				CurrentHandle=(DWORD)pThis->Stat;
			}
			
			if( CurrentHandle!=2323 ) //AnalyzeFrameNumber )					// Frame number is an internal trace
			{
				for( int t0=0; t0!=Current; t0++ )
				{
					if( CurrentHandle==DisplayOrder[t0][0] )		// If duplicate, ignore
						break;
				}
				
				DisplayOrder[t0][0]=CurrentHandle;
				if( t0==Current )
					Current++;
			}

			pThis++;
//
// Clear variables
//
			LeftSelect=0;
			RightSelect=0;
		}
//
// Reserve memory for trace buffer storage (maximum screen width)
//
		TraceBuffer=(DWORD*)gos_Malloc( 1600*4*32, AnalyzeHeap );
//
// Reserve memory for line buffer
//
		LineBuffer=(gos_VERTEX*)gos_Malloc( 200*sizeof(gos_VERTEX), AnalyzeHeap );
		CurrentLine=0;
//
// Do not init these variables again
//
		InitedAnalyze=1;
	}
//
// Calculate information scaling
//
	DisplayedFrames=DisplayLast->Value-DisplayFirst->Value;
	CyclesPerPixel=double(DisplayLast->TimeStamp-DisplayFirst->TimeStamp) / double(Environment.screenWidth-120);
	WidthOfTraces=Environment.screenWidth-120;

//
// Display title in large letters
//
	InitTextDisplay();
	CurrentX=Environment.screenWidth/2-(7*6);
	CurrentY=1;
	DbChrY=0;
	DbChrX=0;
	DbMaxX=80;
	DbMaxY=80;
	DoubleChr=1;
	DrawText( 0xffffff, "ANALYZE" );
	DoubleChr=0;
//
// Display information above traces
//
	InitTextDisplay();
	CurrentX=10;
	CurrentY=10;
	DbChrY=0;
	DbChrX=0;
	DbMaxX=80;
	DbMaxY=80;
	char Buffer[64];
	double Time=(double)((DisplayLast->TimeStamp-DisplayFirst->TimeStamp)/frequency);
	sprintf( Buffer, "Frames=%d, Time=%5.2fs",DisplayedFrames,Time );
	DrawText( 0xffffff, Buffer );
//
// Show scale at left edge
//
	sprintf( Buffer, "Frame=% 7d",DisplayFirst->Value );
	CurrentX=116;
	CurrentY=80-9;
	DrawText( 0xffffff, Buffer );
	sprintf( Buffer, "Time =%7.3f",(double)(DisplayFirst->TimeStamp-StartCount)/frequency );
	CurrentX=116;
	CurrentY=80-17;
	DrawText( 0xffffff, Buffer );
//
// Show scale at right edge
//
	sprintf( Buffer, "Time =%7.3f",(double)(DisplayLast->TimeStamp-StartCount)/frequency );
	CurrentX=116+WidthOfTraces-strlen(Buffer)*6;
	CurrentY=80-17;
	DrawText( 0xffffff, Buffer );
	CurrentX=116+WidthOfTraces-strlen(Buffer)*6;
	CurrentY=80-9;
	sprintf( Buffer, "Frame=% 7d",DisplayLast->Value );
	DrawText( 0xffffff, Buffer );




//
// Generate / Display traces
//
	DWORD Ypos=80;
	DWORD CurrentTrace=0;

	EndOfTraceBuffer=TraceBuffer;

	CurrentLine=0;
	ThisTrace=-1;

	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );

	while( Ypos<Environment.screenHeight-80 && CurrentTrace<32 )
	{
		if( DisplayOrder[CurrentTrace][0] )
		{
			if( DBMouseY>=Ypos && DBMouseY<Ypos+32 && DBMouseX>=116 && DBMouseX<116+WidthOfTraces )
			{
				ThisTrace=CurrentTrace;
				ThisStat=(AnalyzeStat*)DisplayOrder[CurrentTrace][0];
			}
			GenerateTraceData( CurrentTrace );
			DisplayTrace( 116, Ypos, CurrentTrace );
			Ypos+=32;
		}
		CurrentTrace++;
	}
	AnalyzeFlushLines();
	BottomOfLines=Ypos;




//
// Check for controls
//
	bool ShiftDown=KEY_FREE!=gos_GetKeyStatus(KEY_LSHIFT);
	DWORD KeyPress=(gos_GetKey()>>8)&~0xff00;




	MouseAboveTime=0;
	pFrameBeforeMouse=0;
	pFrameAfterMouse=0;
//
// Cursor inside trace region?
//
	if( DBMouseY>=80 && DBMouseY<BottomOfLines && DBMouseX>=116 && DBMouseX<116+WidthOfTraces )
	{
//
// Find exact time where mouse is
//
		MouseAboveTime=(__int64)(DisplayFirst->TimeStamp+CyclesPerPixel*(DBMouseX-116));
//
// Find frame start before mouse position
//
		pFrameBeforeMouse=pFrameAfterMouse=DisplayFirst;
		while( pFrameAfterMouse->TimeStamp < MouseAboveTime )
		{
			pFrameBeforeMouse=pFrameAfterMouse;
			pFrameAfterMouse=FindNextData( AnalyzeFrameNumber, GetNext(pFrameAfterMouse) );
		}
//
// Mouse down? (Not shift mode)
//
		if( ShiftDown )
		{
			Selecting=0;
			LeftSelect=0;
			RightSelect=0;
		}
		else
		{
			if( DBButtons&1 )
			{
				if( !Selecting )				// Start of selection?
				{
					Selecting=1;
					SelectStartX=DBMouseX;
				}
				LeftSelect=SelectStartX > DBMouseX ? DBMouseX : SelectStartX;
				RightSelect=SelectStartX < DBMouseX ? DBMouseX : SelectStartX;
			}
			else
			{
				Selecting=0;
			}
		}
	}


//
// Is there a region selected currently?
//
	if( LeftSelect!=RightSelect )
	{
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
		DrawSquare( LeftSelect, 80, RightSelect-LeftSelect, BottomOfLines-80, 0x80004000 );
	}
//
// Display ruler (after select region)
//
	if( MouseAboveTime && ThisTrace!=-1 )
	{
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		AnalyzeLine( DBMouseX, 80, DBMouseX, BottomOfLines, 0xffffffff );
		AnalyzeFlushLines();
//
// Display information about trace under mouse
//
		DWORD InfoSizeX=100;
		DWORD InfoSizeY=64;

		DWORD InfoX=DBMouseX+10;
		if( DBMouseX > 116+WidthOfTraces/2 )
			InfoX=DBMouseX-InfoSizeX-1;
		DWORD InfoY=DBMouseY+20;
		if( DBMouseY > (BottomOfLines-80)/2+80 )
			InfoY=DBMouseY-InfoSizeY-6;
		
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		DrawSquare( InfoX, InfoY, InfoSizeX, InfoSizeY, 0xffffffff );
//
// Display Text in info box (inside selected region)
//
		InitTextDisplay();
		StartX=CurrentX=InfoX+2;
		StartY=CurrentY=InfoY+2;
		DbChrY=0;
		DbChrX=0;
		DbMaxX=InfoSizeX/6-4;
		DbMaxY=InfoSizeY/8-4;
//
// Text is different based on type of trace
//
		if( LeftSelect!=RightSelect && DBMouseX>=LeftSelect && DBMouseX<=RightSelect )
		{
			if( ThisStat->Type==gos_TypeRoutine )
			{
//
// Average of in/out %in %out, Len In, Len Out
//
				DrawText( 0xff000000, "Num In   123456\n"
									  "Num Out  123456\n"
									  "%%In      123%%\n"
									  "%%Out     123%%\n"
									  "Time In  123456ms\n"
									  "Time Out 123456ms" );

			}
			else
			{



			}
		}
		else
//
// Display text in info box for the current trace the mouse is above
//
		{
			if( ThisStat->Type==gos_TypeRoutine )
			{
				


			}
			else
			{



			}
		}
	}










//
// Cursor keys left / right - move frame forwards and backwards
//
	if( KeyPress==KEY_LEFT )
	{
		AnalyzeData* pThis=FindLastData( AnalyzeFrameNumber, GetPrevious(DisplayFirst) );
		if( pThis && pThis!=DisplayLast )
			DisplayFirst=pThis;
		pThis=FindLastData( AnalyzeFrameNumber, GetPrevious(DisplayLast) );
		if( pThis && pThis!=DisplayFirst )
			DisplayLast=pThis;
	}
	if( KeyPress==KEY_RIGHT )
	{
		AnalyzeData* pThis=FindNextData( AnalyzeFrameNumber, GetNext(DisplayLast) );
		if( pThis && pThis!=DisplayFirst )
			DisplayLast=pThis;
		pThis=FindNextData( AnalyzeFrameNumber, GetNext(DisplayFirst) );
		if( pThis && pThis!=DisplayLast )
			DisplayFirst=pThis;
	}
	if( KeyPress==KEY_HOME )
	{
		DisplayFirst=FindNextData( AnalyzeFrameNumber, pAnalyzeDataStart );
		DisplayLast=FindNextData( AnalyzeFrameNumber, GetNext(DisplayFirst) );
	}
	if( KeyPress==KEY_END )
	{
		DisplayLast=FindLastData( AnalyzeFrameNumber, pAnalyzeDataMax );
		DisplayFirst=FindLastData( AnalyzeFrameNumber, DisplayLast );
	}




//
// Shift and left button OR wheel - zoom in a frame
//
	if( MouseAboveTime && (ShiftDown && DBButtons&1) || DBWheelDelta>0 )
	{
		if( MouseAboveTime-pFrameBeforeMouse->TimeStamp > pFrameAfterMouse->TimeStamp-MouseAboveTime )
		{
			AnalyzeData* pThis=FindNextData( AnalyzeFrameNumber, GetNext(DisplayFirst) );
			if( pThis && pThis!=DisplayLast )
				DisplayFirst=pThis;
		}
		else
		{
			AnalyzeData* pThis=FindLastData( AnalyzeFrameNumber, GetPrevious(DisplayLast) );
			if( pThis && pThis!=DisplayFirst )
				DisplayLast=pThis;
		}
	}
//
// Shift and right button OR wheel - zoom out a frame
//
	if( MouseAboveTime && (ShiftDown && DBButtons&2) || DBWheelDelta<0 )
	{
		if( MouseAboveTime-pFrameBeforeMouse->TimeStamp > pFrameAfterMouse->TimeStamp-MouseAboveTime )
		{
			AnalyzeData* pThis=FindLastData( AnalyzeFrameNumber, GetPrevious(DisplayFirst) );
			if( pThis && pThis!=DisplayLast )
				DisplayFirst=pThis;
		}
		else
		{
			AnalyzeData* pThis=FindNextData( AnalyzeFrameNumber, GetNext(DisplayLast) );
			if( pThis && pThis!=DisplayFirst )
				DisplayLast=pThis;
		}
	}



}




//
//
//
void GenerateTraceData( DWORD Trace )
{
	DWORD Handle=DisplayOrder[Trace][0];
	AnalyzeStat* pStat=(AnalyzeStat*)Handle;

	if( pStat )
	{
		DisplayOrder[Trace][1]=(DWORD)EndOfTraceBuffer;
		DWORD* CurrentData=EndOfTraceBuffer;

		AnalyzeData* FirstValue=FindLastData( Handle, DisplayFirst );
		AnalyzeData* NextValue=FindNextData( Handle, DisplayFirst );

		bool Changes;
		double CurrentCycles=(double)DisplayFirst->TimeStamp;
		DWORD Data=pStat->Type==gos_TypeRoutine ? TypeExit : 0;

		if( FirstValue )
			Data=FirstValue->Value;

		for( int t0=0; t0<WidthOfTraces; t0++ )
		{
			CurrentData[t0]=Data;
			
			Changes=0;
			
			while( NextValue && NextValue->TimeStamp<=CurrentCycles )
			{
				Data=NextValue->Value;
				NextValue=FindNextData( Handle, NextValue+1 );
				Changes=1;
			}

			if( Changes && pStat->Type==gos_TypeRoutine )
			{
				CurrentData[t0]=TypeEnterExit;
			}
		
			CurrentCycles+=CyclesPerPixel;
		}
	
		EndOfTraceBuffer+=WidthOfTraces;
	}
}




//
//
//
void AnalyzeLine( DWORD X1, DWORD Y1, DWORD X2, DWORD Y2, DWORD Color )
{
	gosASSERT( (X1<Environment.screenWidth) || (X2<Environment.screenWidth) );

	LineBuffer[CurrentLine].x=(float)X1;
	LineBuffer[CurrentLine].y=(float)Y1;
	LineBuffer[CurrentLine].z=0;
	LineBuffer[CurrentLine].u=0;
	LineBuffer[CurrentLine].v=0;
	LineBuffer[CurrentLine].argb=Color;
	LineBuffer[CurrentLine].rhw=1.0f;

	LineBuffer[CurrentLine+1].x=(float)X2;
	LineBuffer[CurrentLine+1].y=(float)Y2;
	LineBuffer[CurrentLine+1].z=0;
	LineBuffer[CurrentLine+1].u=0;
	LineBuffer[CurrentLine+1].v=0;
	LineBuffer[CurrentLine+1].argb=Color;
	LineBuffer[CurrentLine+1].rhw=1.0f;

	CurrentLine+=2;

	if( CurrentLine==200 )
		AnalyzeFlushLines();
}

void AnalyzeFlushLines()
{
	if( CurrentLine )
	{
		gos_DrawLines( LineBuffer, CurrentLine );
		CurrentLine=0;
	}
}





//
// Display a single trace line on the screen
//
void DisplayTrace( DWORD Xpos, DWORD Ypos, DWORD Trace )
{
	DWORD* Pointer=(DWORD*)DisplayOrder[Trace][1];
	DWORD RightEdge=Xpos+WidthOfTraces;
	AnalyzeStat* pStat=(AnalyzeStat*)DisplayOrder[Trace][0];
	
	if( Pointer )
	{
//
// Display white box for traces
//
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		DrawSquare( Xpos-1, Ypos, WidthOfTraces+2, 32, 0xffa0a0a0 );
//
// Display name of trace
//
		InitTextDisplay();
		CurrentX=1;
		if( strlen(pStat->Name)<19 )
			CurrentX+=(19-strlen(pStat->Name))*6-2;
		CurrentY=Ypos+11;
		DbChrY=0;
		DbChrX=0;
		DbMaxX=19;
		DbMaxY=80;
		DrawText( 0xffffff, pStat->Name );
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
//
// Display trace data
//
		if( pStat->Type==gos_TypeRoutine )
		{
			while( Xpos<RightEdge )
			{
				if( TypeEnterExit==*Pointer )
				{
					AnalyzeLine( Xpos, Ypos+5, Xpos, Ypos+26 );
					Xpos++;
					Pointer++;
				}
				else
				{
					DWORD StartX=Xpos++;
					DWORD Level=*Pointer++;
					while( *Pointer==Level && Xpos<RightEdge )
					{
						Pointer++;
						Xpos++;
					}
					DWORD YLevel=Level==TypeEnter ? Ypos+5 : Ypos+25;
					AnalyzeLine( StartX, YLevel, Xpos, YLevel );
				}
			}
		}
//
// Data values - change color when values change
//
		else
		{
			DWORD CurrentLevel=*Pointer;
			while( Xpos<RightEdge )
			{
				Pointer++;
				if( *Pointer!=CurrentLevel )
				{
					AnalyzeLine( Xpos, Ypos+5, Xpos, Ypos+26 );
					Xpos++;
					CurrentLevel=*Pointer;
				}
				else
				{
					DWORD StartX=Xpos++;
					while( *Pointer==CurrentLevel && Xpos<RightEdge )
					{
						Pointer++;
						Xpos++;
					}
					AnalyzeLine( StartX, Ypos+25, Xpos, Ypos+25 );
				}
			}
		}
	}
}










//
// Finds first data point for a given statistc AFTER the current position (or 0 returned)
//
AnalyzeData* FindNextData( DWORD Handle, AnalyzeData* CurrentPosition )
{
	AnalyzeData* pThis=CurrentPosition;

	while( pThis<pAnalyzeDataMax )
	{
		if( pThis->Stat==(AnalyzeStat*)Handle )
		{
			return pThis;
		}
		if( pThis->Stat==(AnalyzeStat*)TypeEndOfBlock )
		{
			pThis=(AnalyzeData*)pThis->Value;
		}
		else
		{
			pThis++;
		}
	}
	return (AnalyzeData*)0;
}

//
// Finds last data point for a given statistc BEFORE the current position (or 0 returned)
//
AnalyzeData* FindLastData( DWORD Handle, AnalyzeData* CurrentPosition )
{
	AnalyzeData* LastFound=0;
	AnalyzeData* pThis=pAnalyzeDataStart;

	while( pThis<CurrentPosition )
	{
		if( pThis->Stat==(AnalyzeStat*)Handle )
		{
			LastFound=pThis;
		}
		if( pThis->Stat==(AnalyzeStat*)TypeEndOfBlock )
		{
			pThis=(AnalyzeData*)pThis->Value;
		}
		else
		{
			pThis++;
		}
	}
	return (AnalyzeData*)LastFound;
}



