/*
**	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/>.
*/

//
// Filename:     lanchat.cpp
// Project:      Network.lib, for Commando
// Author:       Tom Spencer-Smith
// Date:         Dec 1998
// Description:  lan chat interface
//
// TODO:
// - allow non-unique player names on the LAN
// - no provision for graceless exit of person (must remove if don't hear from them)
// - num players in a channel not shown
//

//-----------------------------------------------------------------------------
#include "lanchat.h"

#pragma warning(disable : 4201)
#include "systimer.h"

#include "wwdebug.h"
#include "_globals.h"
#include "netutil.h"
#include "langmode.h"
#include "cnetwork.h"
#include "gamemode.h"
#include "registry.h"
#include "gamechanlist.h"
#include "playermanager.h"
#include "gametype.h"
#include "translatedb.h"
#include "string_ids.h"
#include "fromaddress.h"
#include "DlgMPConnect.h"
#include "slavemaster.h"
#include "dlgmpchangelannickname.h"
#include "gamespyadmin.h"
#include "demosupport.h"

//
// Class statics
//
const USHORT	cLanChat::LAN_BROADCAST_INTERVAL_MS		= 1000;
#pragma message ("(TSS) BUMP LAN BCAST PORT FOR D1P")
//const USHORT	cLanChat::LAN_PORT							= 0xEA00;
const USHORT	cLanChat::LAN_PORT							= 3373;

//-----------------------------------------------------------------------------
cLanChat::cLanChat(void) :
	Socket(INVALID_SOCKET),
   LastPositionBroadcastTimeMs(0),
	PositionBroadcastNumber(0),
	CurrentLocation(LANLOC_LOBBY)
{
   WWDEBUG_SAY(("cLanChat::cLanChat\n"));

	ZeroMemory(&Socket, sizeof(Socket));
	ZeroMemory(&LocalAddress, sizeof(LocalAddress));

   Load_Lan_Registry_Keys();
   Save_Lan_Registry_Keys();

	Init_Lan_Protocol_And_Socket();

   //cGameChannelList::Remove_All();
}

//-----------------------------------------------------------------------------
cLanChat::~cLanChat(void)
{
   WWDEBUG_SAY(("cLanChat::~cLanChat\n"));

	Save_Lan_Registry_Keys();
	if (Socket != INVALID_SOCKET) {
		cNetUtil::Close_Socket(Socket);
	}
}

//-----------------------------------------------------------------------------
void cLanChat::Load_Lan_Registry_Keys(void)
{
	WWDEBUG_SAY(("cLanChat::Load_Lan_Registry_Keys\n"));

	RegistryClass * registry = new RegistryClass(APPLICATION_SUB_KEY_NAME_NETOPTIONS);
	WWASSERT(registry);
	WWASSERT(registry->Is_Valid());

   char name[200];
	registry->Get_String("MyLanName", name, sizeof(name), "");

	WideStringClass widename;
	widename.Convert_From(name);

	if (!cGameSpyAdmin::Is_Gamespy_Game()) {
		if (widename.Is_Empty())
		{
			cNetInterface::Set_Random_Nickname();
		}
		else
		{
			cNetInterface::Set_Nickname(widename);
		}
	}

	int sidePref = registry->Get_Int("SidePref", -1);
	cNetInterface::Set_Side_Preference(sidePref);

	delete registry;
}

//-----------------------------------------------------------------------------
void cLanChat::Save_Lan_Registry_Keys(void)
{
	WWDEBUG_SAY(("cLanChat::Save_Lan_Registry_Keys...\n"));

	RegistryClass * registry = new RegistryClass(APPLICATION_SUB_KEY_NAME_NETOPTIONS);
	WWASSERT(registry);
	WWASSERT(registry->Is_Valid());

	if (!cGameSpyAdmin::Is_Gamespy_Game()) {
		StringClass string;
		cNetInterface::Get_Nickname().Convert_To(string);
		registry->Set_String("MyLanName", string);
	}

	registry->Set_Int("SidePref", cNetInterface::Get_Side_Preference());

	delete registry;
}

//-----------------------------------------------------------------------------
void cLanChat::Init_Lan_Protocol_And_Socket(void)
{
	WWASSERT(!IS_SOLOPLAY);
   if (cGameSpyAdmin::Is_Gamespy_Game()) {
	   return;
   }

   bool is_internet = false;
   if (!cNetUtil::Protocol_Init(is_internet)) {
      DIE;
   }

	//
	// Init socket
	//
	if (Socket != INVALID_SOCKET) {
		cNetUtil::Close_Socket(Socket);
	}

   bool succeeded = cNetUtil::Create_Bound_Socket(Socket, LAN_PORT, LocalAddress);
	WWASSERT(succeeded);
}

//-----------------------------------------------------------------------------
void cLanChat::Accept_Actions(void)
{
	DlgMPConnect::DoDialog(cNetInterface::Get_Side_Preference(), 0);
}

//-----------------------------------------------------------------------------
void cLanChat::Refusal_Actions(void)
{
   //
   // Must do this if join rejected
   //
}

//-----------------------------------------------------------------------------
void cLanChat::Send_Position_Broadcast(void)
{
	//GAMESPY
	if (cGameSpyAdmin::Is_Gamespy_Game()) {
		return;
	}

   //Debug_Say(("Send_Position_Broadcast\n"));
	if (TIMEGETTIME() - LastPositionBroadcastTimeMs > LAN_BROADCAST_INTERVAL_MS) {

		//
		// Everybody broadcasts their position periodically, as well as
		// immediately after changing location.
		//

		//
		// Broadcast number is used to decide who has to change their name if
		// there is a name collision. The first guy in wins out.
		//
		LastPositionBroadcastTimeMs = TIMEGETTIME();
		PositionBroadcastNumber++;

		cPacket packet;
		packet.Add((BYTE) LAN_MESSAGE_POSITION);
		packet.Add_Wide_Terminated_String(cNetInterface::Get_Nickname());
		packet.Add(PositionBroadcastNumber);
		bool is_hosting = cNetwork::I_Am_Server() &&
         GameModeManager::Find("Combat")->Is_Active();
		packet.Add(is_hosting);

		if (is_hosting) {
			WWASSERT(PTheGameData != NULL);
			packet.Add((int) The_Game()->Get_Game_Type());
			The_Game()->Export_Tier_1_Data(packet);
		}

		cNetUtil::Broadcast(Socket, LAN_PORT, packet);
	}
}

//-----------------------------------------------------------------------------
void cLanChat::Process_Position_Broadcast(cPacket & packet)
{
   //Debug_Say(("Process_Position_Broadcast\n"));

	WideStringClass sender;
	packet.Get_Wide_Terminated_String(sender.Get_Buffer(256), 256);
	int broadcast_number = packet.Get(broadcast_number);
	bool is_hosting = packet.Get(is_hosting);

	//if (sender == cNetInterface::Get_Nickname() &&
	if (!sender.Compare_No_Case(cNetInterface::Get_Nickname()) &&
		broadcast_number > PositionBroadcastNumber) {

      packet.Flush();

		WWDEBUG_SAY(("*** LAN NICKNAME COLLISION (%s) ***\n", sender));

		DlgMpChangeLanNickname::DoDialog();

      return;
   }

   if (is_hosting) {

		int game_type = packet.Get(game_type);
		cGameData * p_game_data = cGameData::Create_Game_Of_Type(
			(cGameData::GameTypeEnum) game_type);
		WWASSERT(p_game_data != NULL);
		p_game_data->Import_Tier_1_Data(packet);

		/*
		//
		// TSS2001f
		// Manually correct hosting ip address according to recv address
		//
		WWASSERT(packet.Get_From_Address_Wrapper() != NULL);
		p_game_data->Set_Ip_Address(packet.Get_From_Address_Wrapper()->FromAddress.sin_addr.s_addr);
		WWDEBUG_SAY(("Manually setting ip to %s\n",
			cNetUtil::Address_To_String(p_game_data->Get_Ip_Address())));
		*/

		//
		//	Don't add this game to the channel list if the player doesn't
		// have the map locally
		//
		if (p_game_data->Does_Map_Exist ()) {
			cGameChannelList::Add_Channel(p_game_data);
		} else {
			delete p_game_data;
			p_game_data = NULL;
		}

	} else {
	   cGameChannelList::Remove_Channel(sender);
      //Debug_Say(("Removing channel for %s\n", sender));
   }
}

//-----------------------------------------------------------------------------
void cLanChat::Lan_Packet_Handler(cPacket & packet)
{
	//GAMESPY
	if (cGameSpyAdmin::Is_Gamespy_Game()) {
		packet.Flush();
		return;
	}

	if (CurrentLocation == LANLOC_EXIT) {
		//
		// Ignore our own broadcasts
		//
		packet.Flush();

	} else {

		BYTE packet_type = packet.Get(packet_type);

		switch (packet_type) {

			case LAN_MESSAGE_POSITION:
				Process_Position_Broadcast(packet);
				break;

			default:
				WWDEBUG_SAY(("cLanChat::Lan_Packet_Handler: received bogus packet_type (%d)\n", packet_type));
				//DIE;
				packet.Flush();
				break;
		}
	}
}

//-----------------------------------------------------------------------------
static void External_Lan_Packet_Handler(cPacket & packet)
{
   //WWASSERT(LanGameModeClass::PLanChat != NULL);
   PLC->Lan_Packet_Handler(packet);
}

//-----------------------------------------------------------------------------
void cLanChat::Go_To_Location(ChatLocationEnum location)
{
	CurrentLocation = location;
}

//-----------------------------------------------------------------------------
void cLanChat::Think(void)
{
	if (cGameSpyAdmin::Is_Gamespy_Game()) {
		return;
	}

   if (CurrentLocation != LANLOC_EXIT) {
		Send_Position_Broadcast();
		cNetUtil::Lan_Servicing(Socket, External_Lan_Packet_Handler);

		DEMO_SECURITY_CHECK;
	}
}
























		/*
		if (MPLanGameListMenuClass::Get_Instance() != NULL) {
			MPLanGameListMenuClass::Get_Instance()->Set_Is_Name_Colliding();
		}
		*/
		//MPLanGameListMenuClass::Set_Is_Name_Colliding();

//#include "dlgmplangamelist.h"
		//cHelpText::Set(TRANSLATION(IDS_MP_NICKNAME_IN_USE));

		//WWDEBUG_SAY(("*** LAN NICKNAME COLLISION (%s) ***\n", sender));
	/*
	if (!cNetInterface::Is_Valid_Name(name)) {
		cNetInterface::Set_Random_Nickname();
		::strcpy(name, cNetInterface::Get_Nickname());
	}
	*/

