/*
**	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/teammanager.cpp                    $*
 *                                                                                             *
 *                      $Author:: Tom_s                                                       $*
 *                                                                                             *
 *                     $Modtime:: 2/22/02 11:29a                                              $*
 *                                                                                             *
 *                    $Revision:: 73                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "teammanager.h" // I WANNA BE FIRST!

#include <stdio.h>
#include "miscutil.h"
#include "_globals.h"
#include "assets.h"
#include "cnetwork.h"
//#include "gamesettings.h"
#include "multihud.h"
#include <win.h>
#include "player.h"
#include "font3d.h"
#include "gamedata.h"
#include "useroptions.h"
#include "smartgameobj.h"
#include "playertype.h"
#include "colors.h"
#include "render2d.h"
#include "wwprofile.h"
#include "gametype.h"
#include "playermanager.h"
#include "translatedb.h"
#include "string_ids.h"
#include "consolemode.h"
#include "gamespyadmin.h"

//
// Class statics
//
SList<cTeam>				cTeamManager::TeamList;
cTeam *						cTeamManager::Team_Array[];
float							cTeamManager::TeamListHeight				= 0;
Render2DTextClass	*		cTeamManager::PTextRenderer				= NULL;
Font3DInstanceClass *	cTeamManager::PFont							= NULL;
const float					cTeamManager::Y_INCREMENT_FACTOR			= 1.2f;
int							cTeamManager::XPos							= 0;
int							cTeamManager::YPos							= 0;

static WideStringClass renderer_team_heading;
static WideStringClass renderer_team_strings[MAX_TEAMS];
static Vector3 renderer_team_colors[MAX_TEAMS];
static bool renderer_is_intermission_true;

//------------------------------------------------------------------------------------
void cTeamManager::Onetime_Init(void)
{
	if (!ConsoleBox.Is_Exclusive()) {
		WWASSERT(PFont == NULL);
		WWASSERT(WW3DAssetManager::Get_Instance() != NULL);
   	PFont = WW3DAssetManager::Get_Instance()->Get_Font3DInstance("FONT6x8.TGA");
   	WWASSERT(PFont != NULL);
		PFont->Set_Mono_Spaced();
		SET_REF_OWNER(PFont);

		WWASSERT(PTextRenderer == NULL);
		PTextRenderer = new Render2DTextClass(PFont);
   	WWASSERT(PTextRenderer != NULL);
		PTextRenderer->Set_Coordinate_Range(Render2DClass::Get_Screen_Resolution());
	}
	renderer_team_heading=L"";
	renderer_is_intermission_true=false;

}

//------------------------------------------------------------------------------------
void cTeamManager::Onetime_Shutdown(void)
{
	if (!ConsoleBox.Is_Exclusive()) {
		WWASSERT(PTextRenderer != NULL);
		delete PTextRenderer;
		PTextRenderer = NULL;

		WWASSERT(PFont != NULL);
		PFont->Release_Ref();
		PFont = NULL;
	}
}

//------------------------------------------------------------------------------------
void cTeamManager::Think(void)
{
	WWPROFILE( "cTeamManager::Think" );

	if (PTextRenderer == NULL) {
		return;
	}
	if (MultiHUDClass::Is_On()) {
		WWASSERT(PTextRenderer != NULL);
		Render_Team_List();
	}
	else {
		// This will make sure the team list disappears if it is not displayed
		if (PTextRenderer) PTextRenderer->Reset();
	}
}

//-----------------------------------------------------------------------------------
void cTeamManager::Render(void)
{
	WWPROFILE("cTeamManager::Render");
	if (PTextRenderer == NULL) {
		return;
	}

	WWASSERT(PTextRenderer != NULL);
	PTextRenderer->Render();
}

//------------------------------------------------------------------------------------
cTeam * cTeamManager::Find_Team(int team_number)
{
   WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);

   SLNode<cTeam> * objnode;

	cTeam *obj;
   for (objnode = TeamList.Head(); objnode; objnode = objnode->Next()) {
		obj = objnode->Data();
      WWASSERT(obj != NULL);
		if (obj->Get_Id() == team_number) {
			return obj; // found it
		}
	}

	return NULL; // Not found
}

cTeam* cTeamManager::Find_Empty_Team(void)
{
	SLNode<cTeam>* node = TeamList.Head();

	while (node) {
		cTeam* team = node->Data();

		if (team && (team->Tally_Size() == 0)) {
			return team;
		}

		node = node->Next();
	}

	return NULL;
}

//------------------------------------------------------------------------------------
void cTeamManager::Add(cTeam * p_team)
{
   WWASSERT(p_team != NULL);
   TeamList.Add_Tail(p_team);
}

//------------------------------------------------------------------------------------
void cTeamManager::Remove(cTeam * p_team)
{
   WWASSERT(p_team != NULL);
   TeamList.Remove(p_team);
   delete p_team;
}

//------------------------------------------------------------------------------------
void cTeamManager::Remove(int team_number)
{
   WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);

   cTeam * p_team = Find_Team(team_number);
   if (p_team != NULL) {
      Remove(p_team);
   }
}

//------------------------------------------------------------------------------------
void cTeamManager::Remove_All(void)
{
	for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL;) {
		cTeam * p_team = objnode->Data();
		objnode = objnode->Next();
		Remove(p_team);
	}
}

//------------------------------------------------------------------------------------
int cTeamManager::Compute_Team_List_Height(void)
{
	/*
	int height = 0;

	if (The_Game()->Is_Team_Game()) {
		WWASSERT(PFont != NULL);
		height = (int)(4 * PFont->Char_Height() * Y_INCREMENT_FACTOR);
	}
	*/
	WWASSERT(PFont != NULL);
	int height = (int)(4 * PFont->Char_Height() * Y_INCREMENT_FACTOR);

	return height;
}

//-----------------------------------------------------------------------------
void cTeamManager::Log_Team_List(void)
{
	WWDEBUG_SAY(("cTeamManager::Log_Team_List\n"));

	StringClass results_filename;
	results_filename.Format("results%d.txt", cUserOptions::ResultsLogNumber.Get());
	FILE * file = ::fopen(results_filename, "wt");

   if (file != NULL) {

		char line[2000] = "";

		WideStringClass wide_text(0, true);
		Construct_Heading(wide_text);//, true);

		StringClass text;
		wide_text.Convert_To(text);

		::sprintf(line, "%s\n", text.Peek_Buffer());
		::fwrite(line, 1, ::strlen(line), file);

		for (int j = 0; j < MAX_TEAMS; j++) {
			cTeam * p_team = Team_Array[j];
			if (p_team != NULL) {
				//p_team->Get_Player_String(j + 1, wide_text, true);
				p_team->Get_Team_String(j + 1, wide_text);
				wide_text.Convert_To(text);
				::sprintf(line, "%s\n", text.Peek_Buffer());
				::fwrite(line, 1, ::strlen(line), file);
			}
		}

		::sprintf(line, "\n");
		::fwrite(line, 1, ::strlen(line), file);

		::fclose(file);
	}
}

//-----------------------------------------------------------------------------
WideStringClass cTeamManager::Get_Team_Name(int team_number)
{
   WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);

   cTeam * p_team = cTeamManager::Find_Team(team_number);
   WWASSERT(p_team != NULL);
   return p_team->Get_Name();
}

//-----------------------------------------------------------------------------
void cTeamManager::Reset_Teams(void)
{
   SLNode<cTeam> * objnode;
	cTeam * p_team = NULL;
   for (objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
		p_team = objnode->Data();
      WWASSERT(p_team != NULL);
      p_team->Reset();
   }
}

//-----------------------------------------------------------------------------
int cTeamManager::Get_Non_Empty_Team_Count(void)
{
   int num_teams = 0;
   cTeam * p_team;
   for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
		p_team = objnode->Data();
      WWASSERT(p_team != NULL);

      if (p_team->Tally_Size() > 0) {
         num_teams++;
      }
   }

   return num_teams;
}

//-----------------------------------------------------------------------------
void cTeamManager::Sort_Teams(void)
{
	WWPROFILE("cTeamManager::Sort_Teams");

   ZeroMemory(Team_Array, sizeof(Team_Array));

   int num_teams = 0;
   cTeam * p_team;
   for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
		p_team = objnode->Data();
      WWASSERT(p_team != NULL);

      WWASSERT(num_teams < MAX_TEAMS);
      Team_Array[num_teams] = p_team;
      num_teams++;
   }

   qsort(&Team_Array[0], num_teams, sizeof(cTeam *), &Team_Compare);
}

//-----------------------------------------------------------------------------
int cTeamManager::Get_Leaders_Id(void)
{
   WWASSERT(Team_Array[0] != NULL);
   return Team_Array[0]->Get_Id();
}

//-----------------------------------------------------------------------------
int cTeamManager::Sort_Score_Ktd_Kills(cTeam * p_team1, cTeam * p_team2)
{
   WWASSERT(p_team1 != NULL);
   WWASSERT(p_team2 != NULL);

   int result;

   if (p_team1->Get_Score() > p_team2->Get_Score()) {
      result = -1;
   } else if (p_team1->Get_Score() == p_team2->Get_Score()) {
      //if (p_team1->Get_Kill_To_Death_Ratio() > p_team2->Get_Kill_To_Death_Ratio()) {
      //  result = -1;
      //} else if (p_team1->Get_Kill_To_Death_Ratio() == p_team2->Get_Kill_To_Death_Ratio()) {
         if (p_team1->Get_Kills() > p_team2->Get_Kills()) {
            result = -1;
         } else if (p_team1->Get_Kills() == p_team2->Get_Kills()) {
            //
            // OK, give up, they are equal
            //
            result = 0;
         } else {
            result = 1;
         }
      //} else {
      //   result = 1;
      //}
   } else {
      result = 1;
   }

   return result;
}

//-----------------------------------------------------------------------------
int cTeamManager::Team_Compare(const void * elem1, const void * elem2)
{
   //
   // Used by qsort
   //

   WWASSERT(elem1 != NULL);
   WWASSERT(elem2 != NULL);

   cTeam * p_team1 = *((cTeam **)elem1);
   cTeam * p_team2 = *((cTeam **)elem2);
   WWASSERT(p_team1 != NULL);
   WWASSERT(p_team2 != NULL);

   //
   // Sort order:
   // 1. If there is a life limit, sort on lives.
   // 2. Sort on score.
   // 3. Sort on KTD ratio.
   // 4. Sort on Kills
   //
	int result = Sort_Score_Ktd_Kills(p_team1, p_team2);

   return result;
}

//-----------------------------------------------------------------------------
void cTeamManager::List_Print(WideStringClass & text, Vector3 color)
{
   //WWASSERT(text != NULL);
   //WWASSERT(::strlen(text) > 0);
	if (PTextRenderer == NULL) {
		return;
	}

	WWASSERT(PTextRenderer != NULL);

	PTextRenderer->Set_Location(Vector2(cMathUtil::Round(XPos), cMathUtil::Round(YPos)));

	int c = ((int)(color[0]*255)&0xFF) << 16 | ((int)(color[1]*255)&0xFF) << 8 | ((int)(color[2]*255)&0xFF) << 0 | 0xFF000000;

	PTextRenderer->Draw_Text(text, c);

	WWASSERT(PFont != NULL);
   YPos += PFont->Char_Height() * Y_INCREMENT_FACTOR;
}

//-----------------------------------------------------------------------------
void cTeamManager::Construct_Heading(WideStringClass & string)
{
	string.Format(L"");

	WWASSERT(PTheGameData != NULL);
	//bool is_verbose = The_Game()->IsIntermission.Get() || MultiHUDClass::Get_Verbose_Lists();
	bool is_verbose = //force_verbose ||
		               The_Game()->IsIntermission.Is_True() ||
							//MultiHUDClass::Get_Verbose_Lists();
							(MultiHUDClass::Get_Playerlist_Format() == PLAYERLIST_FORMAT_FULL);

	WideStringClass substring(0,true);

   //
	// Num Players
	//
	substring.Format(L"%-3s", L"");
	string += substring;

	/*
	//
	// Standing
	//
   if (is_verbose) {
		substring.Format(L"%-5s", L"");
		string += substring;
   }
	*/

	//
	// "Team" or "Clan"
	//
	//GAMESPY
	//substring.Format(L"%-11s", The_Game()->Get_Team_Word());
	if (cGameSpyAdmin::Is_Gamespy_Game()) {
		substring.Format(L"%-36s", The_Game()->Get_Team_Word());
	} else {
		substring.Format(L"%-11s", The_Game()->Get_Team_Word());
	}
	string += substring;

   //
	// Kills
	//
   if (is_verbose) {
		substring.Format(L"%-8s", TRANSLATION(IDS_MP_KILLS));
		string += substring;
   }

   //
	// Deaths
	//
	if (is_verbose) {
		substring.Format(L"%-8s", TRANSLATION(IDS_MP_DEATHS));
		string += substring;
   }

	//
	// Kill to Death ratio
	//
   if (is_verbose) {
		substring.Format(L"%-8s", TRANSLATION(IDS_MP_KILL_TO_DEATH_RATIO));
		string += substring;
   }

	/**/
	//
	// Money
	//
	if ((The_Game()->Is_Cnc() || The_Game()->Is_Skirmish()) && is_verbose) {
		substring.Format(L"%-8s", TRANSLATION(IDS_MP_MONEY));
	   string += substring;
   }
	/**/

	//
	// Score
	//
	substring.Format(L"%-8s", TRANSLATION(IDS_MP_SCORE));
	string += substring;
}

//-----------------------------------------------------------------------------
void cTeamManager::Render_Team_List(void)
{
	if (PTextRenderer == NULL) {
		return;
	}
   WWASSERT(PFont != NULL);
	WWASSERT(PTextRenderer != NULL);

	WWASSERT(PTheGameData != NULL);
	if (GameModeManager::Find("Combat") == NULL ||
		!GameModeManager::Find("Combat")->Is_Active() ||
		The_Game()->IsIntermission.Is_True()) {

		PTextRenderer->Reset();
		return;
	}

   //
   // Format:
   // AAAAAAAAAxxNNNNxxNNNxNNNxNNNxNxxNNNxxNNNNN, e.g:
   // Charlie    5     25  13    1.5   12   1203
   //
   // Fields are name, num players, kills, death, kills-to-deaths, num-lives, score
   //

   /*
	if (!The_Game()->Is_Team_Game()) {
		PTextRenderer->Reset();
      return;
   }
	*/

   /*
	//
   // Count the non-empty teams
   //
   int num_teams = Get_Non_Empty_Team_Count();
   if (num_teams == 0) {
      return;
   }
	*/

	bool changed=false;

   //
   // Build heading
   //
	WideStringClass heading(0,true);
	Construct_Heading(heading);
	if (heading!=renderer_team_heading) {
		renderer_team_heading=heading;
		changed=true;
	}

	// Verify that the team list is valid
   for (int j = 0; j < MAX_TEAMS; j++) {
		if (!Team_Array[j]) {
			Sort_Teams();
			break;
		}
	}
   for (j = 0; j < MAX_TEAMS; j++) {
	   cTeam * p_team = Team_Array[j];
		WWASSERT(p_team != NULL);
		renderer_team_colors[j]=p_team->Get_Color();
		if (changed) {
			p_team->Get_Team_String(j + 1, renderer_team_strings[j]);
		}
		else {
			WideStringClass team_string(0,true);
			p_team->Get_Team_String(j + 1, team_string);
			if (team_string!=renderer_team_strings[j]) {
				changed=true;
				renderer_team_strings[j]=team_string;
			}
		}
   }

	WWASSERT(PTheGameData != NULL);
	if (The_Game()->IsIntermission.Is_True()!=renderer_is_intermission_true) {
		renderer_is_intermission_true=The_Game()->IsIntermission.Is_True();
		changed=true;
	}

	if (!changed) return;

	PTextRenderer->Reset();

   //
   // Determine left edge placement
   //
	XPos = 0;
	float text_len = PFont->String_Width(heading);
	if (The_Game()->IsIntermission.Is_True()) {
		XPos = Render2DClass::Get_Screen_Resolution().Center().X - text_len / 2.0f;
	} else {
		XPos = Render2DClass::Get_Screen_Resolution().Right - 20 - text_len;
	}

	YPos = 10;

	//
	// Show Win text
	//
	WWASSERT(PTheGameData != NULL);
	if (The_Game()->IsIntermission.Is_True()) {

		int combined_height =
			Compute_Team_List_Height() +
			cPlayerManager::Compute_Full_Player_List_Height() +
			2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;

		YPos = (int)(Render2DClass::Get_Screen_Resolution().Height() / 2.0 - combined_height / 2.0);
		if (YPos < 10) {
			YPos = 10;
		}

		WideStringClass text(cGameData::Get_Win_Text(),true);

		float x = Render2DClass::Get_Screen_Resolution().Center().X -
			PFont->String_Width(text) / 2.0f;
		float y = YPos;
		PTextRenderer->Set_Location(Vector2(cMathUtil::Round(x), cMathUtil::Round(y)));
		PTextRenderer->Draw_Text(text);

		YPos += 2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
	}

   //
   // Draw the teamlist heading
   //
   List_Print(heading, Vector3(1, 1, 1));

   for (j = 0; j < MAX_TEAMS; j++) {
		List_Print(renderer_team_strings[j], renderer_team_colors[j]);
   }

}

















