#include "munga.h"
#pragma hdrstop

#include "icom.h"
#include "app.h"

// #define LOCAL_TEST

#if defined(LOCAL_TEST)
#	define Test_Tell(n) Tell(n << flush);
#else
#	define Test_Tell(n)
#endif

//##########################################################################
//#############################    Icom    #################################
//##########################################################################
Icom::Icom(
	HostID host_id,
	int unit_number
):
Plug(IcomClassID)
{
	Test_Tell("Icom::Icom(" << host_id << ", " << unit_number << ")\n");
	Check_Pointer(this);
	//--------------------------------------
	// Initialize values
	//--------------------------------------
	hostID = host_id;
	unitNumber = unit_number;
	channel = undefinedChannel;
	channelBeforePA = undefinedChannel;
	PTTStatus = False;
	PACaptive = False;

	Check_Fpu();
}

Icom::~Icom()
{
	Test_Tell("Icom::~Icom()\n");
	Check(this);
	//--------------------------------------
	// Cleanup before deletion
	//--------------------------------------
	SetPTTStatusValue(False);
	SetChannelValue(undefinedChannel);
	//--------------------------------------
	// The plug automatically unlinks
	// from the Icom Manager upon deletion.
	//--------------------------------------
	Check_Fpu();
}

Logical
	Icom::TestInstance() const
{
	Plug::TestInstance();
	Check_Fpu();
	return True;
}

//--------------------------------------------------------------------------
void
	Icom::SetChannel(int new_channel)
{
	Test_Tell("Icom::SetChannel(" << new_channel << ")\n");
	Check(this);
	//--------------------------------------
	// Broadcast the change
	// (nonexclusive broadcast: we receive
	// and process this msg locally)
	//--------------------------------------
	IcomManager::IMMessage
		message;

	message.BuildChannelMessage(
		IcomManager::ChannelMessageID,
		hostID,
		unitNumber,
		new_channel
	);
	Check(application);
	application->BroadcastEvent(
		DefaultEventPriority,
		NetworkClient::IcomManagerClientID,
		&message
	);

	Check_Fpu();
}

void
	Icom::SetChannelValue(int new_channel)
{
	Test_Tell("Icom::SetChannelValue(" << new_channel << ")\n");
	Check(this);
	//--------------------------------------
	// Set local value
	//--------------------------------------
	channel = new_channel;
	Check_Fpu();
}

int
	Icom::GetChannel()
{
	Check(this);
	Check_Fpu();
	return channel;
}

//--------------------------------------------------------------------------
void
	Icom::SetPTTStatus(Logical new_ptt)
{
	Test_Tell("Icom::SetPTTStatus(" << new_ptt << ")\n");
	Check(this);

	//---------------------------------------
	// Disallow talking during public address
	//---------------------------------------
	if (!PACaptive)
	{
		//--------------------------------------
		// Broadcast change
		// (nonexclusive broadcast: we receive
		// and process this msg locally)
		//--------------------------------------
		IcomManager::IMMessage
			message;

		message.BuildPTTMessage(
			IcomManager::PTTMessageID,
			hostID,
			unitNumber,
			new_ptt
		);
		Check(application);
		application->BroadcastEvent(
			DefaultEventPriority,
			NetworkClient::IcomManagerClientID,
			&message
		);
	}

	Check_Fpu();
}

void
	Icom::SetPTTStatusValue(Logical new_ptt)
{
	Test_Tell("Icom::SetPTTStatusValue(" << new_ptt << ")\n");
	Check(this);
	//---------------------------------------
	// Disallow talking during public address
	//---------------------------------------
	if (!PACaptive)
	{
		PTTStatus = new_ptt;
	}
	Check_Fpu();
}

Logical
	Icom::GetPTTStatus()
{
	Check(this);
	Check_Fpu();
	return PTTStatus;
}

//--------------------------------------------------------------------------
void
	Icom::SetPACaptive(Logical new_pa)
{
	Test_Tell("Icom::SetPACaptive(" << new_pa << ")\n");
	Check(this);

	//--------------------------------------
	// Broadcast the change
	// (nonexclusive broadcast: we receive
	// and process this msg locally)
	//--------------------------------------
	IcomManager::IMMessage
		message;

	message.BuildPAMessage(
		IcomManager::PAMessageID,
		hostID,
		unitNumber,
		new_pa
	);
	Check(application);
	application->BroadcastEvent(
		DefaultEventPriority,
		NetworkClient::IcomManagerClientID,
		&message
	);

	Check_Fpu();
}

void
	Icom::SetPACaptiveValue(Logical new_pa)
{
	Test_Tell("Icom::SetPACaptiveValue(" << new_pa << ")\n");
	Check(this);

	if (new_pa == True)
	{
		if (PACaptive == False)
		{
			channelBeforePA = channel;
		}
		//--------------------------------------
		// Force PTT off during public address
		//--------------------------------------
		if (PTTStatus == True)
		{
			SetPTTStatusValue(False);
		}
	}
	else
	{
		//--------------------------------------
		// Return to previous channel
		//--------------------------------------
		SetChannelValue(channelBeforePA);
	}
	PACaptive = new_pa;
	Check_Fpu();
}

Logical
	Icom::GetPACaptive()
{
	Check(this);
	Check_Fpu();
	return PACaptive;
}

//##########################################################################
//#########################    IcomManager    ##############################
//##########################################################################

//--------------------------------------------------------------------------
// Virtual Data support
//

Derivation* IcomManager::GetClassDerivations()
{
   	static Derivation classDerivations(NetworkClient::GetClassDerivations(), "IcomManager");
	return &classDerivations;
}

IcomManager::SharedData
	IcomManager::DefaultData(
		IcomManager::GetClassDerivations(),
		IcomManager::GetMessageHandlers()
	);

//--------------------------------------------------------------------------
// Creator
//
IcomManager::IcomManager(
	SharedData &shared_data
):
NetworkClient(IcomManagerClassID, shared_data, IcomManagerClientID),
instanceList(this)
{
	Test_Tell("IcomManager::IcomManager()\n");
	Check_Pointer(this);

	//--------------------------------
	// Initialize unit number service
	//--------------------------------
	nextUnitNumber = 0;

	Check_Fpu();
}

//--------------------------------------------------------------------------
// Destructor
//
IcomManager::~IcomManager()
{
	Test_Tell("IcomManager::~IcomManager()\n");
	Check(this);

	Shutdown();

	Check_Fpu();
}

//--------------------------------------------------------------------------
Logical
	IcomManager::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}

//--------------------------------------------------------------------------
void
	IcomManager::LoadMission(Mission */*mission*/)
{
	Check(this);
	Check_Fpu();
}

//--------------------------------------------------------------------------
void
	IcomManager::Shutdown()
{
	Check(this);

	Icom
		*icom;
	ChainIteratorOf<Icom*>
		i(instanceList);

	//------------------------------------------
	// Delete all icom objects
	//------------------------------------------
	while ((icom=i.ReadAndNext()) != NULL)
	{
		Check(icom);
		Unregister_Object(icom);
		delete icom;
	}
	Check_Fpu();
}

//--------------------------------------------------------------------------
void
	IcomManager::Execute()
{
	Check(this);
	Fail("IcomManager::Execute() should not be called directly!\n");
}

//--------------------------------------------------------------------------
Icom *
	IcomManager::MakeIcom(HostID host_id, int unit_number)
{
	Test_Tell(
		"IcomManager::MakeIcom(" << host_id <<
		", " << unit_number <<
		")\n"
	);
	Check(this);

	//------------------------------------------
	// Create, register icom object
	//------------------------------------------
	Icom
		*icom = new Icom(host_id, unit_number);
	Check(icom);
	Register_Object(icom);
	//------------------------------------------
	// Add to global list
	//------------------------------------------
	instanceList.Add(icom);

	Check_Fpu();
	return icom;
}

//------------------------------------------------------------------------
void
	IcomManager::SetPAState(int pa_channel)
{
	Test_Tell("IcomManager::SetPAState(" << pa_channel << ")\n");
	Check(this);

	Icom
		*icom;

	ChainIteratorOf<Icom*>
		i(instanceList);

	if (pa_channel == Icom::undefinedChannel)
	{
		//------------------------------------------
		// Release all icom objects from PA state
		//------------------------------------------
		while ((icom=i.ReadAndNext()) != NULL)
		{
			Check(icom);
			icom->SetPACaptive(False);
		}
	}
	else
	{
		//------------------------------------------
		// Force all icom objects to PA state
		//------------------------------------------
		while ((icom=i.ReadAndNext()) != NULL)
		{
			Check(icom);
			icom->SetPACaptive(True);
			icom->SetChannel(pa_channel);
		}
	}
	Check_Fpu();
}

//------------------------------------------------------------------------
Icom*
	IcomManager::FindIcom(HostID host_id, int unit_number)
{
	Check(this);

	ChainIteratorOf<Icom*>
		i(instanceList);
	Icom
		*icom;

	while ((icom=i.ReadAndNext()) != NULL)
	{
		Check(icom);
		if (icom->hostID == host_id)
		{
			if (icom->unitNumber == unit_number)
			{
				Check_Fpu();
				return icom;
			}
		}
	}

	Check_Fpu();
	return NULL;
}


void
	IcomManager::ReceiveNetworkPacket(
		NetworkPacket */*packet*/,
		Receiver::Message *packet_message
	)
{
	Test_Tell("IcomManager::ReceiveNetworkPacket(...)\n");
	Check(this);
	Check(packet_message);
	Verify(packet_message->messageID >= 0);
	Verify(packet_message->messageID < NextMessageID);

	IMMessage
		*message = (IMMessage *) packet_message;

	Icom
		*icom = FindIcom(message->hostID, message->unitNumber);

	if (icom != NULL)
	{
		Check(icom);
		switch (message->messageID)
		{
		case ChannelMessageID:
			icom->SetChannelValue(message->data.channelNumber);
			break;
		
		case PTTMessageID:
			icom->SetPTTStatusValue(message->data.PTTStatus);
			break;

		case PAMessageID:
			icom->SetPACaptiveValue(message->data.PAStatus);
			break;
		}
	}

	Check_Fpu();
}
