// MetricsGatheringConnection.cpp
// Copyright 2000-2002 Sony Online Entertainment


#include "FirstMetricsServer.h"
#include "MetricsGatheringConnection.h"

#include "ConfigMetricsServer.h"
#include "MetricsServer.h"
#include "MonAPI2/MonitorAPI.h"
#include "serverNetworkMessages/MetricsInitiationMessage.h"

#include "sharedFoundation/CrcConstexpr.hpp"

#include <algorithm>

//-----------------------------------------------------------------------

std::map<std::string, int> MetricsGatheringConnection::ms_metricsSummaryTotalVal;

namespace MetricsGatheringConnectionNamespace
{
	int s_masterChannelIndex = 1;
	std::map<std::string, int> s_masterGraphMap;
};

using namespace MetricsGatheringConnectionNamespace;

//-----------------------------------------------------------------------

MetricsGatheringConnection::MetricsGatheringConnection(UdpConnectionMT * u, TcpClient * t) :
		ServerConnection(u, t),
		m_initialized(false),
		m_label(),
		m_processIndex(0),
		m_processLabel(),
		m_metricsChannels()
{
	m_label.clear();
	m_processLabel.clear();
}

//-----------------------------------------------------------------------

MetricsGatheringConnection::~MetricsGatheringConnection()
{

}

//-----------------------------------------------------------------------



void MetricsGatheringConnection::onConnectionClosed()
{
	m_initialized = false;
	DEBUG_REPORT_LOG(true, ("Metrics Connection with Server %s closed\n", m_processLabel.c_str()));
	CMonitorAPI * mon = MetricsServer::getMonitor();
	NOT_NULL(mon);

	if (m_processLabel == "CentralServer")
	{
		mon->set(MetricsServer::getWorldCountChannel(), STATUS_DOWN);		
	}

	remove();
}

//-----------------------------------------------------------------------

void MetricsGatheringConnection::onConnectionOpened()
{
	DEBUG_REPORT_LOG(true, ("Metrics Connection with Server opened\n"));
}

//-----------------------------------------------------------------------

void MetricsGatheringConnection::onReceive(const Archive::ByteStream & message)
{
	Archive::ReadIterator ri = message.begin();
	GameNetworkMessage m(ri);
	ri = message.begin(); 

	const uint32 messageType = m.getType();
	
	switch(messageType) {
		case constcrc("ConnectionServerMetricsMessage") :
		{
			DEBUG_FATAL(true, ("This message has been depricated"));
			break;
		}
		case constcrc("MetricsInitiationMessage") :
		{
			WARNING_STRICT_FATAL(m_initialized, ("Received initialize metrics on initialized connection"));
			//Get process name and secondary field (i.e. planet)
			MetricsInitiationMessage mx(ri);
			bool dynamic = mx.getIsDynamic();
			std::string process = mx.getPrimaryName();
			std::string planet = mx.getSecondaryName();
			int index = mx.getIndex();
			initialize(process, planet, dynamic, index);
			break;
		}
		case constcrc("MetricsDataMessage") :
		{
			if (!m_initialized)
			{
				//Received data on uninitialized connection.
				return;
			}
			MetricsDataMessage metricsData(ri);
			update(metricsData.getData());
			break;
		}
		default :
		{
			ServerConnection::onReceive(message);
			break;
		}
	}
}

//-----------------------------------------------------------------------

void MetricsGatheringConnection::initialize(const std::string & process, const std::string & planet, bool numberedProcess, int index)
{
	const std::string dot = ".";
	m_label = process;
	m_label = m_label + dot + planet;

	m_processLabel = m_label;
	if (numberedProcess)
	{		
		m_processIndex = index;
		char tmpBuf[100];
		m_label = m_label + dot + _itoa(m_processIndex, tmpBuf, 10);
		
	}
	m_label = m_label + dot;
	m_metricsChannels.clear();
	m_initialized = true;
}

//-----------------------------------------------------------------------

void MetricsGatheringConnection::update(const std::vector<MetricsPair> & data)
{
	CMonitorAPI * mon = MetricsServer::getMonitor();
	NOT_NULL(mon);
	
	std::vector<MetricsPair>::const_iterator dataIter = data.begin();
	for(; dataIter != data.end(); ++dataIter)
	{
		std::map<std::string, std::pair<std::string, int> >::iterator i = m_metricsChannels.find((*dataIter).m_label);
		if (i == m_metricsChannels.end())
		{
			//If this is the first update for a metrics label, we need to assign it a channel number.
			//Then add it to the montior
			int newChannel = 0;
			std::string graphName = m_label + ((*dataIter).m_label);
			if ( dataIter->m_summary )
				graphName = ((*dataIter).m_label);
			std::map<std::string, int>::iterator graphIter = s_masterGraphMap.find(graphName);
			if (graphIter != s_masterGraphMap.end())
			{
				newChannel = graphIter->second; 
			}
			else
			{
				newChannel = ++s_masterChannelIndex;
				s_masterGraphMap[graphName] = newChannel;
				int ping = MON_HISTORY;
				if(! dataIter->m_persistData)
					ping = MON_ONLY_SHOW;
				mon->add(const_cast<char*>(graphName.c_str()), newChannel, ping);
			}
			m_metricsChannels[(*dataIter).m_label] = std::make_pair(graphName, newChannel);
			mon->set(newChannel, (*dataIter).m_value);

			//note: we know that the "isSecret", and "isLocked" nodes will come after the "population" node in the list 
			//		we are iterating over, so they will override the population setting
			if (strstr((*dataIter).m_label.c_str(), "population") != nullptr)
			{
				// there are nodes under the population node, so don't interpret them as the population
				if (strstr((*dataIter).m_label.c_str(), "population.") == nullptr)
					mon->set(MetricsServer::getWorldCountChannel(), std::max((*dataIter).m_value, 0));
			}
			else if (strstr((*dataIter).m_label.c_str(), "isSecret") != nullptr
				|| strstr((*dataIter).m_label.c_str(), "isLocked") != nullptr)
			{
				if ((*dataIter).m_value == 1)
					mon->set(MetricsServer::getWorldCountChannel(), STATUS_LOCKED);
			}
			else if (strstr((*dataIter).m_label.c_str(), "isLoading") != nullptr)
			{
				// include in the root node description how
				// long the cluster has been loading
				if ((*dataIter).m_value > 0)
				{
					char buffer[128];
					snprintf(buffer, sizeof(buffer)-1, "(isLoading: %d seconds) ", (*dataIter).m_value);
					buffer[sizeof(buffer)-1] = '\0';

					std::string description = buffer;
					description += MetricsServer::getWorldCountChannelDescription();
					mon->setDescription(MetricsServer::getWorldCountChannel(), description.c_str());
				}
				else
				{
					mon->setDescription(MetricsServer::getWorldCountChannel(), MetricsServer::getWorldCountChannelDescription().c_str());
				}
			}

			mon->setDescription(newChannel, (*dataIter).m_description.c_str());			
		}
		else
		{
			if ( dataIter->m_summary )
			{
				int val = (*dataIter).m_value;
				if ( val < 0 )
					val = 0;
				int i_delta = val - m_metricsSummaryMyVal[ (*dataIter).m_label ];
				ms_metricsSummaryTotalVal[ (*dataIter).m_label ] += i_delta;
				m_metricsSummaryMyVal[ (*dataIter).m_label ] = val;

				val = ms_metricsSummaryTotalVal[ (*dataIter).m_label ];   // Don't go below 0 (shouldn't happen)
				if ( val < 0 )
					val = 0;
				mon->set(i->second.second, val );
			}
			else
			{
				mon->set(i->second.second, (*dataIter).m_value);
			}
			
			//note: we know that the "isSecret", and "isLocked" nodes will come after the "population" node in the list 
			//		we are iterating over, so they will override the population setting
			if (strstr((*dataIter).m_label.c_str(), "population") != nullptr)
			{
				// there are nodes under the population node, so don't interpret them as the population
				if (strstr((*dataIter).m_label.c_str(), "population.") == nullptr)
					mon->set(MetricsServer::getWorldCountChannel(), std::max((*dataIter).m_value, 0));
			}
			else if (strstr((*dataIter).m_label.c_str(), "isSecret") != nullptr
				|| strstr((*dataIter).m_label.c_str(), "isLocked") != nullptr)
			{
				if ((*dataIter).m_value == 1)
					mon->set(MetricsServer::getWorldCountChannel(), STATUS_LOCKED);
			}
			else if (strstr((*dataIter).m_label.c_str(), "isLoading") != nullptr)
			{
				// include in the root node description how
				// long the cluster has been loading
				if ((*dataIter).m_value > 0)
				{
					char buffer[128];
					snprintf(buffer, sizeof(buffer)-1, "(isLoading: %d seconds) ", (*dataIter).m_value);
					buffer[sizeof(buffer)-1] = '\0';

					std::string description = buffer;
					description += MetricsServer::getWorldCountChannelDescription();
					mon->setDescription(MetricsServer::getWorldCountChannel(), description.c_str());
				}
				else
				{
					mon->setDescription(MetricsServer::getWorldCountChannel(), MetricsServer::getWorldCountChannelDescription().c_str());
				}
			}

			mon->setDescription(i->second.second, (*dataIter).m_description.c_str());									
		}
	}
}


//-----------------------------------------------------------------------

void MetricsGatheringConnection::remove()
{
	CMonitorAPI * mon = MetricsServer::getMonitor();
	NOT_NULL(mon);
	std::map<std::string, std::pair<std::string, int> >::iterator i = m_metricsChannels.begin();
	for (; i != m_metricsChannels.end(); ++i)
	{
		if ( ms_metricsSummaryTotalVal.count( i->first ) )   // summary of multiple servers data
		{
			// This is a summary entry, we can't remove it, just factor our info out of the total
			ms_metricsSummaryTotalVal[ i->first ] -= m_metricsSummaryMyVal[ i->first ];
			m_metricsSummaryMyVal[ i->first ] = 0;
			mon->set(i->second.second, ms_metricsSummaryTotalVal[ i->first ] );
		}
		else if (m_processIndex)
		{
			mon->set(i->second.second, STATUS_LOADING);
		}
		else
		{
			mon->remove(i->second.second);
			IGNORE_RETURN(s_masterGraphMap.erase(i->second.first));
		}
	}
	
	if(! m_processIndex)
		m_metricsChannels.clear();	
}

//-----------------------------------------------------------------------
