#include "Archiver.h"
#include "Mix/Tool/Win32/Archiver.h"

using namespace System;
using namespace System::Runtime::InteropServices;

namespace Mix{ namespace Tool{

System::String^ Archiver::RootDirectoryName::get( void )
{
	return static_cast<System::String^>( m_RootDirectoryName->Clone() );
}

void Archiver::RootDirectoryName::set( System::String^ value )
{
	m_RootDirectoryName = static_cast<System::String^>( value->Clone() );
}

System::String^ Archiver::InputDirectoryName::get( void )
{
	return static_cast<System::String^>( m_InputDirectoryName->Clone() );
}

void Archiver::InputDirectoryName::set( System::String^ value )
{
	m_InputDirectoryName = static_cast<System::String^>( value->Clone() );
}

System::String^ Archiver::OutputFileName::get( void )
{
	return static_cast<System::String^>( m_OutputFileName->Clone() );
}

void Archiver::OutputFileName::set( System::String^ value )
{
	m_OutputFileName = static_cast<System::String^>( value->Clone() );
}

unsigned int Archiver::Alignment::get( void )
{
	return m_Alignment;
}

void Archiver::Alignment::set( unsigned int value )
{
	m_Alignment = value;
}

unsigned int Archiver::TemporaryBufferSize::get( void )
{
	return m_TemporaryBufferSize;
}

void Archiver::TemporaryBufferSize::set( unsigned int value )
{
	m_TemporaryBufferSize = value;
}

void Archiver::PreCreate::set( DelegatePreCreate^ value )
{
	preCreate = value;
}

void Archiver::ProcessFile::set( DelegateProcessFile^ value )
{
	processFile = value;
}

void Archiver::UpdateFileProgress::set( DelegateUpdateFileProgress^ value )
{
	updateFileProgress = value;
}

void Archiver::UpdateTotalProgress::set( DelegateUpdateTotalProgress^ value )
{
	updateTotalProgress = value;
}

void Archiver::End::set( DelegateEnd^ value )
{
	end = value;
}

Archiver::Archiver( void ) :
m_RootDirectoryName( L"" ),
m_InputDirectoryName( L"" ),
m_OutputFileName( L"" ),
m_Alignment( 1 ),
m_TemporaryBufferSize( 0 ),
preCreate( nullptr ),
processFile( nullptr ),
updateFileProgress( nullptr ),
updateTotalProgress( nullptr ),
end( nullptr )
{
}

void Archiver::Run( void )
{
	::MAF_RESULT result;
	::MAF_CONTEXT context;
	::MAF_CONTEXT_WRITE contextWrite;

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

	IntPtr rootDirNamePtr = Marshal::StringToHGlobalUni( m_RootDirectoryName );
	IntPtr inputDirNamePtr = Marshal::StringToHGlobalUni( m_InputDirectoryName );
	IntPtr outputFileNamePtr = Marshal::StringToHGlobalUni( m_OutputFileName );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// アーカイブを実行
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Logger::Clear();
	Logger::Print( Logger::Type::Info, L"格納するファイルの情報を収集しています" );

	result = ::MAF_InitializeContext(
		static_cast<const wchar_t*>( rootDirNamePtr.ToPointer() ),
		static_cast<const wchar_t*>( inputDirNamePtr.ToPointer() ),
		static_cast<const wchar_t*>( outputFileNamePtr.ToPointer() ),
		m_Alignment,
		m_TemporaryBufferSize,
		context,
		contextWrite );

	if( result == MAF_OK )
	{
		if( preCreate != nullptr )
		{
			preCreate( context.fileList.size(), context.totalFileSize, context.totalSize );
		}

		Logger::Print( Logger::Type::Info, L"ファイルの格納を開始します" );

		result = ::MAF_BeginWrite( context, contextWrite );

		if( result == MAF_OK )
		{
			do
			{
				unsigned int eventFlags = ::MAF_TakeEvent( contextWrite );

				if( ( eventFlags & MAF_EVENT_OUTPUTPROGRESS ) == MAF_EVENT_OUTPUTPROGRESS )
				{
					if( updateTotalProgress != nullptr )
					{
						updateTotalProgress( static_cast<int>( contextWrite.outputProgress ) );
					}
				}
				if( ( eventFlags & MAF_EVENT_FILEINDEX ) == MAF_EVENT_FILEINDEX )
				{
					if( processFile != nullptr )
					{
						processFile( gcnew System::String( ::MAF_GetFileName( context, contextWrite ) ), context.fileList[contextWrite.fileIndex].dataSize );
					}
				}
				if( ( eventFlags & MAF_EVENT_FILEPROGRESS ) == MAF_EVENT_FILEPROGRESS )
				{
					if( updateFileProgress != nullptr )
					{
						updateFileProgress( static_cast<int>( contextWrite.fileProgress ) );
					}
				}

				result = ::MAF_ProcWrite( context, contextWrite );

				::Sleep( 0 );
			}
			while( result == MAF_CONTINUE );

			if( result == MAF_OK )
			{
				result = ::MAF_EndWrite( context, contextWrite );
				if( result != MAF_OK )
				{
					System::String^ fileName = gcnew System::String( ::MAF_GetFileName( context, contextWrite ) );

					switch( result )
					{
					case MAF_ERROR_IO_WRITEOUTPUTFILE:
						Logger::Print( Logger::Type::Error, System::String::Format( L"ファイルの格納に失敗しました : 出力先のファイルが使用中か、容量が不足しています" ) );
						break;
					default:
						Logger::Print( Logger::Type::Error, System::String::Format( L"ファイルの格納中に致命的なエラーが発生しました" ) );
						break;
					}
				}
			}
			else
			{
				System::String^ fileName = gcnew System::String( ::MAF_GetFileName( context, contextWrite ) );

				switch( result )
				{
				case MAF_ERROR_IO_WRITEOUTPUTFILE:
					Logger::Print( Logger::Type::Error, System::String::Format( L"ファイル \'{0}\' の格納に失敗しました : 出力先のファイルが使用中か、容量が不足しています", fileName ) );
					break;
				case MAF_ERROR_IO_READSOURCEFILE:
					Logger::Print( Logger::Type::Error, System::String::Format( L"ファイル \'{0}\' の読み込みに失敗しました : ファイルが使用中、または存在していません", fileName ) );
					break;
				default:
					Logger::Print( Logger::Type::Error, System::String::Format( L"ファイル \'{0}\' の格納中に致命的なエラーが発生しました", fileName ) );
					break;
				}
			}
		}
		else
		{
			switch( result )
			{
			case MAF_ERROR_IO_WRITEOUTPUTFILE:
				Logger::Print( Logger::Type::Error, System::String::Format( L"ファイルの格納に失敗しました : 出力先のファイルが使用中か、容量が不足しています" ) );
				break;
			default:
				Logger::Print( Logger::Type::Error, System::String::Format( L"ファイルの格納中に致命的なエラーが発生しました" ) );
				break;
			}
		}
	}
	else
	{
		//ファイル情報の収集に失敗
		switch( result )
		{
		case MAF_ERROR_INVALID_ARGUMENT:
			Logger::Print( Logger::Type::Error, System::String::Format( L"格納するファイルの情報の収集に失敗しました : パラメータが不正です" ) );
			break;
		case MAF_ERROR_ILLEGAL_DIRECTORYNAME:
			Logger::Print( Logger::Type::Error, System::String::Format( L"格納するファイルの情報の収集に失敗しました : 入力ディレクトリ \'{1}\' は、ルートディレクトリ \'{0}\' 内に存在している必要があります", m_InputDirectoryName, m_RootDirectoryName ) );
			break;
		case MAF_ERROR_IO_COLLECTFILE:
			Logger::Print( Logger::Type::Error, System::String::Format( L"格納するファイルの情報の収集に失敗しました : 格納するファイルが使用中である可能性があります" ) );
			break;
		case MAF_ERROR_IO_COLLECTFILE_BIGFILE:
			Logger::Print( Logger::Type::Error, System::String::Format( L"格納するファイルの情報の収集に失敗しました : 格納するファイルのサイズは 4GB 以内にしてください" ) );
			break;
		case MAF_ERROR_IO_CREATEOUTPUTFILE:
			Logger::Print( Logger::Type::Error, System::String::Format( L"ファイルの格納の開始に失敗しました : ファイル \'{0}\' を作成できませんでした", m_OutputFileName ) );
			break;
		default:
			break;
		}
	}

	::MAF_ReleaseContext( context, contextWrite );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 解放
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Marshal::FreeHGlobal( outputFileNamePtr );
	Marshal::FreeHGlobal( inputDirNamePtr );
	Marshal::FreeHGlobal( rootDirNamePtr );

	if( result == MAF_OK )
	{
		Logger::Print( Logger::Type::Info, System::String::Format( L"正常に終了しました" ) );
	}

	if( end != nullptr )
	{
		end();
	}
}

}}
