////////////////////////////////////////////////////////////////////////////////
//	置換
////////////////////////////////////////////////////////////////////////////////
#define	SW_MODULE_REPLACE

#define	STRINGS_MAX			256			//	変換元、先の最大文字数

#define	OUT_ALLOC			3			// OUT_ALLOC倍のメモリを確保
#define	ANNOTATE			TEXT("//R:")


//----------------------------------
//		
//----------------------------------
struct	REP_LIST{
	TCHAR	di[ STRINGS_MAX ];
	TCHAR	si[ STRINGS_MAX ];
};


//		
class REPLASE
{
private:

	BOOL	checkBorder( LPCTSTR pPtr );
	
	int		replace( LPTSTR in_ptr, int iLen, LPTSTR out_ptr,  const int iOutLen);
	int		get_str( LPCTSTR _pPtr,int ii, int iLen, LPTSTR _pStr, BOOL _bFlg );
	void	chg_tr( LPTSTR  ptr );
	int		check( LPCTSTR _pInPtr, const int _iOffset );
	int		copy( LPCTSTR in, int &i, LPTSTR out, const int iOutLen, int &size, const int idx );
	int		command( LPTSTR ptr );
	
	void	comment_clear();

public:
	REP_LIST		*m_pRep;
	int				m_iEntry;
	
	BOOL			m_bErr;
	
	int				m_iReplaced;			///< 置換数
	
	int				m_iCount;
	
	TCHAR			m_cIniname[_MAX_PATH];
	
	BOOL			m_bFlgComment;
	BOOL			m_bPAgreement;
	TCHAR			m_cComment[STRINGS_MAX*2];
	
	
	void	Init( LPCTSTR _pInitName );
	void	Quit( BOOL _bExit );
	
	BOOL	Func( LPCTSTR _sParamString );
	
	REPLASE();
	~REPLASE();
	
};


int		regex( LPTSTR out, REP_LIST *rep, int entry );


/*
//	魔女っ娘特殊  Change のある行で変換した場合は　.SRC を追加する
static	void	sp_replace(u_char *ptr,char *comment){
	int		i;
	
	
	i = 0;
	while(*(ptr+i) != '(')	i--;
	while(*(ptr+i) > ' ')	i--;
	i++;
	
	if(	(*(ptr+i  ) == 'C') &&
		(*(ptr+i+1) == 'h') &&
		(*(ptr+i+2) == 'a') &&
		(*(ptr+i+3) == 'n') ){
		STRING_cat(comment,TEXT(".SRC"));
	}
	
}
*/

//		
REPLASE::REPLASE()
: m_bErr( FALSE )
, m_pRep( NULL )
, m_iCount( 0 )
, m_bFlgComment( FALSE )
, m_bPAgreement( FALSE )
{
	m_cIniname[0] = 0;
}


//		
REPLASE::~REPLASE()
{
}


//********************************************************************************************
/**
 * @brief		初期化.
 * @author		N.Takeda
 *
 * @param[in]	LPCTSTR	_pInitName	設定ファイル名.
 *
 * @return		なし (none)
 */
//********************************************************************************************
void	REPLASE::Init( LPCTSTR _pInitName )
{
	TCHAR		*pRepFile = NULL;
	Int32		iLen, ii, jj, iMax;
	REP_LIST	rep;
	
	
	if( STRING_cmp( m_cIniname, _pInitName ) == 0 )	return;		//	前回と同じ設定ファイル
	
	STRING_cpy( m_cIniname, _pInitName );
	iLen = MEM_TextRead( &pRepFile, m_cIniname );
	if( iLen == 0 )
	{
		Message_error( TEXT("no inifile.%s"), pickup_name(m_cIniname) );
		return;
	}
	
	
	/// 確保数を得る. 改行を数えているだけ、多い分には問題ない
	iMax = 0;
	for( ii=0 ; ii<iLen ; ii++ )
	{
		if(*(pRepFile+ii) == '\n')	iMax++;
	}
	
	mem_alloc( REP_LIST, m_pRep, iMax+1 );
	
	///	replaceリストを取得
	m_iEntry = ii = 0;
	while( ii < iLen )
	{
		///	変換元文字列
		ii = get_str( pRepFile, ii, iLen, m_pRep[ m_iEntry ].si, FALSE );
		chg_tr( m_pRep[ m_iEntry ].si );	///< \t\nはエスケープシーケンスコードに変換
		
		if( m_pRep[ m_iEntry ].si[0] != 0 )
		{
			//	変換先文字列
			ii = get_str( pRepFile, ii, iLen, m_pRep[ m_iEntry ].di, TRUE );
			
			if( m_pRep[ m_iEntry ].di[0] == '\\' )
			{
				if( m_pRep[ m_iEntry ].di[1] == 'n' && m_pRep[ m_iEntry ].di[2] == 'u' && m_pRep[ m_iEntry ].di[3] == 'm' )		//	num
				{
					if( m_pRep[ m_iEntry ].di[4] >= '0' && m_pRep[ m_iEntry ].di[4] <= '9' )
					{
						jj = m_iCount = 0;
						while( m_pRep[ m_iEntry ].di[ 4+jj ] >= '0' && m_pRep[ m_iEntry ].di[ 4+jj ] <= '9' )
						{
							m_iCount *= 10;
							m_iCount += ( m_pRep[ m_iEntry ].di[4+jj++] - '0' );
						}
					}else{
						_stprintf_s( m_pRep[ m_iEntry ].di, STRINGS_MAX, TEXT("%d"), m_iCount++ );
					}
				}
				
			}
			
//	DEBUG_printf(TEXT("d_rep.txt"),TEXT("%s , %s\r\n"),rep[ m_iEntry ].si,rep[ m_iEntry ].di);
			m_iEntry++;
		}else{
			m_pRep[ m_iEntry ].di[0] = 0;
		}
	}
	mem_del( pRepFile );
	
	
	/// 同じ文字が含まれてい場合、長いものから先に変換した方が都合が良い.
	for( ii=0 ; ii<m_iEntry-1 ; ii++ )
	{
		for( jj=ii+1 ; jj<m_iEntry ; jj++ )
		{
			if( STRING_len( m_pRep[ii].si ) < STRING_len( m_pRep[jj].si ) )
			{
				memcpy( &rep, &m_pRep[ii], sizeof(REP_LIST) );
				memcpy( &m_pRep[ii], &m_pRep[jj], sizeof(REP_LIST) );
				memcpy( &m_pRep[jj], &rep, sizeof(REP_LIST) );
			}
		}
	}
//	for(ii=0 ; ii<m_iEntry ; ii++)	_str(rep[ii].si);
	
	
}


void	REPLASE::Quit( BOOL _bExit )
{
	mem_del(m_pRep);
	m_cIniname[0] = 0;
	
}


//		
BOOL	REPLASE::Func( LPCTSTR _sParamString )
{
	CSwbParam	cParam( _sParamString );							///< パラメータ処理.
	
	TCHAR		*mem=NULL, *out=NULL;
	int			size, iLen, iOutLen, iSIdx;
	LPCTSTR		pInname;
	LPCTSTR		pIniName;
	LPCTSTR		pOutName;
	int			iSym;
	
	
	pInname  = cParam.GetPath();
	pIniName = cParam.GetString( 1 );
	pOutName = cParam.GetFilePath( 2 );
	if( pOutName && pOutName[0] == '-' )	pOutName = NULL;
	
	iSIdx = cParam.SearchOption( 'm' );
	if( iSIdx >= 0 )
	{
		iSym = cParam.GetIntOption( iSIdx, 'm' );
	}
	
	iSIdx = cParam.SearchOption( 'o' );
	if( iSIdx >= 0 )
	{
		if( cParam.CheckOption( iSIdx, 'p', TRUE ) )	//大文字小文字を区別しない
		{	/// レングスモード.
			m_bPAgreement = TRUE;
		}
	}
	
	
	Init( pIniName );					//	複数に対応するため毎回初期化
	
	iLen = MEM_TextRead( &mem, pInname );
	if(iLen == 0){
		Message_error( TEXT("file read error.%s"), pickup_name( pInname ) );
		return FALSE;
	}
	
	
	m_iReplaced = 0;	//	置換数初期化
	
	iOutLen = ((iLen*OUT_ALLOC+1023)/1024) * 1024;
	
	if( iSym == 2 ){		//	regex正規表現
		
		mem_alloc( TCHAR, out, iOutLen );
		
		BUF_copy( out, mem, iLen * sizeof(TCHAR) );
		size = regex( out, m_pRep, m_iEntry );
		if( size < 0)
		{
			Message_error( TEXT("正規表現が無効です。USE_BOOSTを有効にしてビルドして下さい.") );
			size = 0;
		}
		
	}else{			//	SWRITERフォーマット
		m_bFlgComment = (BOOL)iSym;
		if(m_bFlgComment == TRUE)	comment_clear();
		
		mem_alloc( TCHAR, out, iOutLen );
		*out = 0;
		
		size = replace( mem, iLen, out, iOutLen );
		
	}
	Message_printf( TEXT("%s replaced.%d"), pickup_name(pInname), m_iReplaced );
	
	
	if( !pOutName )
		file_textwrite( pInname, out );
	else
		file_textwrite( pOutName, out );
	
	
	mem_del(out);
	mem_del(mem);
	
	return TRUE;
}

#define	REPLACE_RANGE

//------------------------------------------------------------------
//		置換
//in:
//		u_char	*in_ptr
//		int		iLen
//		u_char	*out_ptr
//------------------------------------------------------------------
int		REPLASE::replace( LPTSTR in_ptr, int iLen, LPTSTR out_ptr, const int _iOutLen )
{
	int			ii,size,idx;
	
	
	if(iLen == 0)	return -1;
	
	size = 0;
	ii = 0;
	
	
#ifdef	REPLACE_RANGE
	cRange.Init( in_ptr, iLen );						//範囲限定機能を有効にするにはこの初期化と
	while(cRange.Is(in_ptr,ii,iLen))				//　初回スキップ、下の[スキップ判定]が必要。スキップが必要な間trueを返す
		*(out_ptr+size++) = *(in_ptr+ii++);
#endif
	
	
	for(ii=ii ; ii<iLen ; ii++)
	{
#ifdef	REPLACE_RANGE
		while( cRange.Is( in_ptr, ii, iLen) )			//　スキップ判定
			*(out_ptr+size++) = *(in_ptr+ii++);
#endif
		
		if((idx = check( in_ptr, ii )) != -1){	//	
			copy( in_ptr, ii, out_ptr, _iOutLen, size, idx );
			ii--;
			m_iReplaced++;
		}
		else{	//	該当しない物はそのままコピー
			
			//	改行時、コメントを挿入
			if( m_bFlgComment == TRUE )
				if( *(in_ptr+ii) == '\r' || *(in_ptr+ii) == '\n' )
				{
					if( STRING_len( m_cComment ) > 1 + STRING_len( ANNOTATE ) )	///< コメントがある.
					{
						STRING_cpy( (out_ptr+size), m_cComment );
						size += STRING_len( m_cComment );
						comment_clear();
					}
				}
			
			*(out_ptr+size++) = *(in_ptr+ii);
			*(out_ptr+size) = 0;
		}
		
		if( m_bErr )	break;
	}
	*(out_ptr+size) = 0;
	
	return size;
}


//		
int		REPLASE::command( LPTSTR ptr )
{
	TCHAR	com[][4] = {
		TEXT("n"),	//改行
		TEXT("ts"),	//複数タブ
		TEXT("t"),	//タブ
		TEXT("str"),	//""で括られた文字列
		TEXT("val"),	//数値
		TEXT("knj"),	//全角文字
		TEXT("wrd"),	//単語
		TEXT("blk"),	//ブロック
		TEXT("*")
	};
	int		ii,jj;
	
	
	ii = 0;
	while(com[ii][0] != '*'){
		
		for(jj=0 ; jj<STRING_len(com[ii]) ; jj++){
			if(*(ptr+jj) != com[ii][jj])	break;
		}
		if(jj==STRING_len(com[ii])){
			return	ii;
		}
		ii++;
	}
	
	return -1;
}


//		チェック
//			変換対象となるか否かを判断するだけなので di は参照されない
int		REPLASE::check( LPCTSTR _pInPtr, const int _iOffset )
{
	int		ii, jj, kk, iLen;
	BOOL	bFlg;
	TCHAR	ch1, ch2;
	LPTSTR	pPtr = (LPTSTR)(_pInPtr + _iOffset);
	
	
	if( m_pRep == NULL )	return -1;
	for(ii=0 ; ii<m_iEntry ; ii++)
	{
		kk = jj = 0;
		bFlg = FALSE;
		
//	_printf("-------------------\r\n");
		//	元文pPtr と 変換元m_pRep[ii].si を比較
		iLen = STRING_len( m_pRep[ii].si );
//	TCHAR cTest[256];
//	STRING_cpy( cTest, m_pRep[ii].si );
		while( jj < iLen ){	//	相違チェック
			
			ch1 = *(pPtr+kk);
			ch2 = m_pRep[ii].si[jj];
//	_printf("%c:%c\r\n",ch1,ch2);
			//	★大文字小文字変換はここ
			
			if( ch1 != ch2 ){
				
				//	相違
				if(ch2 == '\\'){
					jj++;
//	_printf("コード(%c)\r\n",rep[ii].si[jj]);
					switch(command(&m_pRep[ii].si[jj])){
					#if	0	//現在はコードに変換しているので不要
						case 0:	//n	改行
							if(*(pPtr+kk) != '\n')	bFlg=TRUE;	//\nでなければ違うと判断
							break;
						case 2:	//t	タブ
							if(*(pPtr+kk) != '\t')	bFlg=TRUE;	//\tでなければ違うと判断
							break;
					#endif
						case 1:	//ts	複数タブ
//	_printf("複数タブ\r\n");
							while(*(pPtr+kk+1) == '\t')	kk++;
							jj += sizeof("ts")-2;	//文字数 -1			//タブ0も有効なので特に判定なし
							break;
						case 3:	//str	""で括られた文字列
							
							break;
						case 4:	//val	数値
							jj += sizeof("val")-2;	//文字数 -1
							if( *(pPtr+kk) < '0' || *(pPtr+kk) > '9' ){
								bFlg = TRUE;	//	数字でない
								break;
							}
							while( *(pPtr+kk) >= '0' && *(pPtr+kk) <= '9' )	kk += 1;
							kk--;
							break;
						case 5:	//knj	全角文字
						//	DEBUG_printf("d.txt","	%c%c : %c%c \r\n",rep[ii].si[jj],rep[ii].si[jj+1],*(pPtr+kk),*(pPtr+kk+1));
							jj += sizeof("knj")-2;	//文字数 -1
							if( !STRING_IsMultiByte( *(pPtr+kk) ) ){
								bFlg = TRUE;	//	全角文字でない
								break;
							}
							while( STRING_IsMultiByte( *(pPtr+kk)) )	kk += STRING_GetMultiByte();
							kk--;
						//	DEBUG_printf("d.txt","	%c:%c \r\n",rep[ii].si[jj+1],*(pPtr+kk+1));
							break;
						case 6:	//wrd
							jj += sizeof("wrd")-2;	//文字数 -1
							if( *(pPtr+kk) < '0' || *(pPtr+kk) > 'z'){
								bFlg = TRUE;	//	単語でない
								break;
							}
							while( *(pPtr+kk) >= '0' && *(pPtr+kk) <= 'z' )	kk += 1;
							kk--;
							break;
						
						case 7:	//blk
							jj += sizeof("blk")-2;	//文字数 -1
							kk--;
							break;
						
						default:
							bFlg = TRUE;	//違う
							break;
					}
				}else{
					bFlg = TRUE;	//違う
					break;
				}
				
			}
			kk++;
			jj++;
			if( bFlg )	break;
		}
		
		if( m_bPAgreement ){	/// 完全一致
			
			if( bFlg == FALSE )
			{
				BOOL	b1;

				if( _iOffset == 0 )	b1 = TRUE;
				else				b1 = checkBorder( pPtr-1 );
				if( b1 == FALSE || checkBorder( pPtr + STRING_len(m_pRep[ii].si) ) == FALSE )
				{
					bFlg = TRUE;	//違う
				}
			}
			
		}
		
		//	最後まで比較した時に相違がなければ一致
		if( bFlg == FALSE )
		{
			
			return ii;
		}
		
	}
	
	return -1;
}


//********************************************************************************************
/**
* @brief		copy関数
* @author		N.Takeda
*
* @param[in]		LPCTSTR		_pIn		
* @param[in,out]	int&		_iOf		
* @param[in,out]	LPTSTR		_pOut		
* @param[in]		const int	_iOutLen	長さ
* @param[in,out]	int&		_iSize		サイズ
* @param[in]		const int	_iIdx		番号
*
* @return		int	
*/
//********************************************************************************************
int		REPLASE::copy( LPCTSTR _pIn, int &_iOf, LPTSTR _pOut, const int _iOutLen, int &_iSize, const int _iIdx )
{
	int			ii, jj, kk, tab, tabs;
	u_char		*pPtr = NULL;
	
	if( m_pRep == NULL )	return 0;
	REP_LIST	*pRep = &m_pRep[ _iIdx ];
	
	
	if( m_bFlgComment == TRUE )
	{
		STRING_cat( m_cComment, pRep->si );
//		if( flag_sp1 == 1 )	sp_replace( in + _iOf,m_cComment );	//特殊
	}
	
	tab = 0;
	if( _iOf > 0 ){
		//	改行インデント合わせのタブ数を得る
		pPtr = (u_char*)(_pIn+_iOf-1);
		while( *pPtr >= ' ' )	pPtr--;
		while( *(pPtr-tab) == '\t' )	tab++;
	}
	
	ii = jj = 0;
	while( pRep->di[jj] != 0 && _iSize < _iOutLen-1 )
	{
		if( STRING_IsMultiByte( pRep->di[jj] ) ){	//	全角文字はそのままコピー
#ifndef	_UNICODE
			*(_pOut+_iSize++) = pRep->di[jj++];
#endif
			*(_pOut+_iSize++) = pRep->di[jj++];
			continue;
		}
		
		if(pRep->di[jj] == '\\' && pRep->di[jj+1] != '\\')
		{
			jj++;
			switch( command( &pRep->di[jj] ) )
			{
				case 0:	//n	改行
					*(_pOut+_iSize++) = '\r';
					*(_pOut+_iSize++) = '\n';
					jj++;
					//	改行インデント合わせのタブを挿入
					for(kk=0 ; kk<tab ; kk++)	*(_pOut+_iSize++) = '\t';
					break;
				case 2:	//t	タブ
					*(_pOut+_iSize++) = '\t';
					jj++;
					break;
				case 1:	//ts	複数タブ
					while( pRep->si[ii] != '\\' || pRep->si[ii+1] != 't' || pRep->si[ii+2] != 's' ){
						ii++;
						_iOf++;
					}
					ii += sizeof("\\ts")-1;
					jj += sizeof("ts")-1;
					tabs = 0;
					while(*(_pIn+_iOf) == '\t'){tabs++;	_iOf++;}	//元のタブはスキップし
					for(kk=0 ; kk<tabs ; kk++){				//数えたタブを挿入
						*(_pOut+_iSize++) = '\t';
					}
					break;
				case 3:	//str	""で括られた文字列
					break;
				case 4:	//val	数値
					while( pRep->si[ii] != '\\' || pRep->si[ii+1] != 'v' || pRep->si[ii+2] != 'a' ){
						ii++;
						_iOf++;
					}
					ii += sizeof("\\val")-1;
					jj += sizeof("val")-1;
					
					while( *(_pIn+_iOf) >= '0' && *(_pIn+_iOf) <= '9' ){
						*(_pOut+_iSize++) = *(_pIn+_iOf++);
					}
					break;
				case 5:	//knj	全角文字
					while( pRep->si[ii] != '\\' || pRep->si[ii+1] != 'k' || pRep->si[ii+2] != 'n' ){
						ii++;
						_iOf++;
					}
					ii += sizeof("\\knj")-1;
					jj += sizeof("knj")-1;
					
					while( STRING_IsMultiByte( *(_pIn+_iOf) ) ){
#ifndef	_UNICODE
						*(_pOut+_iSize++) = *(_pIn+_iOf++);
#endif
						*(_pOut+_iSize++) = *(_pIn+_iOf++);
					}
					break;
				case 6:	//wrd	単語
					while( pRep->si[ii] != '\\' || pRep->si[ii+1] != 'w' || pRep->si[ii+2] != 'r' ){
						ii++;
						_iOf++;
					}
					ii += sizeof("\\wrd")-1;
					jj += sizeof("wrd")-1;
					
					while( *(_pIn+_iOf) >= '0' && *(_pIn+_iOf) <= 'z' ){
						*(_pOut+_iSize++) = *(_pIn+_iOf++);
					}
					break;
				
				case 7:	//blk	ブロック
					while( pRep->si[ii] != '\\' || pRep->si[ii+1] != 'b' || pRep->si[ii+2] != 'l' ){
						ii++;
						_iOf++;
					}
					ii += sizeof("\\blk")-1;
					jj += sizeof("blk")-1;
					
					while( *(_pIn+_iOf) != '\r' || *(_pIn+_iOf+2) != '\r'/* || *(_pIn+_iOf) != '\n' || *(_pIn+_iOf+2) != '\n' */){
						*(_pOut+_iSize++) = *(_pIn+_iOf++);
					}
				//	_iSize -= 2;
				//	_iOf -= 2;
					
					break;
				
				default:
					
					break;	//違う
			}
		}
		else{	//	\コードでない物はそのままコピー
			*(_pOut+_iSize++) = pRep->di[jj++];
		}
	}
	
	_iOf += STRING_len(&pRep->si[ii]);
	
	
	if( _iSize >= _iOutLen-1 )
	{
		Message_error( TEXT("容量をオーバーしました。replase.cpp の OUT_ALLOC(%d) を増やしてリビルドして下さい"), OUT_ALLOC );
		m_bErr = TRUE;
	}
	
	return 0;
}




//******************************************************************
//		\t \n はエスケープシーケンスコードに変換してしまう
//******************************************************************
void	REPLASE::chg_tr( LPTSTR _pPtr )
{
	TCHAR	buff[ STRINGS_MAX ];
	int		ii, jj;
	
	
	STRING_cpy( buff, _pPtr );
	
	jj = 0;
	for(ii=0 ; ii<(int)STRING_len(buff) ; ii++){
		if(*(buff+ii) == '\\'){
			if(*(buff+ii+1) == 'n'){	*(_pPtr+jj++) = '\r';*(_pPtr+jj++) = '\n';ii++;}
			else if(*(buff+ii+1) == 't' && *(buff+ii+2) != 's'){	*(_pPtr+jj++) = '\t';ii++;}
			else *(_pPtr+jj++) = *(buff+ii);
		}
		else
			*(_pPtr+jj++) = *(buff+ii);
	}
	*(_pPtr+jj) = 0;
	
}


//******************************************************************
//		文字情報を得る
//******************************************************************
int		REPLASE::get_str( LPCTSTR _pPtr, int _ii, int _iLen, LPTSTR _pStr, BOOL _bFlg )
{
	int		jj;
	
	
	jj = 0;
	while( _ii < _iLen )
	{
#ifndef	_UNICODE
		if( STRING_IsMultiByte(*(_pPtr+_ii) ) ){	//	全角文字だ
			*(_pStr+jj++) = *(_pPtr+_ii++);
		}
		else
#endif
			if(*(_pPtr+_ii) < ' ')	break;
		*(_pStr+jj++) = *(_pPtr+_ii++);
	}
	*(_pStr+jj) = 0;
	if(_ii==_iLen)	return _ii;
	
	if( _bFlg && *(_pPtr+_ii) == '\t' ){
		Message_error( TEXT("１行内に定義が複数存在します. %s"), _pStr);
	}
	_ii++;
	while(*(_pPtr+_ii) == '\t' && _ii<_iLen)	_ii++;
	
	return _ii;
}


BOOL	REPLASE::checkBorder( LPCTSTR _pPtr )
{
#if 1
xstring		sBordrer = TEXT(" \t\r\n+-*/;:@[]|!#$%&'()=^<>?,.\\");
TCHAR		ch = *_pPtr;

if( sBordrer.find_first_of( ch ) != xstring::npos )
{
	return TRUE;
}

return FALSE;
#else
char	cBordrer[] = " \t\r\n+-*/;:@[]|!#$%&'()=^<>?,.\\";

for(int ii=0 ; ii<STRING_len(cBordrer) ; ii++){
	if( cBordrer[ii] == *_pPtr )	return TRUE;
}
return FALSE;
#endif
}


void	REPLASE::comment_clear(){
	STRING_cpy( m_cComment, TEXT("\t") );
	STRING_cat( m_cComment, ANNOTATE );
}


REPLASE		*pC_Rep = NULL;
