#include "FhsShm.h"

#include "Logger.h"

#include <string.h>
#include <math.h>


// ShmDefine.h 에서 정의된 서비스 상태 코드 외에 미정의된 코드 처리를 위한 정의
#define SERVICE_STAT_NOT_DEFINE     -1


// 생성자
CFhsShm::CFhsShm( struct FhsInfo * p )
	: m_pShm( p )
{
	// 서비스 상태 code 에 대한 문자열 정보를 저장하기 위한 멤버 변수 설정.
	// - may 객체이므로.. key - value 형식으로 저장 처리
	m_mapServiceStatString[SERVICE_STAT_NOT_DEFINE] = "Not_Define";
	m_mapServiceStatString[SERVICE_STAT_OK]         = "OK";
	m_mapServiceStatString[SERVICE_STAT_OUT]        = "Out";
	m_mapServiceStatString[SERVICE_STAT_DISABLE]    = "Disable";
	m_mapServiceStatString[SERVICE_STAT_RONLY]      = "Read-only";
	
}

// 소멸자
CFhsShm::~CFhsShm()
{

}

// 멤버 변수인 공유메모리에 대한 포인터가 유효하지 여부를 판단하기 위한 함수.
bool CFhsShm::IsValid() const
{
	if( m_pShm != NULL )
		return true;
	else
		return false;
}

// 공유메모리 전체 영역을 초기화하기 위한 함수.
void CFhsShm::ClearAll() const
{
	if( m_pShm != NULL )
	{
		memset( m_pShm, 0x00, sizeof( struct FhsInfo ) );
	}
}

// 공유메모리 정보를 로그 파일에 기록 처리.
void CFhsShm::Print()
{
	_LOG( LINF, "-- shared memory --" );	
	_LOG( LINF, "hostname      : %s", m_pShm->system.hostname );	

	_LOG( LINF, "load_avg      : %5.2lf, %5.2lf, %5.2lf", m_pShm->system.load_avg[0], m_pShm->system.load_avg[1], m_pShm->system.load_avg[2] );
	_LOG( LINF, "memory usage  : %3d %%, free %13ju byte, total %13ju byte", m_pShm->system.memory_usage, m_pShm->system.memory_free, m_pShm->system.memory_total );
	_LOG( LINF, "swap usage    : %3d %%, used %13ju byte, total %13ju byte", m_pShm->system.swap_usage, m_pShm->system.swap_used, m_pShm->system.swap_total );
	_LOG( LINF, "disk max busy : %.1lf %%, max queue %ju", m_pShm->system.disk_max_busy, m_pShm->system.disk_max_queue );	
	_LOG( LINF, "storage usage : max %d %%, avg %d %%, avail %ju byte, total %ju byte"
								, m_pShm->system.storage_max_usage
								, ( int ) ceil( (( double ) (m_pShm->system.storage_total - m_pShm->system.storage_avail )) / m_pShm->system.storage_total * 100.0 )
								, m_pShm->system.storage_avail, m_pShm->system.storage_total );	
	_LOG( LINF, "tcp session   : %u, %u, %u  (80_est, 80_total, total)", m_pShm->system.tcp_80_establish, m_pShm->system.tcp_80, m_pShm->system.tcp_total );
	_LOG( LINF, "traffic in    : %10ju byte/sec, packet %8ju count/sec", m_pShm->system.network_traffic_in, m_pShm->system.network_packet_in );
	_LOG( LINF, "traffic out   : %10ju byte/sec, packet %8ju count/sec", m_pShm->system.network_traffic_out, m_pShm->system.network_packet_out );
	_LOG( LINF, "check time" );	
	_LOG( LINF, "   last service check start : %ld", m_pShm->time_service_check_start_sec );
	_LOG( LINF, "   last system  check start : %ld", m_pShm->time_system_check_start_sec );
	_LOG( LINF, "   last storage check ok    : %ld", m_pShm->time_storage_check_ok_sec );
	
	_LOG( LINF, "service stat  : %s ( %d )", GetServiceStatString(), m_pShm->service.stat );
	_LOG( LINF, "--------------" );

	// 2018-02-08 NEW huibong 수집된 공유메모리 정보에 대한 시간별 분석이 용이하도록 해당 정보를 RAW 형식으로 로깅한다. (#31701)
	_LOG( LINF, "RAW %s %.2lf %.2lf %.2lf"
				" %d %ju %d %ju"
				" %.1lf %ju %d %ju"
		        " %u %u %u %ju %ju"
				" %ld %ld %ld"
		  , GetServiceStatString(), m_pShm->system.load_avg[0], m_pShm->system.load_avg[1], m_pShm->system.load_avg[2]
		  , m_pShm->system.memory_usage, m_pShm->system.memory_free, m_pShm->system.swap_usage, m_pShm->system.swap_used
		  , m_pShm->system.disk_max_busy, m_pShm->system.disk_max_queue, m_pShm->system.storage_max_usage, m_pShm->system.storage_avail
		  , m_pShm->system.tcp_80_establish, m_pShm->system.tcp_80, m_pShm->system.tcp_total, m_pShm->system.network_traffic_in, m_pShm->system.network_traffic_out
		  , m_pShm->time_service_check_start_sec, m_pShm->time_system_check_start_sec, m_pShm->time_storage_check_ok_sec 
	);
	
	_LOG( LINF, "--------------" );

	return;
}


// hostname
bool CFhsShm::SetHostname( const char * name ) const
{
	// 전달받은 hostname 정보를 저장 처리한다.
	// - hostname 정보는 규칙상 HOST_NAME_MAX_LENGTH 값만큼 저장 가능하므로...
	// - HOST_NAME_MAX_LENGTH 길이 초과시... 저장 실패로 false 를 반환 처리한다.
	if( name == NULL )
	{
		LOG( LERR, "[FhsShm] hostname is null point.");
		return false;
	}
	if( strlen( name ) > HOST_NAME_MAX_LENGTH )
	{
		LOG( LERR, "[FhsShm] hostname[%s] too long. max length [%d]", name, HOST_NAME_MAX_LENGTH );
		return false;
	}

	// hostname 이 중간에 변경될 경우.. 서비스에 영향을 미치게 되므로....
	// 변경 여부를 로깅한다.
	if( strlen( m_pShm->system.hostname ) != 0 )
	{
		if( strcmp( m_pShm->system.hostname, name ) != 0 )
		{
			_LOG( LWAR, "hostname changed.[%s] -> [%s]", m_pShm->system.hostname, name );
			strcpy( m_pShm->system.hostname, name );
		}
	}
	else
	{
		strcpy( m_pShm->system.hostname, name );
	}
		
	return true;
}

// loadaverage
bool CFhsShm::SetLoadaverage( const int & index, const double & value ) const
{
	if( index > 2 )
	{
		LOG( LERR, "[FhsShm] loadaverage index[%d] not valid.", index );
		return false;
	}

	m_pShm->system.load_avg[index] = value;
	return true;
}

// memory total
void CFhsShm::SetMemoryTotal( const uint64_t & value ) const
{
	m_pShm->system.memory_total = value;
	return;
}

// memory free
void CFhsShm::SetMemoryFree( const uint64_t & value ) const
{
	m_pShm->system.memory_free = value;

	if( m_pShm->system.memory_total == 0 )
		m_pShm->system.memory_usage = 0;
	else
		m_pShm->system.memory_usage = (int)ceil( (double)( m_pShm->system.memory_total - value ) * 100.0 / m_pShm->system.memory_total );
	
	return;
}

// swap total, used
// - 사용률은 자체적으로 계산하여 처리한다.

void CFhsShm::SetSwapInfo( const uint64_t & total, const uint64_t & used ) const
{
	m_pShm->system.swap_total = total;
	m_pShm->system.swap_used  = used;

	if( total == 0 )
		m_pShm->system.swap_usage = 0;
	else
		m_pShm->system.swap_usage = (int)ceil( (double)(used ) * 100.0 / total );

	return;
}

// storage 용량, 사용률 
void CFhsShm::SetStorageInfo( const uint64_t & total, const uint64_t & avail, const int & max_usage ) const
{
	m_pShm->system.storage_total     = total;
	m_pShm->system.storage_avail     = avail;
	m_pShm->system.storage_max_usage = max_usage;

	return;
}

// Disk max queuelength, max busy.
void CFhsShm::SetDiskInfo( const uint64_t & queue, const double & busy ) const
{
	m_pShm->system.disk_max_queue = queue;
	m_pShm->system.disk_max_busy  = busy;

	return;
}

// Network packet, traffic
void CFhsShm::SetNetworkInfo( const uint64_t & packetIn, const uint64_t & packetOut, const uint64_t & trafficIn, const uint64_t & trafficOut ) const
{
	m_pShm->system.network_packet_in   = packetIn;
	m_pShm->system.network_packet_out  = packetOut;
	m_pShm->system.network_traffic_in  = trafficIn;
	m_pShm->system.network_traffic_out = trafficOut;

	return;
}

// TCP 세션 정보
void CFhsShm::SetTcpInfo( const unsigned int & total, const unsigned int & tcp80, const unsigned int & tcp80Establish ) const
{
	m_pShm->system.tcp_total        = total;
	m_pShm->system.tcp_80           = tcp80;
	m_pShm->system.tcp_80_establish = tcp80Establish;

	return;
}


// Service 상태 정보 설정.
bool CFhsShm::SetServiceStat( const int & stat )
{
	if( stat < SERVICE_STAT_OK || stat > SERVICE_STAT_MAX )
	{
		LOG( LERR, "[FhsShm] service stat code[%d] not vaild. normal range: %d~%d", stat, SERVICE_STAT_OK, SERVICE_STAT_MAX );
		return false;
	}
	else
	{
		// Service Stat 정보가 변경될 경우.. 이를 로깅 처리한다.
		if( m_pShm->service.stat != stat )
		{
			// 상태 변경 처리.
			int iOldStat = m_pShm->service.stat;
			m_pShm->service.stat = stat;
			
			_LOG( LINF, "[FhsShm] service stat change : %d (%s) -> %d (%s)"
				  , iOldStat, GetServiceStatString(iOldStat)
				  , m_pShm->service.stat, GetServiceStatString() );
		}
	}
	
	return true;
}


// Service 상태 정보에 대한 문자열 반환.
const char * CFhsShm::GetServiceStatString( const int & stat )
{
	std::map< int, std::string >::iterator pos;

	pos = m_mapServiceStatString.find( stat );	
	if( pos != m_mapServiceStatString.end() )
	{
		// 해당 항목 존재시
		return pos->second.c_str();
	}
	else
	{
		// 해당 항목이 존재하지 않는 경우.
		return m_mapServiceStatString[SERVICE_STAT_NOT_DEFINE].c_str();
	}
}

// 현재 Service 상태 정보에 대한 문자열 반환.
const char * CFhsShm::GetServiceStatString()
{
	std::map< int, std::string >::iterator pos;

	pos = m_mapServiceStatString.find( m_pShm->service.stat );
	if( pos != m_mapServiceStatString.end() )
	{
		// 해당 항목 존재시
		return pos->second.c_str();
	}
	else
	{
		// 해당 항목이 존재하지 않는 경우.
		return m_mapServiceStatString[SERVICE_STAT_NOT_DEFINE].c_str();
	}
}

// Storage last check 시간 정보 업데이트
// 전달받은 인자값이 true 인 경우 시간 정보 설정, false 인 경우 -1 설정 처리함.
void CFhsShm::UpdateTimeStorageCheck( bool bCheckSuccess )
{
	if( bCheckSuccess == true )
	{
		// storage check 성공시
		// - 현재 시스템 기준 시간 정보 중 sec 정보를 계산하여 공유메모리에 저장 처리.
		struct timespec currentClock;
		memset( &currentClock, 0x00, sizeof( struct timespec ) );
		clock_gettime( CLOCK_MONOTONIC, &currentClock );

		m_pShm->time_storage_check_ok_sec = currentClock.tv_sec;
	}
	else
	{
		// storage check 실패시
		// time_storage_check_ok_sec 값의 정의 규약에 따라 -1 설정

		// TODO: 우선 -1 로 설정하면 바로 Service Disable 처리 할 수 있지만...
		// Disk write/read 관련 일시적인 이유로 실패할 수 있어 
		// 오류 상황에 대한 검증 없이 바로 서비스 disable 처리하면 오탐등으로 서비스에 문제가 발생할 수 있으므로... 
		// 우선은 -1 설정을 하지 않고.. expire 처리되도록 한다.
		// -1 설정 활성화는 장애시 여러 data 를 분석하여.. write/read 오류에 대한 검증 후 진행한다.
		//m_pShm->time_storage_check_ok_sec = -1;
	}

	return;
}

// Service 상태 Check Start 시간 정보 업데이트
void CFhsShm::UpdateTimeServiceCheck()
{
	// 현재 시스템 기준 시간 정보 중 sec 정보를 계산하여 공유메모리에 저장 처리.
	struct timespec currentClock;
	memset( &currentClock, 0x00, sizeof( struct timespec ) );
	clock_gettime( CLOCK_MONOTONIC, &currentClock );

	m_pShm->time_service_check_start_sec = currentClock.tv_sec;

	return;
}

// System 상태 Check Start 시간 정보 업데이트
void CFhsShm::UpdateTimeSystemCheck()
{
	// 현재 시스템 기준 시간 정보 중 sec 정보를 계산하여 공유메모리에 저장 처리.
	struct timespec currentClock;
	memset( &currentClock, 0x00, sizeof( struct timespec ) );
	clock_gettime( CLOCK_MONOTONIC, &currentClock );

	m_pShm->time_system_check_start_sec = currentClock.tv_sec;

	return;
}

