/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***                            Confidential - Westwood Studios                              ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Commando                                                     *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Commando/textdisplay.cpp        $*
 *                                                                                             *
 *                      $Author:: Steve_t                                                     $*
 *                                                                                             *
 *                     $Modtime:: 1/02/02 2:27a                                               $*
 *                                                                                             *
 *                    $Revision:: 55                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "textdisplay.h"
#include "font3d.h"
#include "assets.h"
#include "render2D.h"
#include "timemgr.h"
#include "_globals.h"
#include "registry.h"
#include "wwprofile.h"
#include "combat.h"
#include "ccamera.h"
#include "render2Dsentence.h"
#include "wwmemlog.h"
#include "consolemode.h"

/*
** TextDisplayLine
*/
TextDisplayLine::TextDisplayLine( const WideStringClass & text, unsigned long color ) :
	Time( 0 ),
	LineCount( 0 ),
	Text( text ),
	Color( color )
{
	for( const WCHAR * ch = text; *ch; ch++ ) {
		if ( *ch == (WCHAR)'\n' ) {
			LineCount++;
		}
	}
}

/*
** TextDisplayGameMode
*/
TextDisplayGameModeClass * TextDisplayGameModeClass::Instance = NULL;

void	TextDisplayGameModeClass::Init()
{
	if (!ConsoleBox.Is_Exclusive()) {
		// Build Fonts
		WWASSERT(WW3DAssetManager::Get_Instance() != NULL);
		Font = WW3DAssetManager::Get_Instance()->Get_Font3DInstance( "FONT8x8.TGA" );
   	WWASSERT(Font != NULL);
		SET_REF_OWNER( Font );
		MonoFont = WW3DAssetManager::Get_Instance()->Get_Font3DInstance( "FONT8x8.TGA" );
   	WWASSERT(MonoFont != NULL);
		SET_REF_OWNER( MonoFont );
		MonoFont->Set_Mono_Spaced();


		// Update Instance
		WWASSERT( TextDisplayGameModeClass::Instance == NULL );
		TextDisplayGameModeClass::Instance = this;

		DisplayVisWarning = false;
		MaxScrollLines = 30;
		ScrollLinesPersistTime = 10;

		Load_Registry_Keys();

		Display = new Render2DTextClass(Font);
		Display->Set_Coordinate_Range( Render2DClass::Get_Screen_Resolution() );

		VerboseDisplay = new Render2DTextClass(Font);
		VerboseDisplay->Set_Coordinate_Range( Render2DClass::Get_Screen_Resolution() );

		StatisticsDisplay = new Render2DTextClass(MonoFont);
		StatisticsDisplay->Set_Coordinate_Range( Render2DClass::Get_Screen_Resolution() );

		RendererLines.Reset_Active();
		RendererColors.Reset_Active();
		TextChanged=true;
		VerboseTextChanged=true;
		DisplayY=0.0f;
	}
	return ;
}

void 	TextDisplayGameModeClass::Shutdown()
{
	if (!ConsoleBox.Is_Exclusive()) {
		Save_Registry_Keys();

   	Flush();

		delete StatisticsDisplay;
		StatisticsDisplay=NULL;

		delete VerboseDisplay;
		VerboseDisplay=NULL;

		delete Display;
		Display=NULL;

		REF_PTR_RELEASE( Font );
		REF_PTR_RELEASE( MonoFont );

		// Update Instance
		WWASSERT( TextDisplayGameModeClass::Instance == this );
		TextDisplayGameModeClass::Instance = NULL;
	}
}

/*
**
*/
void TextDisplayGameModeClass::Load_Registry_Keys(void)
{
	RegistryClass * registry = new RegistryClass( APPLICATION_SUB_KEY_NAME_OPTIONS );
	WWASSERT( registry );
	if ( registry->Is_Valid() ) {
		MaxScrollLines = registry->Get_Int( "MaxScrollLines", MaxScrollLines );
		ScrollLinesPersistTime = registry->Get_Float( "ScrollLinesPersistTime", ScrollLinesPersistTime );
	}
	delete registry;
}

void TextDisplayGameModeClass::Save_Registry_Keys(void)
{
	RegistryClass * registry = new RegistryClass( APPLICATION_SUB_KEY_NAME_OPTIONS );
	WWASSERT( registry );
	if ( registry->Is_Valid() ) {
		registry->Set_Int( "MaxScrollLines", MaxScrollLines );
		registry->Set_Float( "ScrollLinesPersistTime", ScrollLinesPersistTime );
	}
	delete registry;
}

/*
**
*/
void 	TextDisplayGameModeClass::Think()
{
	WWPROFILE( "TextDisplay Think" );

	if (Font == NULL) {
		return;
	}
	// Update time on all scroll lines
	SLNode<TextDisplayLine> *linenode;
	for (	linenode = ScrollLines.Head(); linenode; linenode = linenode->Next() ) {
		TextDisplayLine *line = linenode->Data();
		line->Update_Time( TimeManager::Get_Frame_Seconds() );
	}

	// never have more than 2 over the max number
	while ( ScrollLines.Get_Count() && (Count_Scroll_Lines() >= MaxScrollLines + 2) ) {
		delete ScrollLines.Remove_Head();
	}

	// if there are lines to remove by count or time
	if ( ScrollLines.Get_Count() &&
				((ScrollLines.Head()->Data()->Update_Time(0) > ScrollLinesPersistTime) ||
				 (Count_Scroll_Lines() > MaxScrollLines) ) ) {
		// bump scroll position
		float char_height = Font->Char_Height();
		VerticalScroll += TimeManager::Get_Frame_Seconds() * char_height * 4;
		if ( VerticalScroll >= char_height ) {
			// top line is hidden, remove it
			VerticalScroll -= char_height;
			int lines = Count_Scroll_Lines();
			while ( ScrollLines.Get_Count() && (Count_Scroll_Lines() >= lines) ) {
				delete ScrollLines.Remove_Head();
			}
		}
	}
	// if no more need to be removed, clear scroll
	if ( ScrollLines.Get_Count() &&
				((ScrollLines.Head()->Data()->Update_Time(0) > ScrollLinesPersistTime) ||
				 (Count_Scroll_Lines() > MaxScrollLines) ) ) {
	} else {
		VerticalScroll = 0;
	}
}

void 	TextDisplayGameModeClass::Render()
{
	WWMEMLOG(MEM_GAMEDATA);
	WWPROFILE( "TextDisplay Render" );

	if (Font == NULL) {
		return;
	}

	// Verbose help first
	if (VerboseTextChanged) {
		VerboseTextChanged=false;
		VerboseDisplay->Reset();
		VerboseDisplay->Set_Location( Vector2( 0, 0 ) );
		VerboseDisplay->Draw_Text( VerboseHelpText, 0xFF00FFFF );
	}
	VerboseDisplay->Render();

	// Then statistics

	StatisticsDisplayManager::Render( StatisticsDisplay );

	// And then the rest of the text...

	bool changed=TextChanged;
	TextChanged=false;
	if ( DisplayVisWarning && StatisticsDisplayManager::Is_Current_Display( "fps" ) ) {
		changed=true;
	}

	// never have more than 2 over the max number
	while ( ScrollLines.Get_Count() && (Count_Scroll_Lines() >= MaxScrollLines + 2) ) {
		delete ScrollLines.Remove_Head();
		changed=true;
	}

	// Check if the text lines have changed
	SLNode<TextDisplayLine> *linenode;
	if (!changed) {
		int j=0;
		WWASSERT(RendererLines.Count()==RendererColors.Count());
		for (	linenode = ScrollLines.Head(); linenode; linenode = linenode->Next(), j++ ) {
			if (j>=RendererLines.Count()) {
				changed=true;
				break;
			}
			if (RendererColors[j]!=linenode->Data()->Color || RendererLines[j]!=linenode->Data()->Text) {
				changed=true;
				break;
			}
		}
	}

	float y=WWMath::Floor( -VerticalScroll );
	if (y==0.0f && DisplayY!=0.0f) changed=true;
	float delta=y-DisplayY;
	DisplayY=y;

	if (!changed) {
		if (delta!=0.0f) Display->Move(Vector2(0.0f,delta));
		Display->Render();
		return;
	}

	// Display
	Display->Reset();

	// Cache the lines that are rendered
	RendererLines.Reset_Active();
	RendererColors.Reset_Active();
	for (	linenode = ScrollLines.Head(); linenode; linenode = linenode->Next() ) {
		Display->Draw_Text( linenode->Data()->Text, linenode->Data()->Color );
		RendererLines.Add(linenode->Data()->Text);
		RendererColors.Add(linenode->Data()->Color);
	}

	Display->Draw_Text( InputText );
	Display->Draw_Text( HelpText );

	if ( DisplayVisWarning && StatisticsDisplayManager::Is_Current_Display( "fps" ) ) {
		Vector2 pos = (Render2DClass::Get_Screen_Resolution().Upper_Left() + Render2DClass::Get_Screen_Resolution().Lower_Left()) * 0.5f;
		Display->Set_Location( pos );
		Display->Draw_Text( "Vis Sector Not Found!", 0xFF0000FF );
	}

	Display->Render();
}

void 	TextDisplayGameModeClass::Flush( void )
{
	while ( ScrollLines.Get_Count() ) {
		delete ScrollLines.Remove_Head();
	}
}

int	TextDisplayGameModeClass::Count_Scroll_Lines( void )
{
	int count = 0;
	SLNode<TextDisplayLine> *linenode;
	for (	linenode = ScrollLines.Head(); linenode; linenode = linenode->Next() ) {
		count += linenode->Data()->Get_Line_Count();
	}
	return count;
}

/*
** Printing
*/
void	TextDisplayGameModeClass::Print( const char * string, const Vector4 & color )
{
	if (Font == NULL) {
		return;
	}
	unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|(unsigned(color[3]*255.0f));
	WideStringClass wide_string;
	wide_string.Convert_From( string );
	TextDisplayLine * line = new TextDisplayLine( wide_string, col );
   ScrollLines.Add_Tail( line );
}

void	TextDisplayGameModeClass::Print( const WideStringClass & string, const Vector4 & color )
{
	if (Font == NULL) {
		return;
	}
	unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|(unsigned(color[3]*255.0f));
	TextDisplayLine * line = new TextDisplayLine( string, col );
   ScrollLines.Add_Tail( line );
}

void	TextDisplayGameModeClass::Print( const char * string, const Vector3 & color )
{
	if (Font == NULL) {
		return;
	}
	//TSS unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|0xff;
	unsigned long col=(unsigned(color[0]*255.0f)<<16)|(unsigned(color[1]*255.0f)<<8)|(unsigned(color[2]*255.0f))|0xFF000000;
	WideStringClass wide_string;
	wide_string.Convert_From( string );
	TextDisplayLine * line = new TextDisplayLine( wide_string, col );
   ScrollLines.Add_Tail( line );
}


void	TextDisplayGameModeClass::Print( const WideStringClass & string, const Vector3 & color )
{
	if (Font == NULL) {
		return;
	}
	//TSS unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|0xff;
	unsigned long col=(unsigned(color[0]*255.0f)<<16)|(unsigned(color[1]*255.0f)<<8)|(unsigned(color[2]*255.0f))|0xFF000000;
	TextDisplayLine * line = new TextDisplayLine( string, col );
   ScrollLines.Add_Tail( line );
}

void	TextDisplayGameModeClass::Print_System( const char * format, ... )
{
	va_list arg_list;
	va_start (arg_list, format);
	StringClass string;
	string.Format_Args( format, arg_list );
	va_end (arg_list);

	Print( string, Vector4( 1,1,0,1 ) );
}

void	TextDisplayGameModeClass::Print_System( const WideStringClass & string )
{
	Print( string, Vector4( 1,1,0,1 ) );
}


void	TextDisplayGameModeClass::Print_Informational( const char * format, ... )
{
	va_list arg_list;
	va_start (arg_list, format);
	StringClass string;
	string.Format_Args( format, arg_list );
	va_end (arg_list);

	Print( string, Vector4( 0.7f,0.7f,0.7f,1.0f ) );
}

void	TextDisplayGameModeClass::Print_Informational( const WideStringClass & string )
{
	Print( string, Vector4( 0.7f,0.7f,0.7f,1.0f ) );
}

/*
** TextDebugDisplayHandler
*/
void	TextDebugDisplayHandlerClass::Display_Text( const char * string, const Vector4 & color )
{
	if (Get_Text_Display() != NULL) {
		Get_Text_Display()->Print( string, color );
	}
}

void	TextDebugDisplayHandlerClass::Display_Text( const WideStringClass & string, const Vector4 & color )
{
	if (Get_Text_Display() != NULL) {
		Get_Text_Display()->Print( string, color );
	}
}


/*
** StatisticsDisplay
*/
static float													_StatsScrollOffset = 0;
static StringClass											_StatsTitle;
static StringClass											_StatsText;
static unsigned long											_StatsColor = 0xFFFFFFFF;
static Vector2													_StatsLocation( 0,0 );

void	StatisticsDisplayManager::Render(Render2DTextClass * renderer)
{
	if (WWDEBUG_TRIGGER(WWDEBUG_TRIGGER_GENERIC0)) {
		_StatsScrollOffset -= 32.0f;
	}
	if (WWDEBUG_TRIGGER(WWDEBUG_TRIGGER_GENERIC1)) {
		_StatsScrollOffset += 32.0f;
	}

	renderer->Reset();
	renderer->Set_Location( _StatsLocation );
	renderer->Draw_Text( _StatsText, _StatsColor );
	renderer->Render();
}

void	StatisticsDisplayManager::Set_Display( const char * title )
{
	_StatsScrollOffset = 0;
	_StatsTitle = title;
	_StatsText = "";
}

bool StatisticsDisplayManager::Is_Current_Display( const char* title) // Returns true if "title" is currently active
{
	return !stricmp( _StatsTitle, title );
}

void	StatisticsDisplayManager::Set_Stat( const char * title, const char * text, unsigned long color, const Vector2& location )
{
	if ( Is_Current_Display( title ) ) {
		_StatsText = text;
		_StatsColor = color;

		if ( location.Y == -240 ) {
			// Handle default case
			Vector2 pos = (Render2DClass::Get_Screen_Resolution().Upper_Left() + Render2DClass::Get_Screen_Resolution().Upper_Right()) * 0.5f;
			_StatsLocation = pos;
		} else {
			_StatsLocation = location;
		}

	}
}
