// ★コメント未記入
// FinderVBScript.cpp: CFinderVBScript クラスのインプリメンテーション
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "shitarabian.h"

#include "FinderVBScript.h"
#include "ShitarabianDoc.h"
#include "GeneralSetting.h"
#include "ExeCommon.h"

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

/*
βエンドルフィン
http://www.aa.alpha-net.ne.jp/bosuke/vc/vbreg.htm
Windows Programing Tips
http://vision.kuee.kyoto-u.ac.jp/~nob/doc/win32/win32.html
*/


VARIANT_BOOL BoolToVariant( BOOL bValue )
{
	if( bValue )
	{
		return( (VARIANT_BOOL)VARIANT_TRUE );
	}
	else
	{
		return( (VARIANT_BOOL)VARIANT_FALSE );
	}
}
//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////

CFinderVBScript::CFinderVBScript()
{
	m_bAlive = ( SUCCEEDED( m_RegExp.CreateInstance(CLSID_RegExp) ) );
}

CFinderVBScript::~CFinderVBScript()
{

}


BOOL	CFinderVBScript::IsAlive()
{
	return( m_bAlive );
}

CString	CFinderVBScript::Name()
{
	return("VBScript Reguler Expression");
}


// 行から文字列を検索
BOOL CFinderVBScript::FindAtLine(
	CString&			strTarget,
	long				idxStartColumn,
	long				idxEndColumn,
	CString&			strSubStringSrc,
	long*				pidxFoundCaretColumn,
	long*				pnCaretColumn,
	BOOL				bCaseCare,
	BOOL				bToken
)
{	// warning C4100:警告の無視
	bToken;

	BOOL	bMatch = FALSE;
	if ( m_bAlive )
	{
		// 全文検索
		m_RegExp->put_Global( (VARIANT_BOOL)VARIANT_TRUE );

		// 大文字小文字区別
		m_RegExp->put_IgnoreCase( BoolToVariant(!bCaseCare) );

		//パターンをセット
		_bstr_t		bstr = strSubStringSrc;
		m_RegExp->put_Pattern( bstr );

		// 検索対象
		_bstr_t bstrSrc = strTarget;

		IDispatch* pDispatch = NULL;
		if ( SUCCEEDED(m_RegExp->Execute(bstrSrc,&pDispatch)) )
		{
			//マッチしたものの情報を取り出す
			IMatchCollection* pCollection = NULL;
			if(SUCCEEDED(pDispatch->QueryInterface(IID_IMatchCollection,(void**)&pCollection)))
			{
				LONG	nMatch = 0;
				pCollection->get_Count(&nMatch);

				int		idxMatch = 0;
				for( idxMatch=0 ; idxMatch<nMatch ; idxMatch++ )
				{
					IDispatch* pDispMatch = NULL;
					IMatch* pMatch = NULL;
					pCollection->get_Item( idxMatch, &pDispMatch );
					if ( pDispMatch != NULL )
					{
						if ( SUCCEEDED(pDispMatch->QueryInterface(IID_IMatch,(void**)&pMatch)) )
						{
							pMatch->get_FirstIndex( pidxFoundCaretColumn );
							pMatch->get_Length( pnCaretColumn );
							pMatch->Release();

							if( idxStartColumn <= *pidxFoundCaretColumn )
							if( idxEndColumn >= *pidxFoundCaretColumn + *pnCaretColumn )
							{
								bMatch = TRUE;
							}
						}
						pDispMatch->Release();
					}
					if( bMatch )
					{
						break;
					}
				}
				pCollection->Release();
			}
			pDispatch->Release();
		}
	}
	return( bMatch ); 
}



// 行から文字列を検索
long CFinderVBScript::GetMatchz(
	CString&					strSrc,
	CString&					strSubString,
	CArray<TMatch, TMatch& >*	paMatchz,
	BOOL						bCaseCare,
	BOOL						bToken,
	BOOL						bNeedSubMatch
)
{	// warning C4100:警告の無視
	bToken;

	paMatchz->RemoveAll();
	if ( m_bAlive )
	{
		// 全文検索
		m_RegExp->put_Global( (VARIANT_BOOL)VARIANT_TRUE );

		// 大文字小文字区別
		m_RegExp->put_IgnoreCase( BoolToVariant(!bCaseCare) );

		//パターンをセット
		_bstr_t		bstr = strSubString;
		m_RegExp->put_Pattern( bstr );

		// 検索対象
		_bstr_t bstrSrc = strSrc;

		IDispatch* pDispatch = NULL;
		if ( SUCCEEDED(m_RegExp->Execute(bstrSrc,&pDispatch)) )
		{
			//マッチしたものの情報を取り出す
			IMatchCollection* pCollection = NULL;
			if(SUCCEEDED(pDispatch->QueryInterface(IID_IMatchCollection,(void**)&pCollection)))
			{
				LONG	nMatch = 0;
				pCollection->get_Count(&nMatch);

				int		idxMatch = 0;
				for( idxMatch=0 ; idxMatch<nMatch ; idxMatch++ )
				{
					IDispatch* pDispMatch = NULL;
					IMatch* pMatch = NULL;
					pCollection->get_Item( idxMatch, &pDispMatch );
					if ( pDispMatch != NULL )
					{
						if ( SUCCEEDED(pDispMatch->QueryInterface(IID_IMatch,(void**)&pMatch)) )
						{
							if ( bNeedSubMatch )
							{
								// マッチ情報
								TMatch	match;
								BOOL	bSubmatchExist = GetSubMatch(
									strSrc,
									pDispMatch,
									&match,
									bCaseCare,
									bToken
								);
								if ( !bSubmatchExist )
								{
									// サブマッチが存在しなかったので
									// マッチ文字列を取得する
									BSTR	bstr;
									if ( SUCCEEDED( pMatch->get_Value(&bstr) ) )
									{
										match.strSubMatch = CString( bstr );
									}

									pMatch->get_FirstIndex( &match.idxStartColumn );
									pMatch->get_Length( &match.nColumn );
								}
								paMatchz->Add( match );
							}
							else
							{
								// マッチ情報
								TMatch	match;
								pMatch->get_FirstIndex( &match.idxStartColumn );
								pMatch->get_Length( &match.nColumn );
								if ( bNeedSubMatch )
								{
									BSTR	bstr;
									pMatch->get_Value( &bstr );
									CString	str( bstr );
									match.strSubMatch = str;
								}
								paMatchz->Add( match );
							}
						}
						pMatch->Release();
					}
					pDispMatch->Release();
				}
				pCollection->Release();
			}
			pDispatch->Release();
		}
	}
	return( paMatchz->GetSize() ); 
}



// 行から文字列を検索
/*-----------------------------------------------------------------------------
[名称]	文字列検索
[機能]	対照文字列から任意の文字列をひとつ検索する。
[戻値]	BOOL	検索結果 [-] (TRUE:マッチ、FALSE:マッチしない)
[備考]	複数行強調表示色の終端文字列の検索に使用する。
-----------------------------------------------------------------------------*/
BOOL CFinderVBScript::QuickMatch(
	LPCTSTR			strSrc,			// I  :検索対象文字列 [-] (-) :-
	CString&		strSubString,	// I  :検索文字列 [普通の文字列] (-) :-
	TMatch*			pMatch,			//   O:マッチ情報 [-] (-) :-
	BOOL			bCaseCare,		// I  :大文字小文字区別 [-] (TRUE:する、FALSE:しない) :-
	BOOL			bToken			// I  :単語単位 [-] (TRUE:する、FALSE:しない) :-
)
{	// warning C4100:警告の無視
	bToken;

	BOOL	bFound = FALSE;
	if ( m_bAlive )
	{
		// 全文検索
		m_RegExp->put_Global( (VARIANT_BOOL)VARIANT_FALSE );

		// 大文字小文字区別
		m_RegExp->put_IgnoreCase( BoolToVariant(!bCaseCare) );

		//パターンをセット
		_bstr_t		bstr = strSubString;
		m_RegExp->put_Pattern( bstr );

		// 検索対象
		_bstr_t bstrSrc = strSrc;

		// 検索処理
		IDispatch* pDispatch = NULL;
		if ( SUCCEEDED(m_RegExp->Execute(bstrSrc,&pDispatch)) )
		{
			//マッチしたものの情報を取り出す
			IMatchCollection* pCollection = NULL;
			if(SUCCEEDED(pDispatch->QueryInterface(IID_IMatchCollection,(void**)&pCollection)))
			{
				long	nMatch = 0;
				pCollection->get_Count(&nMatch);
				if( nMatch > 0 )
				{
					// 先頭のひとつだけを取り出す
					IDispatch* pDispMatch = NULL;
					IMatch* pMatchItem = NULL;
					pCollection->get_Item( 0, &pDispMatch );
					if ( pDispMatch != NULL )
					{
						if ( SUCCEEDED(pDispMatch->QueryInterface(IID_IMatch,(void**)&pMatchItem)) )
						{
							// マッチ情報の取得
							pMatchItem->get_FirstIndex( &pMatch->idxStartColumn );
							pMatchItem->get_Length( &pMatch->nColumn );
							pMatchItem->Release();

							bFound = TRUE;
						}
						pDispMatch->Release();
					}
				}
				pCollection->Release();
			}
			pDispatch->Release();
		}
	}
	return( bFound ); 
}


CString CFinderVBScript::GetReplaceWord(
	CShitarabianDoc*	pDoc,
	long				idxRow,
	long				idxStartColumn,
	long				idxEndColumn,
	CString&			strSubString,
	CString&			strReplaceFormat,
	BOOL				bCaseCare,
	BOOL				bToken
)
{
	// warning C4100:警告の無視
	bToken; bCaseCare; strSubString; idxEndColumn; idxStartColumn; idxRow; pDoc;

	CString	strDst;

	const char*	pstrSrc = (LPCTSTR)strReplaceFormat;
	while( *pstrSrc != 0x00 )
	{
		switch( *pstrSrc )
		{
		case '\\':
			switch( *(pstrSrc+1) )
			{
			case 'n':
				strDst += pDoc->ReturnChar();
				break;
			case 't':
				if ( CGeneralSetting::GetInstance()->GetTabConvert() )
				{
					strDst += CGeneralSetting::GetInstance()->GetTabString();
				}
				else
				{
					strDst += "\t";
				}
				break;
			case '\\':
				strDst += "\\";
				break;
			default:
				strDst += "\\";
				strDst += *(pstrSrc+1);
				break;
			}
			pstrSrc+=2;
			break;

		default:
			if ( _istleadbyte( *pstrSrc ) != 0 )
			{
				strDst += *(pstrSrc);
				strDst += *(pstrSrc+1);
				pstrSrc+=2;
			}
			else
			{
				strDst += *(pstrSrc);
				pstrSrc++;
			}
			break;
		}
	}

	return( strDst ); 
} 





// サブマッチを取得してみる
BOOL	CFinderVBScript::GetSubMatch(
	CString&	strSrc,
	IDispatch*	pDispMatch,
	TMatch*		pSubMatch,
	BOOL		bCaseCare,
	BOOL		bToken
)
{
	BOOL	bSubMatchExists = FALSE;

	IMatch2* pMatch2 = NULL;
	if ( SUCCEEDED(pDispMatch->QueryInterface(IID_IMatch2,(void**)&pMatch2)) )
	{
		// $1を取得する
		IDispatch* pDispSubMatches = NULL;
		ISubMatches* pSubMatches = NULL;
		pMatch2->get_SubMatches( &pDispSubMatches );
		if ( SUCCEEDED(pDispSubMatches->QueryInterface(IID_ISubMatches,(void**)&pSubMatches)) )
		{
			// マッチ情報取得
			CString		strSubMatch;
			{
				// サブマッチを取得
				long		nSubMatch;
				pSubMatches->get_Count( &nSubMatch );
				if ( nSubMatch > 0 )
				{
					VARIANT		vSubMatch;
					if ( SUCCEEDED( pSubMatches->get_Item( 0, &vSubMatch ) ) )
					if ( vSubMatch.vt == VT_BSTR )
					{
						strSubMatch = CString( vSubMatch.bstrVal );

						TMatch	match;
						pMatch2->get_FirstIndex( &match.idxStartColumn );
						pMatch2->get_Length( &match.nColumn );
						// サブマッチの位置を得る
						TMatch			SubMatch;
						BOOL bSubStringExist = QuickMatch(
							(LPCTSTR)strSrc + ::CaretColumnToByteColumn( (LPCTSTR)strSrc, match.idxStartColumn ),
							strSubMatch,
							&SubMatch,
							bCaseCare,
							bToken
						);
						if ( bSubStringExist )
						{
							pSubMatch->idxStartColumn = SubMatch.idxStartColumn + match.idxStartColumn;
							pSubMatch->nColumn = SubMatch.nColumn;
							pSubMatch->strSubMatch = strSubMatch;

							bSubMatchExists = ( match.idxStartColumn+match.nColumn <= match.nColumn );
						}
					}
				}
			}
		}
		pMatch2->Release();
	}
	return( bSubMatchExists );
}