#include "CacheJobList.h"

#include <stdio.h>

#include "Logger.h"


// 생성자
CCacheJobList::CCacheJobList()
{
	pthread_mutex_init( &m_mutexMap, NULL );
}

// 소멸자
CCacheJobList::~CCacheJobList()
{
	pthread_mutex_destroy( &m_mutexMap );
}


// 등록된 job 의 갯수 반환.
unsigned int CCacheJobList::GetSize()
{
	return m_mapJob.size();
}


// 해당 Content 에 대한 Job 이 존재하는지 여부 판단.
// true  : 이미 존재
// false : 존재 안하거나.. 인자값 오류
bool CCacheJobList::IsExist( CCacheRequestData & data )
{
	// 전달받은 정보를 이용하여 map 조회를 위한 key 값 생성
	std::string strKey;
	if( GetKey( data, strKey ) == false )
	{
		// Key 생성 실패시
		return false;
	}

	// 생성된 Key 를 기준으로 존재 여부 확인
	bool bResult;
	std::map<std::string, CCacheData>::const_iterator pos;

	// lock 처리 후 조회
	pthread_mutex_lock( &m_mutexMap );

	pos = m_mapJob.find( strKey );
	if( pos != m_mapJob.end() )
	{
		// 해당 Key 값이 존재하면...
		bResult = true;
	}
	else
	{
		// 해당 Key 값이 존재하지 않는 경우.
		bResult = false;
	}

	// lock 해제
	pthread_mutex_unlock( &m_mutexMap );

	return bResult;
}

// 해당 Content 에 대해 등록된 job 삭제
void CCacheJobList::UnRegister( CCacheData & data )
{
	// 전달받은 정보를 이용하여 map 조회를 위한 key 값 생성
	std::string strKey;
	if( GetKey( data.m_requestData, strKey ) == false )
	{
		// Key 생성 실패시
		return;
	}

	// 생성된 Key 를 기준으로 존재 여부 확인	
	std::map<std::string, CCacheData>::iterator pos;

	// lock 처리 후 조회
	pthread_mutex_lock( &m_mutexMap );

	pos = m_mapJob.find( strKey );
	if( pos != m_mapJob.end() )
	{
		// 해당 Key 값이 존재하면...erase 를 이용하여 삭제 처리
		m_mapJob.erase( pos );		
	}
	
	// lock 해제
	pthread_mutex_unlock( &m_mutexMap );

	return;
}

// 해당 Content 에 대한 Job 등록
// true  : 등록 성공
// false : 등록 실패
bool CCacheJobList::Register( CCacheData & data )
{
	// 전달받은 정보를 이용하여 map 조회를 위한 key 값 생성
	std::string strKey;
	if( GetKey( data.m_requestData, strKey ) == false )
	{
		// Key 생성 실패시
		return false;
	}

	// 생성된 Key 를 기준으로 존재 여부 확인
	bool bResult;
	std::map<std::string, CCacheData>::const_iterator pos;

	// lock 처리 후 조회
	pthread_mutex_lock( &m_mutexMap );

	pos = m_mapJob.find( strKey );
	if( pos != m_mapJob.end() )
	{
		// 해당 Key 값이 이미 존재하는 경우. 등록 실패 처리
		bResult = false;
	}
	else
	{
		// 해당 정보가 존재하지 않는 경우.
		// map 에 등록 처리.
		if( m_mapJob.insert( std::pair<const std::string, CCacheData>( strKey, data ) ).second == false )
		{
			// insert 작업 실패시
			bResult = false;
		}
		else
		{
			// insert 작업 성공시 
			bResult = true;
		}
	}
	
	// lock 해제
	pthread_mutex_unlock( &m_mutexMap );

	// 처리 결과에 따른 로깅 처리
	if( bResult == false )
	{
		LOG( LERR, "cache data insert fail to job [%s][%s]"
				, data.m_requestData.m_strRequestHost.c_str()
				, data.m_requestData.m_strFileNameHash.c_str() );		
	}

	return bResult;
}

// 저장된 job 중에서 상태 정보가 CACHE_STATUS_READY 인 job 을
// 전달된 listData 에 최대 nMaxCount 값만큼 저장하여 반환한다.
// - 전달된 job 은 중복 추출을 방지하기 위해 상태정보를 CACHE_STATUS_START 변경처리한다.
// @param [IN] nMaxCout 추출 가능한 최대 갯수
// @param [OUT] 추출된 job 정보를 저장하기 위한 List
// @return false 추출 가능한 항목이 없을 경우.
//         true  그 외
bool CCacheJobList::GetDataToStart( const unsigned int nMaxCount, std::list<CCacheData>& listData )
{
	// 인자로 전달되어 저장 역활을 담당하는 listData 객체 초기화
	listData.clear();
	
	// 전달받은 인자값 검사
	if( nMaxCount == 0 )
	{
		LOG( LERR, "request job count 0. check flow [%u]", nMaxCount );
		return false;
	}
	
	std::map<std::string, CCacheData>::iterator it;

	// lock 설정
	pthread_mutex_lock( &m_mutexMap );

	for( it = m_mapJob.begin(); it != m_mapJob.end(); ++it )
	{
		// CACHE_STATUS_READY 상태인지 확인
		if( it->second.IsStatusReady() == true )
		{
			// 해당 job 의 상태를 CACHE_STATUS_START 로 변경 처리
			it->second.SetStatusStart();

			// 전달받은 listData 에 해당 정보 push 처리
			listData.push_back( it->second );

			// 요청한 갯수만큼 추출했는지 확인
			if( listData.size() >= nMaxCount )
				break;		
		}
	}
	
	// lock 해제
	pthread_mutex_unlock( &m_mutexMap );

	if( listData.empty() == true )
		return false;
	else
		return true;
}


// 전달받은 정보를 바탕으로 map 에 사용할 Key 값을 생성한다.
// key 값은 동일 Contnt 중복 방지를 위해
// content_length:filename_hash(경로정보를 제외한 Only hash name) 형식으로 구성한다.
// @param data [IN] content length 및 filename hash 정보가 저장된 CCacheRequestData 객체 참조
// @param strKey [OUT] 생성된 key 값.
bool CCacheJobList::GetKey( CCacheRequestData & data, std::string& strKey )
{
	// Key 값 생성을 위한 값에 대한 검사
	if( data.m_uContentLength == 0 )
	{
		LOG( LERR, "invalid key parameter. length[%llu] hash[%s]"
			, data.m_uContentLength, data.m_strFileNameHash.c_str());

		return false;
	}

	std::string strHashName;
	if( data.GetOnlyHashFileName( strHashName ) == false )
	{
		LOG( LERR, "invalid key parameter. hash[%s]"
			,data.m_strFileNameHash.c_str() );

		return false;
	}
	else
	{
		char tempBuffer[256];
		sprintf( tempBuffer, "%llu", data.m_uContentLength );

		strKey = tempBuffer;
		strKey.append( ":" );
		strKey.append( strHashName );

		return true;
	}
}
