#include "RctsSocket.h"

#include "Logger.h"

#include <string.h>
#include <netinet/in.h>


// 생성자
CRctsSocket::CRctsSocket()
	: CBaseSocket( SOCKET_NOT_VALID )
	, m_iPacketHeaderLen( sizeof( struct PacketHeader ) )
	, m_uPacketDataLen( 0 )
{	
}

// 소멸자
CRctsSocket::~CRctsSocket()
{
	// 명시적인 Close 처리.
	Close();
}


// 객체 초기화
// - false : 전달된 인자값 오류시 
bool CRctsSocket::Init( const char * szServer, unsigned int uPort, const char * szRcId )
{
	// 전달된 변수의 유효성 여부 판단.
	if( szServer == NULL )
	{
		LOG( LERR, "server name or ip is null." );
		return false;
	}

	if( szRcId == NULL )
	{
		LOG( LERR, "rcid is null." );
		return false;
	}

	if( strlen( szRcId ) > RC_ID_MAX_LENGTH )
	{
		LOG( LERR, "rcid[%s] too long. max length [%d]", szRcId, RC_ID_MAX_LENGTH );
		return false;
	}

	// 멤버 변수에 저장 처리.
	m_strServer = szServer;
	m_uPort     = uPort;
	m_strRcId   = szRcId;
	
	return true;
}



// RCTS 로 수집된 시스템 정보를 전송한다.
bool CRctsSocket::SendSystemInfo( const struct SystemInfo * pSystem )
{
	// Socket 이 유효, 연결되어 있는지 확인.
	if( IsValidSocket() == false )
	{
		// RCTS 에 연결되지 않은 경우....
		// - RCTS 와 연결 시도.		
		if( Connect( m_strServer, m_uPort ) == false )
		{
			LOG( LERR, "rcts connect fail. rcts[%s][%u]", m_strServer.c_str(), m_uPort );

			Close();
			return false;
		}
	}

	// 각 Packet 전송 관련 구조체 초기화.
	memset( &m_packetHeader, 0x00, sizeof( struct PacketHeader ) );
	memset( &m_packetDataSystem, 0x00, sizeof( struct PacketDataSystem ) );

	// system 수집 정보 전송을 위한 Packet Data 구조체 설정.
	strcpy( m_packetDataSystem.rcid, m_strRcId.c_str() );


	// 공유메모리 구조체를 그대로 복사해도 되지만...
	// - 향후 항목 추가시 통신 오류 발생 가능.
	// - 이로 인해 protocol version 기능이 추가되어 있음.
	// - 따라서 통신 오류 등을 방지하기 위해 공유메모리, 통신 구조체가 별도 분리되어 있음.
	strcpy( m_packetDataSystem.hostname, pSystem->hostname );

	m_packetDataSystem.load_avg[0] = pSystem->load_avg[0];
	m_packetDataSystem.load_avg[1] = pSystem->load_avg[1];
	m_packetDataSystem.load_avg[2] = pSystem->load_avg[2];

	m_packetDataSystem.memory_total = pSystem->memory_total;
	m_packetDataSystem.memory_free  = pSystem->memory_free;
	m_packetDataSystem.memory_usage = pSystem->memory_usage;
	
	m_packetDataSystem.swap_total = pSystem->swap_total;
	m_packetDataSystem.swap_used  = pSystem->swap_used;
	m_packetDataSystem.swap_usage = pSystem->swap_usage;

	m_packetDataSystem.storage_total     = pSystem->storage_total;
	m_packetDataSystem.storage_avail     = pSystem->storage_avail;
	m_packetDataSystem.storage_max_usage = pSystem->storage_max_usage;

	m_packetDataSystem.disk_max_queue = pSystem->disk_max_queue;
	m_packetDataSystem.disk_max_busy  = pSystem->disk_max_busy;
	
	m_packetDataSystem.network_packet_in   = pSystem->network_packet_in;
	m_packetDataSystem.network_packet_out  = pSystem->network_packet_out;
	m_packetDataSystem.network_traffic_in  = pSystem->network_traffic_in;
	m_packetDataSystem.network_traffic_out = pSystem->network_traffic_out;

	m_packetDataSystem.tcp_total        = pSystem->tcp_total;
	m_packetDataSystem.tcp_80           = pSystem->tcp_80;
	m_packetDataSystem.tcp_80_establish = pSystem->tcp_80_establish;
	
	m_uPacketDataLen = sizeof( m_packetDataSystem );

	// system 수집 정보 전송을 위한 Packet Header 설정.
	m_packetHeader.stx              = HEADER_STX_CODE;
	m_packetHeader.type             = HEADER_TYPE_REQUEST;
	m_packetHeader.command[0]       = COMMAND_TRANSFER_SYSTEM;
	m_packetHeader.data_length      = htonl( m_uPacketDataLen );
	m_packetHeader.protocol_version = PROTOCOL_VERSION_SYSTEM;

	// Packet Header 정보 전송
	if( WriteN( &m_packetHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
	{
		LOG( LERR, "system info send fail to rc_mond. " );

		// send 실패시... socket close 하여 다음 전송시 새로 생성되도록 한다.
		Close();
		return false;
	}
	else
	{
		// Data 부분 전송
		WriteN( &m_packetDataSystem, m_uPacketDataLen );
	}

	_LOG( LINF, "[RCTS send system] body[%u] mem_usage[%d] storage_avail[%ju] traffic_out[%ju] tcp80_est[%d]"
		  , m_uPacketDataLen
		  , m_packetDataSystem.memory_usage
		  , m_packetDataSystem.storage_avail
		  , m_packetDataSystem.network_traffic_out
		  , m_packetDataSystem.tcp_80_establish );

	return true;
}

// RCTS 로 서비스 상태 정보를 전송한다.
bool CRctsSocket::SendServiceStatus( std::string & strHostname, const struct ServiceStatus * pService, int & iStorageMaxUsage )
{
	// Socket 이 유효, 연결되어 있는지 확인.
	if( IsValidSocket() == false )
	{
		// RCTS 에 연결되지 않은 경우....
		// - RCTS 와 연결 시도.		
		if( Connect( m_strServer, m_uPort ) == false )
		{
			LOG( LERR, "rcts connect fail. rcts[%s][%u]", m_strServer.c_str(), m_uPort );

			Close();
			return false;
		}
	}

	// 각 Packet 전송 관련 구조체 초기화.
	memset( &m_packetHeader,      0x00, sizeof( struct PacketHeader ) );	
	memset( &m_packetDataService, 0x00, sizeof( struct PacketDataService ) );

	// 서비스 상태 정보 전송을 위한 Packet Data 구조체 설정.
	strcpy( m_packetDataService.rcid, m_strRcId.c_str() );
	strcpy( m_packetDataService.hostname, strHostname.c_str() );	
	m_packetDataService.service_stat      = pService->stat;
	m_packetDataService.storage_max_usage = iStorageMaxUsage;
	
	m_uPacketDataLen = sizeof( m_packetDataService );

	// 서비스 상태 정보 전송을 위한 Packet Header 설정.
	m_packetHeader.stx              = HEADER_STX_CODE;
	m_packetHeader.type             = HEADER_TYPE_REQUEST;
	m_packetHeader.command[0]       = COMMAND_TRANSFER_SERVICE;	
	m_packetHeader.data_length      = htonl( m_uPacketDataLen );
	m_packetHeader.protocol_version = PROTOCOL_VERSION_SERVICE;

	// Packet Header 정보 전송
	if( WriteN( &m_packetHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
	{
		LOG( LERR, "service status send fail to rc_mond. " );

		// send 실패시... socket close 하여 다음 전송시 새로 생성되도록 한다.
		Close();
		return false;
	}
	else
	{
		// Data 부분 전송
		WriteN( &m_packetDataService, m_uPacketDataLen );
	}

	_LOG( LINF, "[RCTS send service] hostname[%s] service stat[%d] max_usage[%d]"
		   , m_packetDataService.hostname
		   , m_packetDataService.service_stat
		   , m_packetDataService.storage_max_usage );

	return true;
}
