#include "Mix/Private/IO/BroadManager.h"

#include <Shlwapi.h>

#include "Mix/ScopedLock.h"
#include "Mix/Private/Memory/Buffer.h"
#include "Mix/Private/IO/FileController.h"
#include "Mix/Private/IO/FileReader.h"
#include "Mix/Private/IO/FileWriter.h"
#include "Mix/Private/IO/BufferedReader.h"

namespace Mix{ namespace IO{

BroadManager* BroadManager::CreateInstance( void )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GENERAL, BroadManager );
}

BroadManager::BroadManager( void )
{
}

BroadManager::~BroadManager( void )
{
	Mix::ScopedLock lock( m_MySync );

	for( ControllerMap::iterator it = m_CtrlMap.begin(); it != m_CtrlMap.end(); ++it )
	{
		MIX_RELEASE( it->second );
	}
}

Mix::IO::Controller* BroadManager::OpenController( const wchar_t* pFilePath, Mix::IO::Controller::MODE mode, const wchar_t* pFailedMsg )
{
	MIX_ASSERT( pFilePath != NULL );

	Mix::StringW filePath;

	if( ModifyPath( P_LOW, pFilePath, filePath ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, MSG_FILE_PATH_NOT_SOLVE, Mix::STR_FILEPATH, pFilePath );
		return NULL;
	}

	ControllerMap::iterator it = m_CtrlMap.find( filePath );
	Mix::IO::Controller* pCtrl = NULL;

	if( it != m_CtrlMap.end() )
	{
		if( it->second->IsOpen() == False )
		{
			pCtrl = it->second;
		}
		else
		{
			MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, MSG_FILE_IN_USE, Mix::STR_FILEPATH, pFilePath );
		}
	}
	else
	{
		pCtrl = Mix::IO::FileController::CreateInstance( filePath.GetConstPtr() );
		if( pCtrl != NULL )
		{
			m_CtrlMap[filePath] = pCtrl;
		}
		else
		{
			MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		}
	}

	if( pCtrl != NULL )
	{
		if( pCtrl->Open( mode ) == False )
		{
			MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, MSG_FILE_NOT_OPEN, Mix::STR_FILEPATH, pFilePath );
			pCtrl = NULL;
		}
	}

	return pCtrl;
}

Mix::Memory::IBuffer* BroadManager::CreateBufferFromFile( const wchar_t* pFilePath, const wchar_t* pFailedMsg )
{
	MIX_ASSERT( pFilePath != NULL );
	MIX_ASSERT( pFailedMsg != NULL );

	UInt32 fileSize;
	Mix::IO::Controller* pCtrl;
	Mix::Memory::Buffer* pBuffer;

	pCtrl = OpenController( pFilePath, Controller::READ, FAILED_CREATEBUFFEREDREADER );
	if( pCtrl == NULL )
	{
		return NULL;
	}

	if( pCtrl->GetSize() > 0x00000000FFFFFFFF )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s] Size[%I64d]", FAILED_CREATEBUFFEREDREADER, MSG_FILE_SIZE_LARGE, Mix::STR_FILEPATH, pFilePath, pCtrl->GetSize() );
		pCtrl->Close();
		return NULL;
	}

	pBuffer = Mix::Memory::Buffer::CreateInstance();
	if( pBuffer == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEBUFFEREDREADER, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		pCtrl->Close();
		return NULL;
	}

	fileSize = static_cast<UInt32>( pCtrl->GetSize() );

	if( pBuffer->Create( Mix::Memory::SECTION_GENERAL, fileSize ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s] Size[%d]", FAILED_CREATEBUFFEREDREADER, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath, fileSize );
		MIX_RELEASE( pBuffer );
		pCtrl->Close();
		return NULL;
	}

	if( pCtrl->Read( pBuffer->GetPointer(), MIX_UIT_TO_UI32( pBuffer->GetSize() ) ) != pBuffer->GetSize() )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEBUFFEREDREADER, MSG_FILE_NOT_READ, Mix::STR_FILEPATH, pFilePath );
		MIX_RELEASE( pBuffer );
		pCtrl->Close();
		return NULL;
	}

	pCtrl->Close();

	return pBuffer;
}

Boolean BroadManager::MountArchive( const wchar_t* pFilePath )
{
	return False;
}

Boolean BroadManager::MountDirectory( const wchar_t* pDirectoryPath )
{
	return False;
}

Boolean BroadManager::MountFile( const wchar_t* pFilePath )
{
	return False;
}

Boolean BroadManager::ExistsFile( const wchar_t* pFilePath )
{
	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s]", FAILED_EXISTFILE,
			Mix::STR_ILLEGALARG,
			MIX_LOG_STR( pFilePath ) );

		return False;
	}

	Mix::StringW filePath;

	if( ModifyPath( 0, pFilePath, filePath ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_EXISTFILE, MSG_FILE_PATH_NOT_SOLVE, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	return PathFileExists( filePath.GetConstPtr() );
}

Boolean BroadManager::CreateBufferFromFile( const wchar_t* pFilePath, Mix::Memory::IBuffer** ppBuffer )
{
	Mix::ScopedLock lock( m_MySync );

	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) ||
		( ppBuffer == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s] ppBuffer[%s]", FAILED_CREATEFILEBUFFER,
			Mix::STR_ILLEGALARG,
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppBuffer ) );

		return False;
	}

	Mix::Memory::IBuffer* pBuff;

	pBuff = CreateBufferFromFile( pFilePath, FAILED_CREATEFILEBUFFER );
	if( pBuff == NULL )
	{
		return False;
	}

	( *ppBuffer ) = pBuff;

	return True;
}

Boolean BroadManager::CreateFileReader( const wchar_t* pFilePath, Mix::IO::IReader** ppReader )
{
	Mix::ScopedLock lock( m_MySync );

	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) ||
		( ppReader == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s] ppReader[%s]", FAILED_CREATEFILEREADER,
			Mix::STR_ILLEGALARG,
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppReader ) );

		return False;
	}

	Mix::IO::Controller* pCtrl;
	Mix::IO::FileReader* pFileReader;

	pCtrl = OpenController( pFilePath, Controller::READ, FAILED_CREATEFILEREADER );
	if( pCtrl == NULL )
	{
		return False;
	}

	pFileReader = Mix::IO::FileReader::CreateInstance( pFilePath, pCtrl );
	if( pFileReader == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEFILEREADER, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	( *ppReader ) = pFileReader;

	return True;
}

Boolean BroadManager::CreateBufferedReader( const wchar_t* pFilePath, Mix::IO::IReader** ppReader )
{
	Mix::ScopedLock lock( m_MySync );

	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) ||
		( ppReader == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s] ppReader[%s]", FAILED_CREATEBUFFEREDREADER,
			Mix::STR_ILLEGALARG,
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppReader ) );

		return False;
	}

	Mix::StringW filePath;
	Mix::Memory::IBuffer* pBuff;
	Mix::IO::BufferedReader* pReader;

	if( ModifyPath( P_LOW, pFilePath, filePath ) == False )
	{
		return False;
	}

	pBuff = CreateBufferFromFile( filePath.GetConstPtr(), FAILED_CREATEBUFFEREDREADER );
	if( pBuff == NULL )
	{
		return False;
	}

	pReader = Mix::IO::BufferedReader::CreateInstance( pFilePath, pBuff );
	if( pReader == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEBUFFEREDREADER, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		MIX_RELEASE( pBuff );
		return False;
	}

	MIX_RELEASE( pBuff );

	( *ppReader ) = pReader;

	return True;
}

Boolean BroadManager::CreateBufferedReader( const wchar_t* pFilePath, Mix::Memory::IBuffer* pBuffer, Mix::IO::IReader** ppReader )
{
	Mix::ScopedLock lock( m_MySync );

	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) ||
		( pBuffer == NULL ) ||
		( ppReader == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s[%s] : pFilePath[%s] pBuffer[%s] ppReader[%s]", FAILED_CREATEBUFFEREDREADER,
			Mix::STR_FILEPATH,
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( pBuffer ),
			MIX_LOG_PTR( ppReader ) );

		return False;
	}

	Mix::IO::BufferedReader* pReader;

	pReader = Mix::IO::BufferedReader::CreateInstance( pFilePath, pBuffer );
	if( pReader == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEBUFFEREDREADER, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	( *ppReader ) = pReader;

	return True;
}

Boolean BroadManager::CreateFileWriter( const wchar_t* pFilePath, Mix::IO::IWriter** ppWriter )
{
	Mix::ScopedLock lock( m_MySync );

	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) ||
		( ppWriter == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s] ppWriter[%s]", FAILED_CREATEFILEWRITER,
			Mix::STR_ILLEGALARG,
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppWriter ) );

		return False;
	}

	Mix::IO::Controller* pCtrl;
	Mix::IO::FileWriter* pFileWriter;

	pCtrl = OpenController( pFilePath, Controller::WRITE, FAILED_CREATEFILEWRITER );
	if( pCtrl == NULL )
	{
		return False;
	}

	pFileWriter = Mix::IO::FileWriter::CreateInstance( pFilePath, pCtrl );
	if( pFileWriter == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEFILEWRITER, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	( *ppWriter ) = pFileWriter;

	return True;
}

}}
