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

namespace Mix{ namespace IO{

//ログの文字列
const wchar_t* NarrowManager::FAILED_MOUNTARCHIVE          = L"アーカイブのマウントに失敗";
const wchar_t* NarrowManager::FAILED_MOUNTDIRECTORY        = L"ディレクトリのマウントに失敗";
const wchar_t* NarrowManager::FAILED_MOUNTFILE             = L"ファイルのマウントに失敗";

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::IO::NarrowManager
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

NarrowManager::NarrowManager( void )
{
}

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

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

Boolean NarrowManager::GetFileReader( const wchar_t* pFailedMsg, const wchar_t* pFilePath, Mix::IO::IReader** ppReader )
{
	Mix::IO::Controller* pController;
	Mix::IO::FileReader* pFileReader;
	
	//ファイルを探す
	pController = FindController( pFilePath, False, pFailedMsg );
	if( pController == NULL )
	{
		return False;
	}

	if( pController->Open( Mix::IO::Controller::READ ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, MSG_FILE_NOT_OPEN, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	pFileReader = Mix::IO::FileReader::CreateInstance( pFilePath, pController );
	if( pFileReader == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		pController->Close();
		return False;
	}

	( *ppReader ) = pFileReader;

	return True;
}

Mix::IO::Controller* NarrowManager::FindController( const wchar_t* pFilePath, Boolean bWrite, const wchar_t* pFailedMsg )
{
	Mix::IO::Controller* pController = NULL;
	Mix::StringW filePath;

	//ファイル名を修正
	if( ModifyPath( P_LOW, pFilePath, filePath ) == True )
	{
		FileMap::iterator it = m_FileMap.find( filePath );

		//ファイルを取得
		if( it != m_FileMap.end() )
		{
			//オープンチェック
			if( it->second->IsOpen() == False )
			{
				pController = it->second;
			}
			else
			{
				MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, MSG_FILE_IN_USE, Mix::STR_FILEPATH, pFilePath );
			}
		}
		else
		{
			if( bWrite == True )
			{
				//新規作成
				Mix::IO::FileController* pFileController;

				pFileController = Mix::IO::FileController::CreateInstance( filePath.GetConstPtr() );
				if( pFileController != NULL )
				{
					m_FileMap[filePath] = pFileController;
					pController = pFileController;
				}
				else
				{
					MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
				}
			}
			else
			{
				MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, Mix::STR_FILENOTFOUND, Mix::STR_FILEPATH, pFilePath );
			}
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", pFailedMsg, MSG_FILE_PATH_NOT_SOLVE, Mix::STR_FILEPATH, pFilePath );
	}

	return pController;
}

Mix::Memory::IBuffer* NarrowManager::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 = FindController( pFilePath, False, FAILED_CREATEBUFFEREDREADER );
	if( pCtrl == NULL )
	{
		return NULL;
	}

	if( pCtrl->Open( Controller::READ ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEBUFFEREDREADER, MSG_FILE_NOT_OPEN, Mix::STR_FILEPATH, pFilePath );
		pCtrl->Close();
		return False;
	}

	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;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::IO::IManager
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean NarrowManager::MountArchive( const wchar_t* pFilePath )
{
	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s]", FAILED_MOUNTARCHIVE, Mix::STR_ILLEGALARG, ( pFilePath != NULL )? pFilePath : L"NULL" );
		return False;
	}

	Mix::StringW fileName;
	HANDLE hFile;

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

	hFile = ::CreateFile( fileName.GetConstPtr(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if( hFile != INVALID_HANDLE_VALUE )
	{
		DWORD readSize;
		ARCHIVE_HEADER header;

		if( ( ::ReadFile( hFile, &header, sizeof( header ), &readSize, NULL ) == TRUE ) &&
			( sizeof( header ) == readSize ) &&
			( header.magicNumber == ARCHIVE_MAGICNUMBER ) ||
			( header.version == ARCHIVE_VERSION ) )
		{
			UIntT lastIndex;
			Mix::String temp;
			Mix::String path;
			Mix::IO::Archive* pArchive = Mix::IO::Archive::CreateInstance( fileName.GetConstPtr() );

			temp = fileName;

			lastIndex = temp.LastIndexOf( L'\\' );
			if( lastIndex == SIZE_MAX )
			{
				lastIndex = temp.LastIndexOf( L'\\' );
			}

			if( lastIndex != SIZE_MAX )
			{
				path = temp.Mid( 0, ( lastIndex + 1 ) );
			}
			else
			{
				path = L"";
			}

			if( pArchive != NULL )
			{
				UInt32 fileListByteSize;
				UInt32 stringBufferByteSize;
				Mix::STL::Vector<Mix::Memory::SECTION_GENERAL, ARCHIVE_FILE> fileList;
				Mix::STL::Vector<Mix::Memory::SECTION_GENERAL, wchar_t> stringBuffer;
				LARGE_INTEGER distance;
				LARGE_INTEGER newPoint;

				fileListByteSize = ( sizeof( ARCHIVE_FILE ) * header.fileCount );
				stringBufferByteSize = ( sizeof( wchar_t ) * header.stringSize );

				fileList.resize( header.fileCount );
				stringBuffer.resize( header.stringSize );

				distance.QuadPart = header.fileStart;
				if( ( ::SetFilePointerEx( hFile, distance, &newPoint, FILE_BEGIN ) == TRUE ) &&
					( ::ReadFile( hFile, &( fileList[0] ), fileListByteSize, &readSize, NULL ) == TRUE ) &&
					( fileListByteSize == readSize ) )
				{
					distance.QuadPart = header.stringStart;
					if( ( ::SetFilePointerEx( hFile, distance, &newPoint, FILE_BEGIN ) == TRUE ) &&
						( ::ReadFile( hFile, &( stringBuffer[0] ), stringBufferByteSize, &readSize, NULL ) == TRUE ) &&
						( stringBufferByteSize == readSize ) )
					{
						for( Mix::STL::Vector<Mix::Memory::SECTION_GENERAL, ARCHIVE_FILE>::iterator it = fileList.begin(); it != fileList.end(); ++it )
						{
							Mix::String tempKey;
							Mix::String key;
							Mix::IO::ArchiveController* pController;

							tempKey = path;
							tempKey += &( stringBuffer[( *it ).namePos] );

							if( ModifyPath( P_LOW, tempKey.GetConstPtr(), key ) == False )
							{
								MIX_LOG_ERROR( L"%s : 絶対パスに変換できませんでした : %s[%s]", FAILED_MOUNTARCHIVE, Mix::STR_FILEPATH, tempKey.GetConstPtr() );

								MIX_RELEASE( pArchive );
								::CloseHandle( hFile );

								return False;
							}

							pController = Mix::IO::ArchiveController::CreateInstance( key.GetConstPtr(), pArchive, ( sizeof( ARCHIVE_HEADER ) + ( ( *it ).dataPos * header.alignment ) ), ( *it ).dataSize );
							if( pController != NULL )
							{
								FileMap::iterator it_file = m_FileMap.find( key );
								if( it_file != m_FileMap.end() )
								{
									MIX_RELEASE( it_file->second );
									m_FileMap.erase( it_file );
								}

								m_FileMap[key] = pController;
							}
							else
							{
								MIX_LOG_ERROR( L"%s : %s", FAILED_MOUNTARCHIVE, Mix::STR_OUTOFMEMORY );

								MIX_RELEASE( pArchive );
								::CloseHandle( hFile );

								return False;
							}
						}
					}
					else
					{
						MIX_LOG_ERROR( L"%s : 不正なアーカイブファイル(3) : %s[%s]", FAILED_MOUNTARCHIVE, Mix::STR_FILEPATH, pFilePath );

						MIX_RELEASE( pArchive );
						::CloseHandle( hFile );

						return False;
					}
				}
				else
				{
					MIX_LOG_ERROR( L"%s : 不正なアーカイブファイル(2) : %s[%s]", FAILED_MOUNTARCHIVE, Mix::STR_FILEPATH, pFilePath );
					MIX_RELEASE( pArchive );
					::CloseHandle( hFile );
					return False;
				}

				MIX_RELEASE( pArchive );
			}
			else
			{
				MIX_LOG_ERROR( L"%s : %s", FAILED_MOUNTARCHIVE, Mix::STR_OUTOFMEMORY );
				::CloseHandle( hFile );
				return False;
			}
		}
		else
		{
			MIX_LOG_ERROR( L"%s : 不正なアーカイブファイル(1) : %s[%s]", FAILED_MOUNTARCHIVE, Mix::STR_FILEPATH, pFilePath );
			::CloseHandle( hFile );
			return False;
		}

		::CloseHandle( hFile );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_MOUNTARCHIVE, Mix::STR_FILENOTFOUND, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	MIX_LOG_INFO( L"アーカイブをマウント : Archive[%s]", pFilePath );

	return True;
}

Boolean NarrowManager::MountDirectory( const wchar_t* pDirectoryPath )
{
	if( ( pDirectoryPath == NULL ) ||
		( ::wcslen( pDirectoryPath ) == 0 ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pDirectoryPath[%s]", FAILED_MOUNTDIRECTORY, Mix::STR_ILLEGALARG, ( pDirectoryPath != NULL )? pDirectoryPath : L"NULL" );
		return False;
	}

	Boolean bSuccess;
	Mix::StringW directoryPath;
	Mix::String searchPath;
	WIN32_FIND_DATAW wfd;
	HANDLE hFind;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	bSuccess = True;

	if( ModifyPath( P_DIR | P_LOW, pDirectoryPath, directoryPath ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_MOUNTDIRECTORY, MSG_DIR_PATH_NOT_SOLVE, Mix::STR_DIRPATH, pDirectoryPath );
		return False;
	}

	searchPath.Sprintf( L"%s*.*", directoryPath.GetConstPtr() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ディレクトリ内を検索
	////////////////////////////////////////////////////////////////////////////////////////////////////

	hFind = ::FindFirstFileW( searchPath.GetConstPtr(), &wfd );
	if( hFind != INVALID_HANDLE_VALUE )
	{
		do
		{
			if( ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY )
			{
				if( ( ::wcscmp( wfd.cFileName, L"." ) != 0 ) &&
					( ::wcscmp( wfd.cFileName, L".." ) != 0 ) )
				{
					//さらに下のディレクトリへ移動

					Mix::StringW nextDirectoryName;
					nextDirectoryName.Sprintf( L"%s%s", directoryPath.GetConstPtr(), wfd.cFileName );
					MountDirectory( nextDirectoryName.GetConstPtr() );
				}
			}
			else
			{
				//ローカルコントローラーを追加

				Mix::String temp;
				Mix::StringW key;

				temp.Sprintf( L"%s%s", directoryPath.GetConstPtr(), wfd.cFileName );
				ModifyPath( P_LOW, temp.GetConstPtr(), key );

				FileMap::iterator it_file = m_FileMap.find( key );
				if( it_file == m_FileMap.end() )
				{
					Mix::IO::FileController* pController = Mix::IO::FileController::CreateInstance( key.GetConstPtr() );

					if( pController != NULL )
					{
						m_FileMap[key] = pController;
					}
					else
					{
						bSuccess = False;
					}
				}
			}
		}
		while( ( ::FindNextFileW( hFind, &wfd ) == TRUE ) && ( bSuccess == True ) );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : 検索パスが不正 : SearchPath[%s]", FAILED_MOUNTDIRECTORY, searchPath.GetConstPtr() );
		bSuccess = False;
	}

	MIX_LOG_INFO( L"ディレクトリをマウント : %s[%s]", Mix::STR_DIRPATH, pDirectoryPath );

	return bSuccess;
}

Boolean NarrowManager::MountFile( const wchar_t* pFilePath )
{
	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pDirectoryPath[%s]", FAILED_MOUNTFILE, Mix::STR_ILLEGALARG, ( pFilePath != NULL )? pFilePath : L"NULL" );
		return False;
	}

	Mix::StringW fileName;
	WIN32_FIND_DATA wfd;

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

	HANDLE hFind = ::FindFirstFile( fileName.GetConstPtr(), &wfd );
	if( hFind != INVALID_HANDLE_VALUE )
	{
		::FindClose( hFind );

		if( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
		{
			// 同名のディレクトリだった
			MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_MOUNTFILE, Mix::STR_FILENOTFOUND, Mix::STR_FILEPATH, pFilePath );
			return False;
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_MOUNTFILE, Mix::STR_FILENOTFOUND, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	FileMap::iterator it_file = m_FileMap.find( fileName );
	if( it_file == m_FileMap.end() )
	{
		Mix::IO::FileController* pController = Mix::IO::FileController::CreateInstance( fileName.GetConstPtr() );
		if( pController != NULL )
		{
			m_FileMap[fileName] = pController;
			MIX_LOG_INFO( L"ファイルをマウント : %s[%s]", Mix::STR_FILEPATH, pFilePath );
		}
		else
		{
			MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_MOUNTFILE, Mix::STR_FILEPATH, Mix::STR_OUTOFMEMORY );
			return False;
		}
	}

	return True;
}

Boolean NarrowManager::ExistsFile( const wchar_t* pFilePath )
{
	Mix::ScopedLock lock( m_MySync );

	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pDirectoryPath[%s]", FAILED_EXISTFILE, Mix::STR_ILLEGALARG, MIX_LOG_STR( pFilePath ) );
		return False;
	}

	Mix::StringW fileName;

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

	if( m_FileMap.find( fileName.GetConstPtr() ) == m_FileMap.end() )
	{
		return False;
	}

	return True;
}

Boolean NarrowManager::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 NarrowManager::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;
	}

	if( GetFileReader( FAILED_CREATEFILEREADER, pFilePath, ppReader ) == False )
	{
		return False;
	}

	return True;
}

Boolean NarrowManager::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::Memory::IBuffer* pBuff;
	Mix::IO::BufferedReader* pReader;

	pBuff = CreateBufferFromFile( pFilePath, 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 NarrowManager::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 NarrowManager::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;
	}

	size_t pos;
	size_t len;
	Mix::String work;
	Mix::String directoryName;
	Mix::IO::Controller* pController;
	Mix::IO::FileWriter* pFileWriter;

	len = ::wcslen( pFilePath );
	for( pos = len; pos > 0; --pos )
	{
		if( ( pFilePath[pos] == L'\\' ) ||
			( pFilePath[pos] == L'/' ) )
		{
			break;
		}
	}

	if( pos == 0 )
	{
		MIX_LOG_ERROR( L"%s : 指定されたファイルパスは無効です : %s[%s]", FAILED_CREATEFILEWRITER, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

	work = pFilePath;
	directoryName = work.Left( pos + 1 );

	if( CreateDirectory( FAILED_CREATEFILEWRITER, directoryName.GetConstPtr() ) == False )
	{
		return False;
	}

	pController = FindController( pFilePath, True, FAILED_CREATEFILEWRITER );
	if( pController == NULL )
	{
		return False;
	}

	if( pController->Open( Mix::IO::Controller::WRITE ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEFILEWRITER, Mix::STR_FILENOTFOUND, Mix::STR_FILEPATH, pFilePath );
		return False;
	}

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

	( *ppWriter ) = pFileWriter;

	return True;
}

}}
