
// LuaUDPEndpoint.cpp

// Implements the cLuaUDPEndpoint class representing a Lua wrapper for the cUDPEndpoint class and the callbacks it needs

#include "Globals.h"
#include "LuaUDPEndpoint.h"





cLuaUDPEndpoint::cLuaUDPEndpoint(cPluginLua & a_Plugin, int a_CallbacksTableStackPos):
	m_Plugin(a_Plugin),
	m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos)
{
	// Warn if the callbacks aren't valid:
	if (!m_Callbacks.IsValid())
	{
		LOGWARNING("cLuaUDPEndpoint in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str());
		cPluginLua::cOperation Op(m_Plugin);
		Op().LogStackTrace();
	}
}





cLuaUDPEndpoint::~cLuaUDPEndpoint()
{
	// If the endpoint is still open, close it:
	cUDPEndpointPtr Endpoint = m_Endpoint;
	if (Endpoint != nullptr)
	{
		Endpoint->Close();
	}

	Terminated();
}





bool cLuaUDPEndpoint::Open(UInt16 a_Port, cLuaUDPEndpointPtr a_Self)
{
	ASSERT(m_Self == nullptr);  // Must not be opened yet
	ASSERT(m_Endpoint == nullptr);

	m_Self = a_Self;
	m_Endpoint = cNetwork::CreateUDPEndpoint(a_Port, *this);
	return m_Endpoint->IsOpen();
}





bool cLuaUDPEndpoint::Send(const AString & a_Data, const AString & a_RemotePeer, UInt16 a_RemotePort)
{
	// Safely grab a copy of the endpoint:
	cUDPEndpointPtr Endpoint = m_Endpoint;
	if (Endpoint == nullptr)
	{
		return false;
	}

	// Send the data:
	return Endpoint->Send(a_Data, a_RemotePeer, a_RemotePort);
}





UInt16 cLuaUDPEndpoint::GetPort(void) const
{
	// Safely grab a copy of the endpoint:
	cUDPEndpointPtr Endpoint = m_Endpoint;
	if (Endpoint == nullptr)
	{
		return 0;
	}

	// Get the port:
	return Endpoint->GetPort();
}





bool cLuaUDPEndpoint::IsOpen(void) const
{
	// Safely grab a copy of the endpoint:
	cUDPEndpointPtr Endpoint = m_Endpoint;
	if (Endpoint == nullptr)
	{
		// No endpoint means that we're not open
		return false;
	}

	// Get the state:
	return Endpoint->IsOpen();
}





void cLuaUDPEndpoint::Close(void)
{
	// If the endpoint is still open, close it:
	cUDPEndpointPtr Endpoint = m_Endpoint;
	if (Endpoint != nullptr)
	{
		Endpoint->Close();
		m_Endpoint.reset();
	}

	Terminated();
}





void cLuaUDPEndpoint::EnableBroadcasts(void)
{
	// Safely grab a copy of the endpoint:
	cUDPEndpointPtr Endpoint = m_Endpoint;
	if (Endpoint != nullptr)
	{
		Endpoint->EnableBroadcasts();
	}
}





void cLuaUDPEndpoint::Release(void)
{
	// Close the endpoint, if not already closed:
	Close();

	// Allow self to deallocate:
	m_Self.reset();
}





void cLuaUDPEndpoint::Terminated(void)
{
	// Disable the callbacks:
	if (m_Callbacks.IsValid())
	{
		m_Callbacks.UnRef();
	}

	// If the endpoint is still open, close it:
	{
		cUDPEndpointPtr Endpoint = m_Endpoint;
		if (Endpoint != nullptr)
		{
			Endpoint->Close();
			m_Endpoint.reset();
		}
	}
}





void cLuaUDPEndpoint::OnReceivedData(const char * a_Data, size_t a_NumBytes, const AString & a_RemotePeer, UInt16 a_RemotePort)
{
	// Check if we're still valid:
	if (!m_Callbacks.IsValid())
	{
		return;
	}

	// Call the callback:
	cPluginLua::cOperation Op(m_Plugin);
	if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_NumBytes), a_RemotePeer, a_RemotePort))
	{
		LOGINFO("cUDPEndpoint OnReceivedData callback failed in plugin %s.", m_Plugin.GetName().c_str());
	}
}





void cLuaUDPEndpoint::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
{
	// Check if we're still valid:
	if (!m_Callbacks.IsValid())
	{
		return;
	}

	// Call the callback:
	cPluginLua::cOperation Op(m_Plugin);
	if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), a_ErrorCode, a_ErrorMsg))
	{
		LOGINFO("cUDPEndpoint OnError() callback failed in plugin %s; the endpoint error is %d (%s).",
			m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str()
		);
	}

	Terminated();
}




