#include "LocalIPCache.h"

#include <errno.h>
#include <string.h>
#include <unistd.h>


// IP Info save cache interval (sec)
#define IPCACHE_TERM_SECOND 60 


// Constructor
CLocalIPCache::CLocalIPCache( const string& szNICName )
	  : m_szNICName( szNICName )
{
    m_szLocalIP.clear();    
	pthread_mutex_init(&m_mxLock, NULL);
    
	LOG( LDBG, "Created CLocalIPCache object..");
}

CLocalIPCache::~CLocalIPCache()
{
	// 동작 중인 Thread 정지 처리.
	// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 가 다른 Thread Handle 일 수 있으므로 업무 Flow 수정시 주의할 것
    pthread_cancel(m_threadHandle); 

    pthread_mutex_destroy(&m_mxLock);    
	//LOG( LDBG, "Destroyed CLocalIPCache object..");
}

bool CLocalIPCache::GetLocalIP(string& szLocalIP)
{
	LOG( LDBG, "return IP is [%s]", m_szLocalIP.c_str());
    
    pthread_mutex_lock(&m_mxLock);
    szLocalIP = m_szLocalIP;
    pthread_mutex_unlock(&m_mxLock);
   
    // 유효한 IP인지를 확인 한다.
    if( (int)inet_addr(szLocalIP.c_str()) == -1 )
    {
        LOG( LERR, "[%s] is invalid IP.", szLocalIP.c_str());
        return false;
    }
    return true;
}

bool CLocalIPCache::GetIP(string& szLocalIP)
{
	int fd = 0; // make new file descriptor to open socket
	int nResult = 0;

	struct ifreq interface;
	struct sockaddr_in * pAddr = NULL;

	// NEW 2010-11-30 huibong struct 초기화 코드 추가
	memset( &interface, 0x00, sizeof(struct ifreq) );
    
	fd = socket(AF_INET, SOCK_DGRAM, 0); // use IPv4
	if (fd < 0)
	{
        LOG( LERR, "socket(SOCK_DGRAM) create failed.");	
        return false;
	}

	interface.ifr_addr.sa_family = AF_INET;
	strncpy(interface.ifr_name, m_szNICName.c_str(), IFNAMSIZ-1); 
    
	nResult = ioctl(fd, SIOCGIFADDR, &interface);
	if( nResult < 0 )
	{	
        LOG( LERR, "ioctl error [%s][%d][%s]", m_szNICName.c_str(), errno, strerror(errno));	
		close(fd);
		
		return false;
	}
    
	close(fd);

	// CHG 2012-02-01 huibong
	// inet_ntoa() 함수가 static 메모리 사용으로 인해 Thread-safe 하지 않으므로..
	// inet_ntop() 함수로 교체 처리한다.
	
	//szLocalIP = inet_ntoa(((struct sockaddr_in *)&interfaceReq.ifr_addr)->sin_addr);
	pAddr = (struct sockaddr_in *)&(interface.ifr_addr);
	char tempBuffer[INET_ADDRSTRLEN];
	memset(tempBuffer, 0x00, sizeof(tempBuffer));

	if( inet_ntop( AF_INET, &(pAddr->sin_addr), tempBuffer, sizeof(tempBuffer)) != NULL )
	{
		szLocalIP = tempBuffer;
		return true;
	}
	else
	{
        LOG( LERR, "inet_ntop error.[%d][%s]", errno, strerror(errno));	
		return false;
	}
}

///@brief 이 함수에서 로컬IP 정보를 가져온다.
void CLocalIPCache::Excute()
{
    string szLocalIP;
    
	if ( !GetIP(szLocalIP) )
	{
		LOG( LWAR, "Don't get ip from [%s]NIC. ", m_szNICName.c_str());
	}
    
	pthread_mutex_lock(&m_mxLock);
    m_szLocalIP = szLocalIP;
    pthread_mutex_unlock(&m_mxLock);
}

void* CLocalIPCache::EntryPoint(void* arg)
{
    CLocalIPCache* pObject = reinterpret_cast<CLocalIPCache *>(arg);    
	pthread_detach( pthread_self() );
    
    while(1)
    {
        pthread_testcancel();
        pObject->Excute();
        pthread_testcancel();
        
        sleep(IPCACHE_TERM_SECOND);        
    }
	
	return NULL;
}

bool CLocalIPCache::Start()
{
    if( m_szNICName.empty() )
    {
		LOG( LERR, "Thread create failed: NIC name is empty.");
		return false;
    }
	int nRet = ::pthread_create(&m_threadHandle, 0, CLocalIPCache::EntryPoint, this);

	if( nRet )
	{
		LOG( LERR, "Thread create failed: errno:%d errmsg:%s", errno, strerror(errno));
		return false;
	}
    LOG( LDBG, "Thread create succeed");
	sleep(0);
	return true;
}
