////////////////////////////////////////////////////////////////////////////
// CFileWriter クラスのインプリメンテーション
//
////////////////////////////////////////////////////////////////////////////

#include "FileWriter.h"
#include "TaEditShell.h"

////////////////////////////////////////////////////////////////////////////
// 構築/破棄
////////////////////////////////////////////////////////////////////////////

CFileWriter::CFileWriter() :
	pData( NULL ),
	EnableExitFlg( false ),
	InterruptFlg( false ),
	ErrorInfo(),
	CProcesingObj(),
	FaultWithTmp( false )
{

}

CFileWriter::~CFileWriter()
{

}

////////////////////////////////////////////////////////////////////////////
// メソッド
////////////////////////////////////////////////////////////////////////////

// 情報を設定
void CFileWriter::SetInfo( const string &argFileName, const T_LineData *argData, bool argUseTmpFile )
{
	FileName = argFileName;
	pData = argData;
	EnableExitFlg = false;
	InterruptFlg = false;
	UseTmpFile = argUseTmpFile;
	FaultWithTmp = false;
}

// 書き込み用スレッドのエントリポイント
void CFileWriter::run()
{
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	T_LineDataItr_c itr;
	T_LineDataItr_c itrLastLine;	// 最後の行をポイント
	string TmpfileName;

	EnableExitFlg = false;
	InterruptFlg = false;
	ErrorInfo = true;
	FaultWithTmp = false;

	// テンポラリのファイルに待避する
	if ( UseTmpFile ) {
		if ( !MoveToTmp( FileName, &TmpfileName ) ) {
			// （このメッセージは結局使われないらしい）
			ErrorInfo = CErrorBool( pConf->RefFaultToSaveTmpFileMsg() );
			FaultWithTmp = true;
			return ;
		}
		if ( InterruptFlg ) return ;
	}

	// ファイルを開く
	FILE *outfile = fopen( FileName.c_str(), "w" );
	if ( NULL == outfile ) {
		ErrorInfo = CErrorBool( pConf->RefFaultFileOpenMsg() );
		EnableExitFlg = true;
		return ;
	}

	// 最後の行をポイントするイテレータを準備
	itrLastLine = pData->end();
	--itrLastLine;

	for ( itr = pData->begin(); itr != pData->end() && !ferror( outfile ) && !InterruptFlg; ++itr ) {
		// 一行ずつ出力する
		fputws( itr->getString().c_str(), outfile );
		// （最後の行には改行コードを出力しない）
		if ( itr != itrLastLine ) putwc( L'\n', outfile );
	}

	if ( ferror( outfile ) )
		ErrorInfo = pConf->RefIOErrorMsg( errno );
	fclose( outfile );

	// テンポラリのファイルを破棄する
	if ( ErrorInfo && UseTmpFile && !InterruptFlg )
		unlink( TmpfileName.c_str() );

	EnableExitFlg = true;
}

// 終了可能か否か
bool CFileWriter::EnableExit() const
{
	return EnableExitFlg;
}

// 割り込み
void CFileWriter::Interrupt()
{
	InterruptFlg = true;
}

// エラー情報を取得
CErrorBool CFileWriter::GetErrorInfo() const
{
	return ErrorInfo;
}

// テンポラリのファイルへ移動
bool CFileWriter::MoveToTmp( const string &argFileName, string *pDestName ) const
{
	VPtr< char > pFNameBuf;
	VPtr< char > pMoveBuf;
	string wS2;
	int outfd = -1;
	FILE *infile = NULL;
	FILE *outfile = NULL;
	int readlen;
	bool r = false;
	int i;

	// カレントディレクトリに生成するテンポラリファイル名を生成する
	wS2 = FileName;
	for ( i = 0; i < wS2.length(); i++ ) {
		if ( wS2[i] == '/' )
			wS2.replace( i, 1, "_", 1 );
	}
	wS2 = string( "/tmp/" ) + wS2 + "_XXXXXX";

	// ファイル名用のバッファを用意する
	if ( !pFNameBuf.Malloc( sizeof( char ) * ( wS2.length() + 1 ) ) ) return false;
	strncpy( pFNameBuf.GetPtr(), wS2.c_str(), wS2.length() );
	pFNameBuf.GetPtr()[ wS2.length() ] = '\0';

	// テンポラリのファイルを開く
	outfd = mkstemp( pFNameBuf.GetPtr() );
	if ( -1 == outfd ) return false;
	outfile = fdopen( outfd, "wb" );
	if ( NULL == outfile ) goto ERR_EXIT;
	outfd = -1;

	// 使用したファイル名を返す
	(*pDestName) = pFNameBuf.GetPtr();
	pFNameBuf.Free();

	// 既存のファイルを開く
	infile = fopen( argFileName.c_str(), "rb" );
	if ( NULL == infile ) goto ERR_EXIT;

	// 中身の移動用のバッファを確保
	if ( !pMoveBuf.Malloc( 65536 ) )
		goto ERR_EXIT;

	// 移動
	while ( !ferror( infile ) && !feof( infile ) && !ferror( outfile ) && !feof( outfile ) && !InterruptFlg ) {
		readlen = fread( pMoveBuf.GetPtr(), sizeof( char ) , 65536, infile );
		if ( fwrite( pMoveBuf.GetPtr(), sizeof( char ) , readlen, outfile ) < readlen ) goto ERR_EXIT;
	}
	r = !ferror( infile ) && !ferror( outfile );

ERR_EXIT:
	// ファイルを閉じる
	if ( infile ) fclose( infile );
	infile = NULL;
	if ( outfile ) fclose( outfile );
	outfile = NULL;
	if ( outfd >= 0 ) close( outfd );
	outfd = -1;

	return r;
}

// テンポラリファイルへの保存で失敗したのか否か
bool CFileWriter::GetFaultWithTmpFlg() const
{
	return FaultWithTmp;
}

