#include "GtsSendSocket.h"
#include "Logger.h"
#include "ProcessConfig.h"

#include <string.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <stdio.h>


#define SQLITE_DB_LOCK_TIMEOUT       3000   // 세션 중에 SQLITE_BUSY 상황 발생 문제 해결을 위해 DB Lock 관련 timeout 설정값. ( 3 sec )


CGtsSendSocket::CGtsSendSocket()
	: CBaseSocket( SOCKET_NOT_VALID )
{
	// GTS 서버 정보 설정.
	m_strGtsServer = CProcessConfig::GetInstance()->GetGtsServer();
	m_nGtsPort     = CProcessConfig::GetInstance()->GetGtsPortStat();
	m_strRcId      = CProcessConfig::GetInstance()->GetRcId();

}


CGtsSendSocket::~CGtsSendSocket()
{
	m_vecNotSendService.clear();

	// 소멸자 Socket 명시적 Close 처리.
	Close();
}

// 통계 정보 중 GTS 로 전송하지 않을 서비스 정보를 Local DB 에서 조회하여 저장하기 위한 함수.
bool CGtsSendSocket::InitNotSendService()
{
	// Local DB 관련 변수 초기화.
	sqlite3      * db   = NULL;
	sqlite3_stmt * stmt = NULL;
	int db_result;
	int nRetryCount;

	// SELECT Query..
	const char * select_sql = "SELECT user_seq, svc_seq "
		                      "FROM stat_not_send_service "
							  "ORDER BY user_seq ASC, svc_seq ASC";

	// Data 를 저장할 vector 객체 초기화.
	m_vecNotSendService.clear();

	// Local DB 와 연결을 수행한다.	
	db_result = sqlite3_open( CProcessConfig::GetInstance()->GetLocalDbFileName(), &db );
	if( db_result != SQLITE_OK )
	{
		LOG( LERR, "GtsSendSocket: sqlite3 open failed.[%s][%d][%s]", CProcessConfig::GetInstance()->GetLocalDbFileName(), db_result, sqlite3_errmsg( db ) );
		sqlite3_close(db);
		db = NULL;

		return false;
	}
	else
	{		
		// DB File 이 open 된 경우...
		nRetryCount = 0;

		db_result = sqlite3_prepare(db, select_sql, strlen(select_sql), &stmt, NULL );

		// sqlite3_prepare() 함수 실행시 SQLITE_BUSY 오류가 발생할 수 있으므로.. 재시도 로직을 추가한다.
		while( db_result == SQLITE_BUSY  && nRetryCount < 20 )
		{
			// sqlite 에서 SQLITE_BUSY 반환 관련 lock 대기 timeout 을 설정.
			sqlite3_busy_timeout( db, SQLITE_DB_LOCK_TIMEOUT );
			++nRetryCount;

			LOG( LWAR, "GtsSendSocket: sqlite3 prepare SQLITE_BUSY, Retry[%d]", nRetryCount );

			db_result = sqlite3_prepare(db, select_sql, strlen(select_sql), &stmt, NULL );
		}

		if( db_result != SQLITE_OK )
		{
			LOG( LERR, "GtsSendSocket: sqlite3 prepare failed.[%d][%s]", db_result, sqlite3_errmsg(db));
			sqlite3_close(db);
			db = NULL;
			stmt = NULL;

			return false;
		}
		else
		{
			// select query 수행.
			db_result = sqlite3_step(stmt);
			nRetryCount = 0;

			// 만약 BUSY 상태로 인해 오류가 발생하면..20번 재시도
			while( db_result == SQLITE_BUSY  && nRetryCount < 20 )
			{
				sqlite3_busy_timeout( db, SQLITE_DB_LOCK_TIMEOUT );
				++nRetryCount;

				LOG( LWAR, "GtsSendSocket: sqlite3 step SQLITE_BUSY, Retry[%d]",nRetryCount );

				// query 재시도
				db_result = sqlite3_step(stmt);
			}

			// select query 수행시 오류가 발생한 경우...
			if( db_result != SQLITE_ROW && db_result != SQLITE_DONE )
			{
				LOG( LERR, "GtsSendSocket: sqlite3 step error.[%d][%s]", db_result, sqlite3_errmsg(db));

				// Local DB 오류 발생시.. DB 에 문제가 있는 것으로 보고.. DB 객체를 초기화한다.
				sqlite3_finalize(stmt);
				sqlite3_close(db);

				db = NULL;
				stmt = NULL;

				return false;
			}
			else
			{
				struct gts_not_send_service stService;


				// select Query 가 정상적으로 수행된 경우....
				while( db_result == SQLITE_ROW )
				{
					// 임시 저장변수 초기화.
					memset( &stService, 0x00, sizeof(struct gts_not_send_service) );

					stService.user_seq  = sqlite3_column_int  (stmt, 0);
					stService.svc_seq   = sqlite3_column_int  (stmt, 1);
					
					// vector 객체에 query 결과를 저장한다.
					m_vecNotSendService.push_back( stService );


					// Next 결과 처리..
					db_result = sqlite3_step( stmt );
					nRetryCount = 0;

					// 만약 BUSY 상태로 인해 오류가 발생하면..20번 재시도
					while( db_result == SQLITE_BUSY  && nRetryCount < 20 )
					{
						sqlite3_busy_timeout( db, SQLITE_DB_LOCK_TIMEOUT );
						++nRetryCount;

						LOG( LWAR, "GtsSendSocket: sqlite3 step SQLITE_BUSY, Retry[%d]",nRetryCount );

						// query 재시도
						db_result = sqlite3_step(stmt);
					}

				}

				// Local DB 와의 연결을 종료 처리한다.
				sqlite3_finalize(stmt);
				sqlite3_close(db);
				db = NULL;
				stmt = NULL;
			}
		}
	}

	return true; 
}

// 전달받은 서비스 정보가 GTS 로 통계을 전송하지 않을 서비스 목록에 포함되어 있는지 검사
// true  : GTS 로 통계 전송.
// false : GTS 로 통계 전송하지 않을 서비스임.
bool CGtsSendSocket::IsSendStat(const int & user_seq, const int & svc_seq )
{
	// 비전송 서비스 목록이 존재하지 않을 경우...
	// GTS 로 서비스 정보를 전송하도록 true 반환.
	if( m_vecNotSendService.empty() == true )
		return true;


	std::vector<struct gts_not_send_service>::iterator it;
	for( it = m_vecNotSendService.begin(); it != m_vecNotSendService.end(); ++it )
	{
		if( it->svc_seq == svc_seq && it->user_seq == user_seq )
		{
			_LOG( LINF, "GtsSendSocket: GTS not send service.[%d][%d]", user_seq, svc_seq );
			return false;
		}
	}


	// 비전송 서비스 목록이 존재하지 않을 경우...
	// GTS 로 서비스 정보를 전송하도록 true 반환.
	return true;
}



bool CGtsSendSocket::SendStatNetwork( std::vector<struct gts_send_stat_network> &vecNetworkStat )
{
	// 전달 받은 Vector 를 확인하여... Data 가 없는 경우는 정상 전송한 것으로 처리한다.
	if( vecNetworkStat.empty() == true )
		return true;

	// GTS 로 비전송 서비스 목록을 Local DB 에서 조회하여 생성한다.
	InitNotSendService();


	// GTS cc_statd 로 서비스 Network 정보 전송을 위한 XML 조립 처리...
	//  - XML 라이브러리를 사용하여 조립할 경우.. 라이브러리 포함에 따른 초기화, 링킹 등의 복잡한 작업이 필요함.
	//  - 하지만. 전송 XML 형식은 단순하므로.... 
	//  - XML 라이브러리를 사용하지 않고.. 문자열을 이용하여 직접 조립하여 전송하기로 한다.
	
	//  전송 XML Format
	//  <?xml version="1.0" encoding="UTF-8"?>
	//  <CCSTAT>
	//    <SERVICE RC="bd16" USERSEQ="47" SVCSEQ="346">
	//      <STAT>
	//          <NETWORK TIME="4614930">
	//            <UP_TRAFFIC>0</UP_TRAFFIC><DOWN_TRAFFIC>0</DOWN_TRAFFIC>
	//            <UP_SIZE>0</UP_SIZE><DOWN_SIZE>0</DOWN_SIZE>
	//            <UP_CONCURRENT_SESS>0</UP_CONCURRENT_SESS><DOWN_CONCURRENT_SESS>1</DOWN_CONCURRENT_SESS>
	//          </NETWORK>
	//      </STAT>
	//    </SERVICE>
	//    <SERVICE RC="bd16" USERSEQ="40" SVCSEQ="400">
	//      <STAT>
	//          <NETWORK TIME="4614938">
	//            <UP_TRAFFIC>0</UP_TRAFFIC><DOWN_TRAFFIC>0</DOWN_TRAFFIC>
	//            <UP_SIZE>0</UP_SIZE><DOWN_SIZE>0</DOWN_SIZE>
	//            <UP_CONCURRENT_SESS>100</UP_CONCURRENT_SESS><DOWN_CONCURRENT_SESS>1</DOWN_CONCURRENT_SESS>
	//          </NETWORK>
	//      </STAT>
	//    </SERVICE>
	//  </CCSTAT>


	std::string sendXml;
	char tempBuffer[1024];
	unsigned int sendBodyLength;

	// 실제 GTS 로 전송할 서비스 통계 갯수 정보.
	int nSendServiceCount = 0;


	std::vector<struct gts_send_stat_network>::iterator it;

	// XML Header 부분 조립.
	sendXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><CCSTAT>";
	
	// XML Body 부분 조립	
	for( it = vecNetworkStat.begin(); it != vecNetworkStat.end(); ++it )
	{
		// GTS 로 비전송 서비스인지 확인한다.
		if( IsSendStat ( it->user_seq, it->svc_seq ) == false )
		{
			// GTS 비전송 서비스인 경우...
			// send_flag 를 ignore 로 처리한다.
			it->send_flag = 2;
		}
		else
		{
			// std::to_string 이 현재 gcc 버전에서 지원하지 않아.. sprintf 함수로 사용함.
			// tempStr  = "<SERVICE RC=\"" + m_strRcId + "\" USERSEQ=\"" + it->user_seq + "\" SVCSEQ=\"" + it->svc_seq + "\">";
			// tempStr += "<STAT><NETWORK TIME=\"" + it->timestamp + "\">";
			// tempStr += "<UP_TRAFFIC>" + it->up_traffic  + "</UP_TRAFFIC><DOWN_TRAFFIC>" + it->down_traffic + "</DOWN_TRAFFIC>";
			// tempStr += "<UP_SIZE>" + it->up_content_size + "</UP_SIZE><DOWN_SIZE>" + it->down_content_size + "</DOWN_SIZE>";
			// tempStr += "<UP_CONCURRENT_SESS>" + it->up_max_concurrent_sess + "</UP_CONCURRENT_SESS><DOWN_CONCURRENT_SESS>" + it->down_max_concurrent_sess + "</DOWN_CONCURRENT_SESS>";

			sprintf( tempBuffer, "<SERVICE RC=\"%s\" USERSEQ=\"%d\" SVCSEQ=\"%d\">", m_strRcId.c_str(), it->user_seq, it->svc_seq );
			sendXml.append( tempBuffer );

			sprintf( tempBuffer, "<STAT><NETWORK TIME=\"%u\">", it->timestamp);
			sendXml.append( tempBuffer );

			sprintf( tempBuffer, "<UP_TRAFFIC>%lu</UP_TRAFFIC><DOWN_TRAFFIC>%lu</DOWN_TRAFFIC>", it->up_traffic, it->down_traffic );
			sendXml.append( tempBuffer );

			sprintf( tempBuffer, "<UP_SIZE>%lu</UP_SIZE><DOWN_SIZE>%lu</DOWN_SIZE>", it->up_content_size, it->down_content_size );
			sendXml.append( tempBuffer );

			// 3.5 버전에서는 세션 정보를 수집 처리하지 않음.
			// 하지만.. cc_statd 전송시에는 해당 항목 전달해 줘야 하므로.. 0 으로 설정 처리하여 전송함.
			sprintf( tempBuffer, "<UP_CONCURRENT_SESS>0</UP_CONCURRENT_SESS><DOWN_CONCURRENT_SESS>0</DOWN_CONCURRENT_SESS>");
			sendXml.append( tempBuffer );

			sendXml += "</NETWORK></STAT></SERVICE>";

			++nSendServiceCount;
		}
	}

	// XML Tail 부분 조립
	sendXml += "</CCSTAT>";

	// GTS 로 전송할 서비스 정보가 존재하는 경우...
	if( nSendServiceCount > 0 )
	{

		// GTS 장비와 TCP 연결.
		if( Connect( m_strGtsServer, m_nGtsPort ) == false )
		{
			LOG( LERR, "GtsSendSocket: GTS connect fail. GTS[%s][%d]", m_strGtsServer.c_str(), m_nGtsPort );

			Close();
			return false;
		}

		// 전송 Size 값을 먼저 전송한 후..... ( 4Byte, Network Byte Order )
		sendBodyLength = htonl( sendXml.size() );
		if( WriteN( &sendBodyLength, 4) != 4 )
		{
			LOG( LERR, "GtsSendSocket: XML Network body length[%lu] send fail.", sendXml.size() );
			Close();

			return false;

		}

		if( WriteN( sendXml.c_str(), sendXml.size() ) != (ssize_t)(sendXml.size()) )
		{
			LOG( LERR, "GtsSendSocket: XML Network body send fail. xml size[%lu] xml[%s]", sendXml.size(), sendXml.c_str() );
			Close();

			return false;
		}

		//_LOG( LDBG, "GtsSendSocket: GTS send data [%lu][%s]", sendXml.size(), sendXml.c_str());

		_LOG( LINF, "GtsSendSocket: Network stats send to GTS OK.. XML Size[%lu]", sendXml.size());


		// 정상적으로 전송이 완료되었으며... send_flag 값을 변경한다.
		for( it = vecNetworkStat.begin(); it != vecNetworkStat.end(); ++it )
		{
			if( it->send_flag == 0 )
				it->send_flag = 1;
		}
	
		// 전송 완료 후 Connection 을 종료 처리한다. (GTS cc_statd 와의 통신 규칙)
		Close();
	}
	else
	{
		_LOG( LINF, "GtsSendSocket: Network stat to send GTS is not exist.");

	}

	return true;

}

bool CGtsSendSocket::SendStatStorage( std::vector<struct gts_send_stat_storage> &vecStorageStat )
{
	// 전달 받은 Vector 를 확인하여... Data 가 없는 경우는 정상 전송한 것으로 처리한다.
	if( vecStorageStat.empty() == true )
		return true;

	// GTS 로 비전송 서비스 목록을 Local DB 에서 조회하여 생성한다.
	InitNotSendService();

	// GTS cc_statd 로 서비스 Storage 정보 전송을 위한 XML 조립 처리...
	//  - XML 라이브러리를 사용하여 조립할 경우.. 라이브러리 포함에 따른 초기화, 링킹 등의 복잡한 작업이 필요함.
	//  - 하지만. 전송 XML 형식은 단순하므로.... 
	//  - XML 라이브러리를 사용하지 않고.. 문자열을 이용하여 직접 조립하여 전송하기로 한다.
	
	//  전송 XML Format
	//  <?xml version="1.0" encoding="UTF-8"?>
	//  <CCSTAT>
	//    <SERVICE RC="bd16" USERSEQ="47" SVCSEQ="346">
	//      <STAT>
	//          <STORAGE TIME="4614930">
	//            <STG_SIZE>85899345920</STG_SIZE>
	//            <USED_STG_SIZE>0</USED_STG_SIZE>
	//          </STORAGE>
	//      </STAT>
	//    </SERVICE>
	//    <SERVICE RC="bd16" USERSEQ="40" SVCSEQ="400">
	//      <STAT>
	//          <STORAGE TIME="4614938">
	//            <STG_SIZE>10995116277</STG_SIZE>
	//            <USED_STG_SIZE>2662913716</USED_STG_SIZE>
	//          </STORAGE>
	//      </STAT>
	//    </SERVICE>
	//  </CCSTAT>


	std::string sendXml;
	char tempBuffer[1024];
	unsigned int sendBodyLength;

	// 실제 GTS 로 전송할 서비스 통계 갯수 정보.
	int nSendServiceCount = 0;

	std::vector<struct gts_send_stat_storage>::iterator it;

	// XML Header 부분 조립.
	sendXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><CCSTAT>";
	
	// XML Body 부분 조립	
	for( it = vecStorageStat.begin(); it != vecStorageStat.end(); ++it )
	{
		// GTS 로 비전송 서비스인지 확인한다.
		if( IsSendStat ( it->user_seq, it->svc_seq ) == false )
		{
			// GTS 비전송 서비스인 경우...
			// send_flag 를 ignore 로 처리한다.
			it->send_flag = 2;
		}
		else
		{
			// std::to_string 이 현재 gcc 버전에서 지원하지 않아.. sprintf 함수로 사용함.
			sprintf( tempBuffer, "<SERVICE RC=\"%s\" USERSEQ=\"%d\" SVCSEQ=\"%d\">", m_strRcId.c_str(), it->user_seq, it->svc_seq );
			sendXml.append( tempBuffer );

			sprintf( tempBuffer, "<STAT><STORAGE TIME=\"%u\">", it->timestamp);
			sendXml.append( tempBuffer );

			sprintf( tempBuffer, "<STG_SIZE>%lu</STG_SIZE>", it->quota);
			sendXml.append( tempBuffer );

			sprintf( tempBuffer, "<USED_STG_SIZE>%lu</USED_STG_SIZE>", it->used );
			sendXml.append( tempBuffer );

			sendXml += "</STORAGE></STAT></SERVICE>";

			++nSendServiceCount;

		}
	}

	// XML Tail 부분 조립
	sendXml += "</CCSTAT>";

	// GTS 로 전송할 서비스 정보가 존재하는 경우...
	if( nSendServiceCount > 0 )
	{

		// GTS 장비와 TCP 연결.
		if( Connect( m_strGtsServer, m_nGtsPort ) == false )
		{
			LOG( LERR, "GtsSendSocket: GTS connect fail.GTS[%s][%d]", m_strGtsServer.c_str(), m_nGtsPort );

			Close();
			return false;
		}

		// 전송 Size 값을 먼저 전송한 후..... ( 4Byte, Network Byte Order )
		sendBodyLength = htonl( sendXml.size() );
		if( WriteN( &sendBodyLength, 4) != 4 )
		{
			LOG( LERR, "GtsSendSocket: XML Storage body length[%lu] send fail.", sendXml.size() );
			Close();

			return false;

		}

		if( WriteN( sendXml.c_str(), sendXml.size() ) != (ssize_t)(sendXml.size()) )
		{
			LOG( LERR, "GtsSendSocket: XML Storage body send fail. xml size[%lu], xml[%s]", sendXml.size(), sendXml.c_str() );
			Close();

			return false;
		}

		//_LOG( LDBG, "GtsSendSocket: GTS send data [%lu][%s]", sendXml.size(), sendXml.c_str());

		_LOG( LINF, "GtsSendSocket: Storage stats send to GTS OK.. XML Size[%lu]", sendXml.size());


		// 정상적으로 전송이 완료되었으며... send_flag 값을 변경한다.
		for( it = vecStorageStat.begin(); it != vecStorageStat.end(); ++it )
		{
			if( it->send_flag == 0 )
				it->send_flag = 1;
		}
	
		// 전송 완료 후 Connection 을 종료 처리한다. (GTS cc_statd 와의 통신 규칙)
		Close();
	}
	else
	{
		_LOG( LINF, "GtsSendSocket: Storage stat to send GTS is not exist.");

	}

	return true;

}
