/*-----------------------------------------------------------------------------
[名称]	Grep.cpp
[概要]	Grep検索クラスの実装
[備考]	なし
[履歴]	2004-01-30	b031228 (3367)Grep検索(dincom)
-----------------------------------------------------------------------------*/
// □総ファイル数を前もって出す
// □ファイル名にフィルタをかける
// □キャレット移動をさせる

//-----------------------------------------------------------------------------
// インクルード
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "shitarabian.h"
#include "ShitarabianDoc.h"
#include "Common.h"
#include "Grep.h"
#include "CommandInput.h"
#include "ExeCommon.h"
#include "Finder.h"
#include "ProgressDlg.h"
#include "MainFrm.h"
#include "GrepProgressDlg.h"
#include "EmphasisDefine.h"
#include "GeneralSetting.h"
// ▼ (3204) BGN 2004.05.10 (moralog) 外部アプリとのＩＦをWM_COPYDATAメッセージとする
#include "ShitarabianPostCommand.h"
// ▲ (3204) END

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////

CGrep::CGrep():
m_nHit( 0 )
{

}

CGrep::~CGrep()
{

}


/*-----------------------------------------------------------------------------
[名称]	文字列の挿入要求
[機能]	文字列の挿入要求
[戻値]	BOOL	処理結果 [-] (TRUE:成功、FALSE:失敗)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CGrep::SendInsertString(
	CString&	strInsert	// <I> 検索結果文字列
)
{
	CMainFrame*		pFrame = ::GetMainFrame();

	if ( pFrame == NULL )
	{
		return FALSE;
	}

	CWnd*	pWnd = pFrame;

// ▼ (3204) BGN 2004.05.10 (moralog) 外部アプリとのＩＦをWM_COPYDATAメッセージとする
	BOOL	bContinue( TRUE );

	// キャレットを行末へ移動
	if ( bContinue )
	{
		bContinue = CShitarabianPostCommand::GetInstance()->Send(
			pWnd->m_hWnd,
			pWnd->m_hWnd,
			(WPARAM)COMMAND_MESSAGE_CARET_EOF,
			NULL,
			0
		);
	}

	// 選択状態の解除
	if ( bContinue )
	{
		bContinue = CShitarabianPostCommand::GetInstance()->Send(
			pWnd->m_hWnd,
			pWnd->m_hWnd,
			COMMAND_MESSAGE_INIT_SELECTION,
			NULL,
			0
		);
	}

	// 文字列挿入
	if ( bContinue )
	{
		bContinue = CShitarabianPostCommand::GetInstance()->Send(
			pWnd->m_hWnd,
			pWnd->m_hWnd,
			COMMAND_MESSAGE_INSERT,
			(void*)((LPCTSTR)strInsert),
			strInsert.GetLength() +1
		);
	}

	pWnd->RedrawWindow();

	return ( bContinue );

/*
// ▽ original BGN
	CWnd*	pWnd = pFrame->GetActiveView();

	// キャレットを行末へ移動
	BOOL	bCaretEOF( FALSE );
	while( !bCaretEOF )
	{
		if( pWnd->PostMessage( pFrame->m_UM_CommandMessageID, (WPARAM)COMMAND_MESSAGE_CARET_EOF , (LPARAM)&bCaretEOF ))
		{
			Sleep(10);
		}
		else
		{
			return FALSE;
		}
	}
	// 選択状態の解除
	BOOL	bSelected( FALSE );
	while( !bSelected )
	{
		if( pWnd->PostMessage( pFrame->m_UM_CommandMessageID, (WPARAM)COMMAND_MESSAGE_INIT_SELECTION , (LPARAM)&bSelected ))
		{
			Sleep(10);
		}
		else
		{
			return FALSE;
		}
	}
	// 文字列挿入
	InsertString	tInsert;
	tInsert.strInsert	= strInsert;
	tInsert.bSuccess	= FALSE;
	while( !( tInsert.bSuccess ) )
	{
		if( pWnd->PostMessage( pFrame->m_UM_CommandMessageID, (WPARAM)COMMAND_MESSAGE_INSERT, (LPARAM)&tInsert ) )
		{
			Sleep(10);
		}
		else
		{
			return FALSE;
		}
	}

	return TRUE;
// △ original END
*/
// ▲ (3204) END
}


/*-----------------------------------------------------------------------------
[名称]	Grep実行
[機能]	進捗の表示とGrep条件をExecuteCore()に渡す
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CGrep::Execute(
	CString		strTargetFolder,		// <I> Grep対象フォルダ
	CString		strSubString,			// <I> 検索文字列
	CString		strFilter,				// <I> ファイルフィルタ情報
	BOOL		bRegExp,				// <I> 正規表現フラグ		[-] (TRUE:ON、FALSE:OFF)
	BOOL		bCaseCare,				// <I> 大小文字検索フラグ	[-] (TRUE:ON、FALSE:OFF)
	BOOL		bToken,					// <I> 単語単位検索フラグ	[-] (TRUE:ON、FALSE:OFF)
	BOOL		bSubFolder				// <I> サブフォルダ検索フラグ[-] (TRUE:ON、FALSE:OFF)
)
{
	CMainFrame*		pFrame = ::GetMainFrame();
	if ( pFrame == NULL )
	{
		return;
	}
	//CProgressDlg*	pProgress = pFrame->GetProgressDlg();
	CGrepProgressDlg* pProgress = pFrame->GetGrepProgressDlg();
	if ( pProgress == NULL )
	{
		return;
	}
	CShitarabianDoc*	pDoc = ::GetDocument();

	CString	strLine;
	long	nHit( 0 );
	// ヘッダ部分表示
	{
		// 検索文字列表示
		strLine.Format( _T( "\"%s\"を%s"), strSubString, pDoc->ReturnChar() );
		SendInsertString( strLine );

		// フォルダ表示
		strLine.Format( _T( "\"%s\"フォルダから検索...%s"), strTargetFolder, pDoc->ReturnChar() );
		SendInsertString( strLine );
	}

	//ファイルフィルタリスト作成
	CStringArray  astrFilters;
	{
		int idxLeft		= 0;
		int idxRight	= 1;

		strFilter += _T(";");
		while( idxRight > 0 )
		{
			idxRight = strFilter.Find( _T(";") , idxLeft );
			astrFilters.Add( strFilter.Mid( idxLeft , (idxRight - idxLeft) ) );
			idxLeft = idxRight + 1;
		}
	}
	
	// 検索文字列を強調表示するため描画情報の再構築する
	CArray<CEmphasisItem, CEmphasisItem&>& Itemz = CGeneralSetting::GetInstance()->GetEmphasisDefine()->GetItemz();
	CEmphasisItem&	Emphasis = Itemz.ElementAt(EMPHASIS_FOUND);
	if ( strSubString != Emphasis.GetPattern() )
	{
		Emphasis.SetPattern( strSubString );

		Emphasis.GetFindCondition()->bRegExp = bRegExp;			// 正規表現
		Emphasis.GetFindCondition()->bCaseCare = bCaseCare;		// 大文字小文字区別
		Emphasis.GetFindCondition()->bToken = bToken;			// 単語単位

		pDoc->UpdateEmphasis();
	}

	// スレッド終了までプログレスダイアログを表示	
	pProgress->SetTextFolderName( strTargetFolder );
	pProgress->SetTextHit( m_nHit );
	pProgress->Show();
	pProgress->SetActiveWindow();

	// フォルダ内のファイルを列挙
	long	nCompleteFile = 0;

	ExecuteCore(
		strTargetFolder,
		strSubString,
		astrFilters,
		bRegExp,
		bCaseCare,
		bToken,
		bSubFolder,
		nHit,
		nCompleteFile
	);

	// プログレスダイアログを非表示にする
	pProgress->Hide();

	if ( !pProgress->IsAbort() )
	{
		// フッタ部分表示
		{
			// 件数表示
			strLine.Format( _T( "%d 件ヒットしました。%s"), nHit, pDoc->ReturnChar() );
			SendInsertString( strLine );
		}

		pProgress->NotifyComplete();
	}
	else
	{
		pProgress->NotifyStop();
		strLine.Format( _T( "中断しました。%s"), pDoc->ReturnChar() );
		SendInsertString( strLine );
	}
}

/*-----------------------------------------------------------------------------
[名称]	Grep実行
[機能]	ファイルを検索し、検索結果を算出する
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CGrep::ExecuteCore(
	CString			strTargetFolder,	// <I> Grep対象フォルダ
	CString			strSubString,		// <I> 検索文字列
	CStringArray&	astrFilters,		// <I> ファイルフィルタリスト
	BOOL			bRegExp,			// <I> 正規表現フラグ		[-] (TRUE:ON、FALSE:OFF)
	BOOL			bCaseCare,			// <I> 大小文字検索フラグ	[-] (TRUE:ON、FALSE:OFF)
	BOOL			bToken,				// <I> 単語単位検索フラグ	[-] (TRUE:ON、FALSE:OFF)
	BOOL			bSubFolder,			// <I> サブフォルダ検索フラグ[-] (TRUE:ON、FALSE:OFF)
	long&			nHit,				// <O> ヒット数 [-]
	long&			nConpleteFile		// <O> 検索数 [-]
)
{
	CMainFrame*		pFrame = ::GetMainFrame();
	if ( pFrame == NULL )
	{
		return;
	}
	CGrepProgressDlg*	pProgress = pFrame->GetGrepProgressDlg();
	if ( pProgress == NULL )
	{
		return;
	}

	CString				strLine;
	CCommandInput		cmdInput;

	// フォルダ内のファイルを列挙
	{
		CFileFind fnd;
		CString strPathName = strTargetFolder;
		strPathName.TrimRight('\\');
		strPathName += _T("\\*.*");

		pProgress->SetTextFolderName( strTargetFolder );

		if(fnd.FindFile(strPathName, 0))
		{
			int i = 1;
			while(i)
			{
				i = fnd.FindNextFile();

				// ファイル名が"."か".."の場合は次を検索
				if( fnd.IsDots() )
				{
					continue;
				}

				// ファイル名取得
				// GetFilePath()にはバグがあり正確に取得できない場合があるので使わない
				CString strFindFile = strTargetFolder;
				strFindFile.TrimRight('\\');
				strFindFile += _T("\\") + fnd.GetFileName();

				// ディレクトリであれば階層下を検索
				if ( fnd.IsDirectory() )
				if ( bSubFolder )
				{
					ExecuteCore(
						strFindFile,
						strSubString,
						astrFilters,	
						bRegExp,
						bCaseCare,
						bToken,
						bSubFolder,
						nHit,
						nConpleteFile
					);

					// 中断チェック
					if ( pProgress->IsAbort() )
					{
						break;
					}
					else
					{
						continue;
					}
				}
			}
			fnd.Close();
		}

		int nFilter = astrFilters.GetSize();
		for ( int idxFilter = 0 ; idxFilter < nFilter ; idxFilter++ )
		{
			strPathName.Format( _T("%s\\%s"), strTargetFolder , astrFilters.GetAt( idxFilter ) );

			if(fnd.FindFile(strPathName, 0))
			{
				int i = 1;
				while(i)
				{
					i = fnd.FindNextFile();

					// ファイル名が"."か".."の場合は次を検索
					if( fnd.IsDots() )
					{
						continue;
					}

					// ファイル名取得
					// GetFilePath()にはバグがあり正確に取得できない場合があるので使わない
					CString strFindFile = strTargetFolder;
					CString	strFileName = fnd.GetFileName();
					strFindFile.TrimRight('\\');
					strFindFile += _T("\\") + strFileName;

					// ディレクトリは無視
					if ( fnd.IsDirectory() )
					{
						continue;
					}
					pProgress->SetTextFileName( strFileName );

					// ファイルを１行づつ読んで、該当文字列を含むかを検査する
					long	lHitRow( 0 );
					long	lHitColumn( 0 );
					CString strHit;

					nHit += FindFromFile(
						strFindFile,
						pProgress,
						strSubString,
						bRegExp,			//正規表現フラグ		[-] (TRUE:ON、FALSE:OFF)
						bCaseCare,			//大小文字検索フラグ	[-] (TRUE:ON、FALSE:OFF)
						bToken,				//単語単位検索フラグ	[-] (TRUE:ON、FALSE:OFF)
						&lHitRow,
						&lHitColumn,
						&strHit
					);

					// 中断チェック
					if ( pProgress->IsAbort() )
					{
						pProgress->NotifyStop();
						break;
					}
					nConpleteFile++;

// ▼ (3204) BGN 2004.05.10 (moralog) 外部アプリとのＩＦをWM_COPYDATAメッセージとする
					Sleep(50);
/*
// ▽ original BGN
					// メッセージがたまっていれば処理する
					MSG msg;
					while(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))
					{
						::TranslateMessage(&msg);
						DispatchMessage(&msg);
					}
// △ original END
					CMainFrame*		pFrame = ::GetMainFrame();
					CShitarabianPostCommand::GetInstance()->Send(
						pFrame->m_hWnd,
						pFrame->m_hWnd,
						COMMAND_MESSAGE_NOP,
						NULL,
						0
					);
*/
// ▲ (3204) END
				}
				fnd.Close();
			}
		}
	}
}

/*-----------------------------------------------------------------------------
[名称]	ファイルから該当文字検索
[機能]	ファイルを１行づつ読んで、該当文字列を含むかを検査するる
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
long CGrep::FindFromFile(
	CString				strFindFile,		// <I> 検索対象ファイル
	CGrepProgressDlg*	pProgress,			// <I> プログレスダイアログ
	CString				strSubString,		// <I> 検索文字列
	BOOL				bRegExp,			// <I> 正規表現フラグ		[-] (TRUE:ON、FALSE:OFF)
	BOOL				bCaseCare,			// <I> 大小文字検索フラグ	[-] (TRUE:ON、FALSE:OFF)
	BOOL				bToken,				// <I> 単語単位検索フラグ	[-] (TRUE:ON、FALSE:OFF)
	long				*plHitRow,			// <O> ヒットした行			[-]
	long				*plHitColumn,		// <O> ヒットしたカラム		[-]
	CString				*pstrHit			// <O> ヒット情報
)
{
	BOOL	bHit( FALSE );
	long	lHitRow( 0 );
	long	lHitColumn( 0 );
	CString	strHit;
	long	nHit( 0 );
	{
		long		nRow = GetFileRow( strFindFile );
		CStdioFile	file;
		CString		strReadLine;

		if ( file.Open( strFindFile, CFile::modeRead ) )
		{
			long	idxRow( 0 );

			while ( file.ReadString( strReadLine ) )
			{
				// 中断チェック
				if ( pProgress->IsAbort() )
				{
					break;
				}
				pProgress->SetTextFileProgress( nRow , idxRow );

				// HIT位置情報を得る
				TMatch	matched;
				CFinder*	pFinder = ::GetFinder( bRegExp );
				bHit = pFinder->QuickMatch(
					strReadLine,
					strSubString,
					&matched,
					bCaseCare,
					bToken
				);

				if ( bHit )
				{
					lHitRow = idxRow;
					lHitColumn = matched.idxStartColumn;
					strHit = strReadLine;

					// ファイル名、行、表示
					if ( bHit )
					{
					CShitarabianDoc*	pDoc = ::GetDocument();
						CString				strLine;
						strLine.Format( _T( "%s(%d,%d) : %s%s"), strFindFile, lHitRow+1, lHitColumn, strHit, pDoc->ReturnChar() );
						if ( SendInsertString( strLine ) )
						{
							nHit++;
							m_nHit++;
							pProgress->SetTextHit( m_nHit );
						}
					}
				}
				idxRow++;
			}
		}
	}
	*plHitRow = lHitRow;
	*plHitColumn = lHitColumn;
	*pstrHit = strHit;
	return nHit;
}

/*-----------------------------------------------------------------------------
[名称]	行数取得
[機能]	ファイルの行数を取得する
[戻値]	long 行数 [-] (-)
[備考]	なし
-----------------------------------------------------------------------------*/
long CGrep::GetFileRow(
	CString&	strFilePath		// <I> ファイル名
)
{
	long nRow( 0 );
	CStdioFile	file;
	CString		strDummy;

	if ( file.Open( strFilePath, CFile::modeRead ) )
	{
		while ( file.ReadString( strDummy ) )
		{
			nRow++;
		}
	}

	return nRow;
}
