#include"CFileRead.h"
#include"../CString.h"

#include<io.h>

#include"CFileReadNormal.h"
#include"CFileReadMemory.h"
#include"../Debug/CWarning.h"
#include"../Debug/CAssert.h"
#include"../Debug/CException.h"


namespace Maid
{

	CPackFileManager CFileRead::s_PackManager;


/*!
 	@class	CFileRead CFileRead.h
 	@brief	複数のファイル読み込みをサポートするクラス
 
 */

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 */
CFileRead::CFileRead()
{

}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! デストラクタ
/*!
 */
CFileRead::~CFileRead()
{
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 指定したファイルを開く
/*!
 	@param	FileName	[i ]	読み込むファイル名
 */
void CFileRead::Open( const mstring& FileName )
{
	MAID_ASSERT( !IsExist(FileName), "ファイルが存在していません" << FileName );

	Close();

	try {
		const MySTL::string str = CString::ConvertMAIDtoSJIS(FileName);

		if( ::access(str.c_str(),0)!=-1 )
		{
			//	普通のファイルがあったらそいつ
			boost::shared_ptr<CFileReadNormal> pFile( new CFileReadNormal );

			pFile->Open(FileName);

			m_pFile = pFile;
		}else
		{
			//	存在していなかったらパックファイルから読み込む

			mstring name(FileName);
			{
				for( int i=0; i<(int)name.length(); ++i )
				{
					const unt32 c = name[i];
					if( c=='/'    ) { name[i] = '\\'; }
				}
			}

			m_pFile = s_PackManager.FindFile( name );
		}
	}
	catch( CException& e )
	{
		//	エラったらログを残しておく
		MAID_WARNING( "ファイルのオープンに失敗" << FileName << e.Text );
		throw;
	}

	m_FileName = FileName;
	m_IsOnmemory = false;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ファイルをオープン後、メモリ上に展開しておく
/*!
 	@param	FileName	[i ]	読み込むファイル名
 */
void	CFileRead::OpenOnmemory( const mstring& FileName )
{
	Close();

	boost::shared_array<unt08> pDat;

	CFileRead hFile;

	hFile.Open(FileName);

	const int FileSize = hFile.GetSize();

	pDat.reset( new unt08[FileSize] );

	hFile.Read( pDat.get(), FileSize );

	{
		boost::shared_ptr<CFileReadMemory> pObj(new CFileReadMemory);

		pObj->Open(pDat, FileSize);

		m_pFile = pObj;
	}

	m_FileName = FileName;
	m_IsOnmemory = true;
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! すでに開いているファイルを閉じる
/*!
 */
void CFileRead::Close()
{
	m_pFile.reset();
	m_IsOnmemory = false;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ファイルを読み込む
/*!
 *	\param	pData	[ o]	読込先
 *	\param	Size	[i ]	読み込む長さ（バイト単位）
 *
 *	\return	実際に読み込んだバイト数
 */
int CFileRead::Read( void* pData, int Size )
{
	MAID_ASSERT( !m_pFile, "ファイルが開かれていません" );
	if( !m_pFile ) { return 0; }
	return m_pFile->Read( pData, Size );
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ファイルポジションの移動
/*!
 *	\param	Size	[i ]	移動量（バイト単位）
 *	\param	ePos	[i ]	移動基準位置
 *
 *	\return	関数の成功で DENRESULT_OK
 *			関数の失敗で 0以外
 */
void	CFileRead::Seek( int Size, IFileRead::POSITION ePos )
{
	MAID_ASSERT( !m_pFile, "ファイルが開かれていません" );
	if( !m_pFile ) { return; }
	return m_pFile->Seek( Size, ePos );
}

void	CFileRead::SetPosition( int pos )
{
	m_pFile->Seek( pos, IFileRead::POSITION_BEGIN );
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! デストラクタ
/*!
 *	\return IFileRead::GetSize と同じ
 */
int		CFileRead::GetSize()    const
{
	MAID_ASSERT( !m_pFile, "ファイルが開かれていません" );
	if( !m_pFile ) { return 0; }
	return m_pFile->GetSize();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! デストラクタ
/*!
 *	\return IFileRead::GetPosition と同じ
 */
int		CFileRead::GetPosition() const
{
	MAID_ASSERT( !m_pFile, "ファイルが開かれていません" );
	if( !m_pFile ) { return 0; }
	return m_pFile->GetPosition();
}

bool CFileRead::IsEOF() const
{
	return GetSize() <= GetPosition();
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! １バイトビックエンディアンで読み込む
/*!
 */
unt08 CFileRead::Read1ByteBigEndian()
{
	//	１バイトってエンディアン関係ないよねぇ(´_ゝ`)
	unt08 r=0;
	Read( &r, sizeof(r) );
	return r;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ２バイトビックエンディアンで読み込む
/*!
 */
unt16 CFileRead::Read2ByteBigEndian()
{
	unt08 r[2] = {0};
	Read( &r, sizeof(r) );

	return (r[0]<<8) | (r[1]<<0);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ３バイトビックエンディアンで読み込む
/*!
 */
unt32 CFileRead::Read3ByteBigEndian()
{
	unt08 r[3] = {0};
	Read( &r, sizeof(r) );

	return (r[0]<<16) | (r[1]<<8) | (r[2]<<0);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ４バイトビックエンディアンで読み込む
/*!
 */
unt32 CFileRead::Read4ByteBigEndian()
{
	unt08 r[4] = {0};
	Read( &r, sizeof(r) );

	return (r[0]<<24) | (r[1]<<16) | (r[2]<<8) | (r[3]<<0);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! １バイトリトルエンディアンで読み込む
/*!
 */
unt08 CFileRead::Read1ByteLittleEndian()
{
	//	１バイトってエンディアン関係ないよねぇ(´_ゝ`)
	return Read1ByteBigEndian();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ２バイトリトルエンディアンで読み込む
/*!
 */
unt16 CFileRead::Read2ByteLittleEndian()
{
	unt08 r[2] = {0};
	Read( &r, sizeof(r) );

	return (r[1]<<8) | (r[0]<<0);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ３バイトリトルエンディアンで読み込む
/*!
 */
unt32 CFileRead::Read3ByteLittleEndian()
{
	unt08 r[3] = {0};
	Read( &r, sizeof(r) );

	return (r[2]<<16) | (r[1]<<8) | (r[0]<<0);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ４バイトリトルエンディアンで読み込む
/*!
 */
unt32 CFileRead::Read4ByteLittleEndian()
{
	unt08 r[4] = {0};
	Read( &r, sizeof(r) );

	return (r[3]<<24) | (r[2]<<16) | (r[1]<<8) | (r[0]<<0);
}
float CFileRead::ReadfloatBigEndian()
{
	float ret;
	unt08 tmp[sizeof(ret)] = {0};
	Read( tmp, sizeof(tmp) );

	unt08* p = (unt08*)&ret;
	for( int i=0; i<sizeof(ret); ++i ) { p[(sizeof(ret)-1)-i]=tmp[i]; }

	return ret;
}

float CFileRead::ReadfloatLittleEndian()
{
	float ret;
	unt08 tmp[sizeof(ret)] = {0};
	Read( tmp, sizeof(tmp) );
	unt08* p = (unt08*)&ret;

	for( int i=0; i<sizeof(ret); ++i ) { p[i]=tmp[i]; }

	return ret;
}

double CFileRead::ReaddoubleBigEndian()
{
	double ret;
	unt08 tmp[sizeof(ret)] = {0};
	Read( tmp, sizeof(tmp) );

	unt08* p = (unt08*)&ret;
	for( int i=0; i<sizeof(ret); ++i ) { p[(sizeof(ret)-1)-i]=tmp[i]; }

	return ret;
}

double CFileRead::ReaddoubleLittleEndian()
{
	double ret;
	unt08 tmp[sizeof(ret)] = {0};
	Read( tmp, sizeof(tmp) );
	unt08* p = (unt08*)&ret;

	for( int i=0; i<sizeof(ret); ++i ) { p[i]=tmp[i]; }

	return ret;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の場所から '\0' まで読み取る
/*!
 	@param	endian	[i ]	読み取り終了後に合わせる量(デフォルトで1)
 
 	@return よみとった文字列
 */
mstring CFileRead::ReadString( int endian )
{
	MAID_ASSERT( endian<=0, "１以上必要" );
	MySTL::string str;

	while( true )
	{
		const char c = (char)Read1ByteBigEndian();

		if( c=='\0' )
		{
			const int len = (int)str.length()+1;
			const int mod = len%endian;

			Seek( mod, IFileRead::POSITION_CURRENT );
			break;
		}

		str += c;
	}

	//	本当なら文字コードを判別しないといけないけど、メンドイのでスルー


	return CString::ConvertSJIStoMAID(str);
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ファイルが存在しているか？
/*!
 *	\param	FileName	[i ]	調べたいファイル名
 *
 *	\return 存在しているなら true
 */
bool CFileRead::IsExist( const mstring& FileName )
{
	const MySTL::string str = CString::ConvertMAIDtoSJIS(FileName);

	if( ::access(str.c_str(),0)!=-1 ) { return true; }

	return s_PackManager.IsExist( FileName );
}


CPackFileManager& CFileRead::GetPackFile()
{
	return s_PackManager;
}


}