/*-----------------------------------------------------------------------------
[名称]	Finder.cpp
[概要]	検索クラスの実装
[備考]	なし
[履歴]	2003-02-25	NC		コメント付加(moralog)
-----------------------------------------------------------------------------*/


//-----------------------------------------------------------------------------
// インクルード
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "Shitarabian.h"
#include "Finder.h"
#include "ShitarabianDoc.h"
#include "ShitarabianView.h"
#include "Selection.h"
#include "CommandCaret.h"
#include "CommandDelete.h"
#include "CommandManager.h"
#include "CommandInput.h"
#include "GeneralSetting.h"
#include "EmphasisDefine.h"
#include "TokenReader.h"
#include "BregPool.h"
#include "ExeCommon.h"


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


/*-----------------------------------------------------------------------------
[名称]	コンストラクタ
[機能]	インスタンスの生成。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
CFinder::CFinder()
{

}


/*-----------------------------------------------------------------------------
[名称]	デストラクタ
[機能]	インスタンスの破棄。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
CFinder::~CFinder()
{

}


/*-----------------------------------------------------------------------------
[名称]	文字列検索→選択
[機能]	文字列を検索し、見つかれば選択する。
[戻値]	なし
[備考]	見つからなければダイアログを表示する。
-----------------------------------------------------------------------------*/
BOOL CFinder::FindNextForSelect(
	CShitarabianDoc*	pDoc,			// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	CString&			strSubString,	// I  :検索文字列 [-] (-) :-
	BOOL				bLoop,			// I  :上下循環 [-] (TRUE:循環する、FALSE:循環しない) :-
	BOOL				bCaseCare,		// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken			// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	// 検索処理
	long	idxFoundCaretRow;
	long	idxFoundCaretColumn;
	long	nCaretColumn;
	BOOL	bFound = FindNext(
		pDoc,
		strSubString,
		&idxFoundCaretRow,
		&idxFoundCaretColumn,
		&nCaretColumn,
		bLoop,
		bCaseCare,
		bToken
	);

	// 見つかればキャレットを移動して選択、
	// 見つからなければ警告
	if ( bFound )
	{
		// 選択
		pDoc->GetSelection()->SetSelectedStart( idxFoundCaretRow, idxFoundCaretColumn );
		pDoc->GetSelection()->SetSelectedEnd( idxFoundCaretRow, idxFoundCaretColumn+nCaretColumn );

		// キャレットの移動
		CCommandCaret	cmdCaret;
		cmdCaret.SetAttribute( 0x00, idxFoundCaretRow, idxFoundCaretColumn+nCaretColumn );
		cmdCaret.Execute( pDoc );
	}
	else
	{
		CString		strMes;
		strMes.Format( "\"%s\"が見つかりません。", strSubString );
		MessageBox(
			pDoc->GetMainView()->m_hWnd,
			strMes,
			"したらびあん",
			MB_OK
		);
	}

	// 検索文字列を強調表示するため描画情報の再構築する
	CArray<CEmphasisItem, CEmphasisItem&>& Itemz = CGeneralSetting::GetInstance()->GetEmphasisDefine()->GetItemz();
	CEmphasisItem&	Emphasis = Itemz.ElementAt(EMPHASIS_FOUND);
	if ( strSubString != Emphasis.GetPattern() )
	{
		Emphasis.SetPattern( strSubString );

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

		pDoc->UpdateEmphasis();
	}

	// メインウィンドウの再描画
	pDoc->GetMainView()->ScrollToCaret();
	pDoc->SetAllUpdate();
	pDoc->ReDraw();

	return( bFound );
}


/*-----------------------------------------------------------------------------
[名称]	文字列検索IF
[機能]	文書全体を検索する。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチした、FALSE:マッチしなかった)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CFinder::FindNext(
	CShitarabianDoc*	pDoc,					// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	CString&			strSubString,			// I  :検索文字列 [-] (-) :-
	long*				pidxFoundCaretRow,		//   O:マッチ行 [-] (-) :-
	long*				pidxFoundCaretColumn,	//   O:マッチカラム [-] (-) :-
	long*				pnCaretColumn,			//   O:マッチカラム数 [-] (-) :-
	BOOL				bLoop,					// I  :上下循環 [-] (TRUE:循環する、FALSE:循環しない) :-
	BOOL				bCaseCare,				// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken					// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	BOOL	bFond;

	bFond = FindNextAtDocument(
		pDoc,
		strSubString,
		pidxFoundCaretRow,
		pidxFoundCaretColumn,
		pnCaretColumn,
		bLoop,
		bCaseCare,
		bToken
	);

	return( bFond );
}


/*-----------------------------------------------------------------------------
[名称]	文字列検索（文書全体）
[機能]	文書全体を検索する。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチした、FALSE:マッチしなかった)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CFinder::FindNextAtDocument(
	CShitarabianDoc*	pDoc,					// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	CString&			strSubString,			// I  :検索文字列 [-] (-) :-
	long*				pidxFoundCaretRow,		//   O:マッチ行 [-] (-) :-
	long*				pidxFoundCaretColumn,	//   O:マッチカラム [-] (-) :-
	long*				pnCaretColumn,			//   O:マッチカラム数 [-] (-) :-
	BOOL				bLoop,					// I  :上下循環 [-] (TRUE:循環する、FALSE:循環しない) :-
	BOOL				bCaseCare,				// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken					// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	BOOL	bFound;

	// 選択範囲の先頭＋１から検索を開始する
	long	idxStartRow = pDoc->GetSelection()->GetSelectedStartRow();
	long	idxStartColumn = pDoc->GetSelection()->GetSelectedStartColumn();
	long	idxEndRow = pDoc->GetSelection()->GetSelectedEndRow();
	long	idxEndColumn = pDoc->GetSelection()->GetSelectedEndColumn();
	{
		bFound = FindNextAtLinez(
			pDoc,
			idxEndRow,
			idxEndColumn,
			pDoc->GetLineCount()-1,
			INT_MAX,
			strSubString,
			pidxFoundCaretRow,
			pidxFoundCaretColumn,
			pnCaretColumn,
			bCaseCare,
			bToken
		);
	}

	// 見つからなかった場合、ファイルの先頭から検索
	if ( !bFound )
	if ( bLoop )
	{
		bFound = FindNextAtLinez(
			pDoc,
			0,
			0,
			idxStartRow,
			idxStartColumn,
			strSubString,
			pidxFoundCaretRow,
			pidxFoundCaretColumn,
			pnCaretColumn,
			bCaseCare,
			bToken
		);
	}

	return( bFound );
}


/*-----------------------------------------------------------------------------
[名称]	文字列検索（文書一部）
[機能]	文書一部を検索する。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチした、FALSE:マッチしなかった)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CFinder::FindNextAtLinez(
	CShitarabianDoc*	pDoc,				// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	long				idxStartRow,		// I  :検索開始行 [-] (-) :-
	long				idxStartColumn,		// I  :検索開始カラム [-] (-) :-
	long				idxEndRow,			// I  :検索終了行 [-] (-) :-
	long				idxEndColumn,		// I  :検索終了カラム [-] (-) :-
	CString&			strSubString,		// I  :検索文字列 [-] (-) :-
	long*				pidxFoundCaretRow,	//   O:マッチ行 [-] (-) :-
	long*				pidxFoundCaretColumn,//   O:マッチカラム [-] (-) :-
	long*				pnCaretColumn,		//   O:マッチカラム数 [-] (-) :-
	BOOL				bCaseCare,			// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken				// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	BOOL	bFound = FALSE;

	int			idxRow;
	for( idxRow=idxStartRow ; idxRow<=idxEndRow ; idxRow++ ) // 行のループ
	{
		long	lStartColumn = 0;
		if ( idxRow == idxStartRow )
		{
			lStartColumn = idxStartColumn;
		}

		long	lEndColumn  = INT_MAX;
		if ( idxRow == idxEndRow )
		{
			lEndColumn = idxEndColumn;
		}

		bFound = FindAtLine(
			*(pDoc->GetLine( idxRow )),
			lStartColumn,
			lEndColumn,
			strSubString,
			pidxFoundCaretColumn,
			pnCaretColumn,
			bCaseCare,
			bToken
		);
		if ( bFound )
		{
			*pidxFoundCaretRow = idxRow;
			break;
		}
	}

	return( bFound );
}


/*-----------------------------------------------------------------------------
[名称]	文字列検索→選択
[機能]	文字列を検索し、見つかれば選択する。
[戻値]	なし
[備考]	見つからなければダイアログを表示する。
-----------------------------------------------------------------------------*/
BOOL CFinder::FindPrevForSelect(
	CShitarabianDoc*	pDoc,			// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	CString&			strSubString,	// I  :検索文字列 [-] (-) :-
	BOOL				bLoop,			// I  :上下循環 [-] (TRUE:循環する、FALSE:循環しない) :-
	BOOL				bCaseCare,		// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken			// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	long	idxFoundCaretRow;
	long	idxFoundCaretColumn;
	long	nCaretColumn;
	BOOL	bFound = FindPrev(
		pDoc,
		strSubString,
		&idxFoundCaretRow,
		&idxFoundCaretColumn,
		&nCaretColumn,
		bLoop,
		bCaseCare,
		bToken
	);

	if ( bFound )
	{
		// 選択
		pDoc->GetSelection()->SetSelectedStart( idxFoundCaretRow, idxFoundCaretColumn );
		pDoc->GetSelection()->SetSelectedEnd( idxFoundCaretRow, idxFoundCaretColumn+nCaretColumn );

		// キャレットの移動
		CCommandCaret	cmdCaret;
		cmdCaret.SetAttribute( 0x00, idxFoundCaretRow, idxFoundCaretColumn+nCaretColumn );
		cmdCaret.Execute( pDoc );
	}
	else
	{
		CString		strMes;
		strMes.Format( "\"%s\"が見つかりません。", strSubString );
		MessageBox(
			pDoc->GetMainView()->m_hWnd,
			strMes,
			"したらびあん",
			MB_OK
		);
	}

	// 描画情報の再構築
	CArray<CEmphasisItem, CEmphasisItem&>& Itemz = CGeneralSetting::GetInstance()->GetEmphasisDefine()->GetItemz();
	CEmphasisItem&	Emphasis = Itemz.ElementAt(EMPHASIS_FOUND);
	if ( strSubString != Emphasis.GetPattern() )
	{
		Emphasis.SetPattern( strSubString );
		pDoc->UpdateEmphasis();
	}

	// メインウィンドウの再描画
	pDoc->GetMainView()->ScrollToCaret();
	pDoc->SetAllUpdate();
	pDoc->ReDraw();

	return( bFound );
}


/*-----------------------------------------------------------------------------
[名称]	文字列検索IF
[機能]	文書全体を検索する。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチした、FALSE:マッチしなかった)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CFinder::FindPrev(
	CShitarabianDoc*	pDoc,					// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	CString&			strSubString,			// I  :検索文字列 [-] (-) :-
	long*				pidxFoundCaretRow,		//   O:マッチ行 [-] (-) :-
	long*				pidxFoundCaretColumn,	//   O:マッチカラム [-] (-) :-
	long*				pnCaretColumn,			//   O:マッチカラム数 [-] (-) :-
	BOOL				bLoop,					// I  :上下循環 [-] (TRUE:循環する、FALSE:循環しない) :-
	BOOL				bCaseCare,				// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken					// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	BOOL	bFond;

	bFond = FindPrevAtDocument(
		pDoc,
		strSubString,
		pidxFoundCaretRow,
		pidxFoundCaretColumn,
		pnCaretColumn,
		bLoop,
		bCaseCare,
		bToken
	);

	return( bFond );
}


/*-----------------------------------------------------------------------------
[名称]	文字列検索（文書全体）
[機能]	文書全体を検索する。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチした、FALSE:マッチしなかった)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CFinder::FindPrevAtDocument(
	CShitarabianDoc*	pDoc,					// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	CString&			strSubString,			// I  :検索文字列 [-] (-) :-
	long*				pidxFoundCaretRow,		//   O:マッチ行 [-] (-) :-
	long*				pidxFoundCaretColumn,	//   O:マッチカラム [-] (-) :-
	long*				pnCaretColumn,			//   O:マッチカラム数 [-] (-) :-
	BOOL				bLoop,					// I  :上下循環 [-] (TRUE:循環する、FALSE:循環しない) :-
	BOOL				bCaseCare,				// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken					// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	// 選択位置直前から検索を開始
	BOOL	bFound;

	long	idxStartRow = pDoc->GetSelection()->GetSelectedStartRow();
	long	idxStartColumn = pDoc->GetSelection()->GetSelectedStartColumn();
	long	idxEndRow = pDoc->GetSelection()->GetSelectedEndRow();
	long	idxEndColumn = pDoc->GetSelection()->GetSelectedEndColumn();
	{
		bFound = FindPrevAtLinez(
			pDoc,
			0,
			0,
			idxStartRow,
			idxStartColumn,
			strSubString,
			pidxFoundCaretRow,
			pidxFoundCaretColumn,
			pnCaretColumn,
			bCaseCare,
			bToken
		);
	}

	// 見つからなかった場合、ファイルの末尾から検索
	if ( !bFound )
	if ( bLoop )
	{
		bFound = FindPrevAtLinez(
			pDoc,
			idxEndRow,
			idxEndColumn,
			pDoc->GetLineCount()-1,
			INT_MAX,
			strSubString,
			pidxFoundCaretRow,
			pidxFoundCaretColumn,
			pnCaretColumn,
			bCaseCare,
			bToken
		);
	}

	return( bFound );
}


/*-----------------------------------------------------------------------------
[名称]	文字列検索（文書一部）
[機能]	文書一部を検索する。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチした、FALSE:マッチしなかった)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CFinder::FindPrevAtLinez(
	CShitarabianDoc*	pDoc,				// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	long				idxStartRow,		// I  :検索開始行 [-] (-) :-
	long				idxStartColumn,		// I  :検索開始カラム [-] (-) :-
	long				idxEndRow,			// I  :検索終了行 [-] (-) :-
	long				idxEndColumn,		// I  :検索終了カラム [-] (-) :-
	CString&			strSubString,		// I  :検索文字列 [-] (-) :-
	long*				pidxFoundCaretRow,	//   O:マッチ行 [-] (-) :-
	long*				pidxFoundCaretColumn,//   O:マッチカラム [-] (-) :-
	long*				pnCaretColumn,		//   O:マッチカラム数 [-] (-) :-
	BOOL				bCaseCare,			// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken				// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	BOOL	bFound = FALSE;

	// 選択範囲の先頭＋１から検索を開始する
	int			idxRow;
	for( idxRow=idxEndRow ; idxRow>=idxStartRow ; idxRow-- ) // 行のループ
	{
		long	lStartColumn = 0;
		if ( idxRow == idxStartRow )
		{
			lStartColumn = idxStartColumn;
		}

		long	lEndColumn  = INT_MAX;
		if ( idxRow == idxEndRow )
		{
			lEndColumn = idxEndColumn;
		}

		bFound = FindLastAtLine(
			pDoc,
			idxRow,
			lStartColumn,
			lEndColumn,
			strSubString,
			pidxFoundCaretColumn,
			pnCaretColumn,
			bCaseCare,
			bToken
		);
		if ( bFound )
		{
			*pidxFoundCaretRow = idxRow;
			break;
		}
	}

	return( bFound );
}


/*-----------------------------------------------------------------------------
[名称]	最後尾マッチ検索（行）
[機能]	行中でマッチする最後のものを検索。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチした、FALSE:マッチしなかった)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CFinder::FindLastAtLine(
	CShitarabianDoc*	pDoc,				// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	long				idxRow,				// I  :検索開始行 [-] (-) :-
	long				idxStartColumn,		// I  :検索開始カラム [-] (-) :-
	long				idxEndColumn,		// I  :検索終了カラム [-] (-) :-
	CString&			strSubString,		// I  :検索文字列 [-] (-) :-
	long*				pidxFoundCaretColumn,//   O:マッチカラム [-] (-) :-
	long*				pnCaretColumn,		//   O:マッチカラム数 [-] (-) :-
	BOOL				bCaseCare,			// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken				// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	BOOL	bFound = FALSE;
	while( TRUE )
	{
		long		idxFoundCaretColumn;
		long		nCaretColumn;
		BOOL bContinue = FindAtLine(
			*(pDoc->GetLine( idxRow )),
			idxStartColumn,
			idxEndColumn,
			strSubString,
			&idxFoundCaretColumn,
			&nCaretColumn,
			bCaseCare,
			bToken
		);

		if ( !bContinue )
		{
			return( bFound );
		}
		bFound = TRUE;
		idxStartColumn = idxFoundCaretColumn + nCaretColumn;
		*pidxFoundCaretColumn = idxFoundCaretColumn;
		*pnCaretColumn = nCaretColumn;
	}
}


/*-----------------------------------------------------------------------------
[名称]	正規表現補完
[機能]	正規表現パターンをコンボボックスのエディットに出力する。
[戻値]	なし
[備考]	例：選択パターン"[]"を"["と"]"に分けて情報を持っている。
		コンボボックス上には"[]"と出力し、間にキャレットを置く。
-----------------------------------------------------------------------------*/
void CFinder::RegExpGuide(
	CComboBox*	pCombo,				//   O:コンボボックス [-] (-) :-
	CString		strLeft,			// I  :左端文字列 [-] (-) :-
	CString		strRight			// I  :右端文字列 [-] (-) :-
)
{
	pCombo->GetParent()->UpdateData( FALSE );
	CString		strText;
	pCombo->GetWindowText( strText );
	int		idxCaret = strText.GetLength() + strLeft.GetLength();

	CString		strNewText;
	strNewText.Format("%s%s%s", strText, strLeft, strRight );
	pCombo->SetWindowText( strNewText );

	pCombo->GetParent()->UpdateData( TRUE );
	pCombo->SetFocus();
	pCombo->SetEditSel ( idxCaret, idxCaret );
}


/*-----------------------------------------------------------------------------
[名称]	置換
[機能]	選択部分の置換処理。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
long CFinder::Replace(
	CShitarabianDoc*	pDoc,				// I  :ﾄﾞｷｭﾒﾝﾄ [-] (-) :-
	long				idxRow,				// I  :検索開始行 [-] (-) :-
	long				idxStartColumn,		// I  :検索開始カラム [-] (-) :-
	long				idxEndColumn,		// I  :検索終了カラム [-] (-) :-
	CString&			strSubString,		// I  :検索文字列 [-] (-) :-
	CString&			strReplaceFormat,	// I  :置換パターン [-] (-) :-
	BOOL				bCaseCare,			// I  :大文字小文字区別 [-] (TRUE:区別する、FALSE:区別しない) :-
	BOOL				bToken				// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{
	// 置換文字列の取得
	CString		strReplace;
	{
		strReplace = GetReplaceWord(
			pDoc,
			idxRow,
			idxStartColumn,
			idxEndColumn,
			strSubString,
			strReplaceFormat,
			bCaseCare,
			bToken
		);
	}

	// Hit範囲の削除
	{
		// 削除コマンドの実行・登録
		CCommandDelete	cmdDelete;
		cmdDelete.SetAttribute(
			idxRow, idxStartColumn,
			idxRow, idxEndColumn
		);
		cmdDelete.Execute( pDoc );

		CCommandManager*	pCmdManager = CCommandManager::GetInstance();
		pCmdManager->Add( cmdDelete );
	}

	{
		// コマンドの登録
		CCommandInput	cmdInput;
		cmdInput.SetAttribute( strReplace, idxRow, idxStartColumn );
		cmdInput.Execute( pDoc );

		CCommandManager*	pCmd = CCommandManager::GetInstance();
		pCmd->Add( cmdInput );
	}

	return( ::StringToColumn( strReplace ) );
}
