/*-- Include
*/
#include "httpdownloader.h"

/*-- Global data
*/
extern HINSTANCE g_instance;
Loader           g_classDesc;

/*-- Prototype
*/
ClassDesc* GetClassDesc( ) { return &g_classDesc; }

/*-- Loader Constructor
*/
Loader::Loader(void)
: m_internet( NULL ),
m_session( NULL ),
m_port( 80 )
{
}

/*-- Loader Destructor
*/
Loader::~Loader(void)
{
	Close( );
}

/*-- Open Method - 인터넷 연결 - HTTP 방식
*/
DWORD Loader::Open(char* url, unsigned int port)
{
	Close( );

	try {
		m_internet = InternetOpen( NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, 0, INTERNET_FLAG_NO_CACHE_WRITE );
		if ( m_internet == NULL )
			throw GetLastError( );
		if( port != 0)
			m_session = InternetConnect( m_internet, url, port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 );
		else
			m_session = InternetConnect( m_internet, url, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 );

		if ( m_session == NULL )
			throw GetLastError( );

		strcpy( m_url, url );
		m_port = (port == 0) ? 80 : port ;
		return 0;
	} catch ( DWORD error ) {
		Close( );
		return error;
	}
}

/*-- Close Method
*/
void Loader::Close( )
{
	if ( m_session != NULL )
	{
		InternetCloseHandle( m_session );
		m_session = NULL;
	}
	if ( m_internet != NULL )
	{
		InternetCloseHandle( m_internet );
		m_internet = NULL;
	}
}

/*-- Get Method
*/
DWORD Loader::Get(char* url, LOADER_PROGRESS_ROUTINE routine, DWORD* exitCode, void* data, char** buffer, DWORD* bufferLen)
{
	HINTERNET request = NULL;
	DWORD     retcode = 0;

	Open( m_url, m_port );

	try {
		// HTTP 형식의 Request 핸들 생성 - URL 참조.
		request = HttpOpenRequest( m_session, "GET", url, HTTP_VERSION, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NEED_FILE, 0 );
		if ( request == NULL )
			throw GetLastError( );

		// HTTP 서버에 요청 보내기.
		if ( HttpSendRequest( request, "", 0, 0, 0 ) == FALSE )
			throw GetLastError( );

		char  query[ 32 ];
		memset( query, 0, sizeof(query) );
		DWORD cbLengthQuery = sizeof(query);

		// HTTP 서버의 컨텐츠 크기 받기.
		if ( HttpQueryInfo( request, HTTP_QUERY_CONTENT_LENGTH, query, &cbLengthQuery, NULL ) == FALSE )
			throw GetLastError( );

		// 메모리 할당 및 변수 초기화.
		DWORD offset = 0;

		(*bufferLen) = (DWORD)atol( query );
		(*buffer)    = (char*)GlobalAlloc( GPTR, (*bufferLen)+1 );

		if ( (*buffer) == NULL )
			throw GetLastError( );

		DWORD numOfByteAvaiable   = 0;
		DWORD cbNumOfByteAvaiable = 0;
		char* ptr                 = (*buffer);

		(*exitCode) = LPR_CONTINUE;
		do {
			// 서버에서 보낼 준비가 된 크기.`
			if ( InternetQueryDataAvailable( request, &numOfByteAvaiable, 0, 0 ) == FALSE )
				throw GetLastError( );

			// 서버에서 데이터 받기.
			if ( InternetReadFile( request, ptr, numOfByteAvaiable, &cbNumOfByteAvaiable ) == FALSE )
				throw GetLastError( );

			// 서버에서 받은 데이터 누적.
			offset += cbNumOfByteAvaiable;
			ptr    += cbNumOfByteAvaiable;

			if ( routine != NULL && (*exitCode) == LPR_CONTINUE )
				(*exitCode) = (*routine)( (*bufferLen), offset, data );

		} while ( offset < (*bufferLen) && (*exitCode) == LPR_CONTINUE );
	} catch ( DWORD error ) {
		retcode = error;
	}

	if ( request != NULL )
	{
		InternetCloseHandle( request );
		request = NULL;
	}

	Close();

	return retcode;
}
