#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "IPCache.h"

#define MX_LOCK_MAP()\
do \
{\
	pthread_mutex_lock(&m_mx_IPPairMap);\
} while( 0 );

#define MX_UNLOCK_MAP()\
do \
{\
	pthread_mutex_unlock(&m_mx_IPPairMap);\
} while( 0 );


// initialize class 
CPrivateIP::CPrivateIP()
	: m_time_lastUpdated ( 0L ) 
{ 
}

CPrivateIP::CPrivateIP(const std::string szPrivateIP, const long time_lastUpdated )
	: m_szPrivateIP ( szPrivateIP ) 
	, m_time_lastUpdated ( time_lastUpdated ) 
{ 
}

// initialize class 
CIPCache::CIPCache()
{ 
	m_time_timeout = 300; // 5min ( default 300 second )
	//m_mx_IPPairMap = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_init( &m_mx_IPPairMap, NULL );
}

CIPCache::~CIPCache()
{ 
	pthread_mutex_destroy(&m_mx_IPPairMap);
}

bool CIPCache::SetTimeout(const long time_timeout)
{
	if ( time_timeout < 0 )
	{ 
		// time is equal or bigger than 0
		return false;
	}
	
	m_time_timeout = time_timeout;
	return true;
}

bool CIPCache::GetTimeout(long& time_timeout) 
{
	time_timeout = m_time_timeout;
	return true;
}

bool CIPCache::GetPrivateIP( const std::string& szPublic, std::string& szPrivate ) 
{
	time_t time_now;
	time_t res = 0;

	// Public ip check
	if (szPublic.empty() == true)
		return false;

	res = time( &time_now );
	if ( res < 0)
	{ 
		return false;
	}

	MX_LOCK_MAP();

	IPPairMap::iterator it;
	it = m_IPPair.find(szPublic);

	if ( it == m_IPPair.end() )  // Not exist...
	{ 
		MX_UNLOCK_MAP();
		return false;
	}
	else if ( time_now - it->second.m_time_lastUpdated > (long)m_time_timeout )  // Cache Time out
	{ 
		m_IPPair.erase(it);
		
		MX_UNLOCK_MAP();
		return false;
	}
	else
	{
		szPrivate = it->second.m_szPrivateIP;
		
		MX_UNLOCK_MAP();
		return true;
	}

}

bool CIPCache::SetPrivateIP( const std::string& szPublic, std::string& szPrivate )
{
	time_t time_now;
	time_t res = 0;

    // check valid ip 
    if( (int)inet_addr(szPrivate.c_str()) == -1 )
    {
        return false;
    }
	
	res = time( &time_now );
	if ( res < 0)
	{
		return false;
	}

	MX_LOCK_MAP();

	m_IPPair[szPublic].m_szPrivateIP = szPrivate;
	m_IPPair[szPublic].m_time_lastUpdated = time_now;

	MX_UNLOCK_MAP();
	return true;
}
