//===========================================================================//
// 
// Purpose: LiveAPI WebSocket implementation
// 
//===========================================================================//
#include "liveapi.h"

#include "DirtySDK/dirtysock.h"
#include "DirtySDK/dirtysock/netconn.h"
#include "DirtySDK/proto/protossl.h"
#include "DirtySDK/proto/protowebsocket.h"

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
static void LiveAPI_ParamsChangedCallback(IConVar* var, const char* pOldValue)
{
	// TODO[ AMOS ]: latch this off to the server frame thread!
	LiveAPISystem()->UpdateParams();
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
static void LiveAPI_AddressChangedCallback(IConVar* var, const char* pOldValue)
{
	// TODO[ AMOS ]: latch this off to the server frame thread!
	LiveAPISystem()->InstallAddressList();
}

//-----------------------------------------------------------------------------
// console variables
//-----------------------------------------------------------------------------
ConVar liveapi_enabled("liveapi_enabled", "1", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Enable LiveAPI functionality");
ConVar liveapi_session_name("liveapi_session_name", "liveapi_session", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "LiveAPI session name to identify this connection");

// WebSocket core
static ConVar liveapi_use_websocket("liveapi_use_websocket", "1", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Use WebSocket to transmit LiveAPI events");
static ConVar liveapi_servers("liveapi_servers", "ws://127.0.0.1:7777", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Comma separated list of addresses to connect to", &LiveAPI_AddressChangedCallback, "ws://domain.suffix:port");

// WebSocket connection base parameters
static ConVar liveapi_retry_count("liveapi_retry_count", "5", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Amount of times to retry connecting before marking the connection as unavailable", &LiveAPI_ParamsChangedCallback);
static ConVar liveapi_retry_time("liveapi_retry_time", "30", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Amount of time between each retry", &LiveAPI_ParamsChangedCallback);

// WebSocket connection context parameters
static ConVar liveapi_timeout("liveapi_timeout", "300", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "WebSocket connection timeout in seconds", &LiveAPI_ParamsChangedCallback);
static ConVar liveapi_keepalive("liveapi_keepalive", "30", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Interval of time to send Pong to any connected server", &LiveAPI_ParamsChangedCallback);
static ConVar liveapi_lax_ssl("liveapi_lax_ssl", "1", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Skip SSL certificate validation for all WSS connections (allows the use of self-signed certificates)", &LiveAPI_ParamsChangedCallback);

//-----------------------------------------------------------------------------
// constructors/destructors
//-----------------------------------------------------------------------------
LiveAPI::LiveAPI()
{
}

//-----------------------------------------------------------------------------
// Initialization of the LiveAPI system
//-----------------------------------------------------------------------------
void LiveAPI::Init()
{
	if (!liveapi_enabled.GetBool())
		return;

	if (liveapi_use_websocket.GetBool())
	{
		const char* initError = nullptr;

		if (!InitWebSocket(initError))
		{
			Error(eDLL_T::RTECH, 0, "LiveAPI: WebSocket initialization failed! [%s]\n", initError);
			return;
		}
	}
}

//-----------------------------------------------------------------------------
// Shutdown of the LiveAPI system
//-----------------------------------------------------------------------------
void LiveAPI::Shutdown()
{
	webSocketSystem.Shutdown();
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void LiveAPI::CreateParams(CWebSocket::ConnParams_s& params)
{
	params.bufSize = LIVE_API_MAX_FRAME_BUFFER_SIZE;

	params.retryTime = liveapi_retry_time.GetFloat();
	params.maxRetries = liveapi_retry_count.GetInt();

	params.timeOut = liveapi_timeout.GetInt();
	params.keepAlive = liveapi_keepalive.GetInt();
	params.laxSSL = liveapi_lax_ssl.GetInt();
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void LiveAPI::UpdateParams()
{
	CWebSocket::ConnParams_s connParams;
	CreateParams(connParams);

	webSocketSystem.UpdateParams(connParams);
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
bool LiveAPI::InitWebSocket(const char*& initError)
{
	CWebSocket::ConnParams_s connParams;
	CreateParams(connParams);

	return webSocketSystem.Init(liveapi_servers.GetString(), connParams, initError);
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void LiveAPI::InstallAddressList()
{
	webSocketSystem.ClearAll();
	webSocketSystem.UpdateAddressList(liveapi_servers.GetString());
}

//-----------------------------------------------------------------------------
// LiveAPI state machine
//-----------------------------------------------------------------------------
void LiveAPI::RunFrame()
{
	if (!IsEnabled())
		return;

	if (liveapi_use_websocket.GetBool())
		webSocketSystem.Update();
}

//-----------------------------------------------------------------------------
// Send an event to all sockets
//-----------------------------------------------------------------------------
void LiveAPI::LogEvent(const char* const dataBuf, const int32_t dataSize)
{
	if (!IsEnabled())
		return;

	if (liveapi_use_websocket.GetBool())
		webSocketSystem.SendData(dataBuf, dataSize);
}

//-----------------------------------------------------------------------------
// Returns whether the system is enabled and able to run
//-----------------------------------------------------------------------------
bool LiveAPI::IsEnabled() const
{
	return liveapi_enabled.GetBool();
}

static LiveAPI s_liveApi;

//-----------------------------------------------------------------------------
// Singleton accessor
//-----------------------------------------------------------------------------
LiveAPI* LiveAPISystem()
{
	return &s_liveApi;
}