/*-----------------------------------------------------------------------------
[名称]	Padding.cpp
[概要]	パディングクラスの実装
[備考]	なし
[履歴]	2002-12-12	NC		コメント付加(moralog)
		2004-05-31	b040111 (4419)フローティング開始時の高速化(moralog)
-----------------------------------------------------------------------------*/

//-----------------------------------------------------------------------------
// インクルード
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "Shitarabian.h"
#include "Padding.h"
#include "WhitespaceDefine.h"
#include "EditDrawer.h"
#include "GeneralSetting.h"
#include <memory.h>

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

//-----------------------------------------------------------------------------
// ファイルグローバル変数
//-----------------------------------------------------------------------------
//		‥‥スマートでない please!
static char*		f_pstrDstLine;		// パディング空白文字列候補 [-] (-)
static char*		f_pstrWorkLine;		// パディング空白文字列作成ワークエリア [-] (-)
static long			f_lMax = 0;			// パディング空白文字列候補長さ [pixel] (-)
static DWORD		f_dwStartTime = 0;	// パディング開始時間 [::GetTickCount()] (-)


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

}

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

}


/*-----------------------------------------------------------------------------
[名称]	空白文字パディング
[機能]	指定のPixel長さの空白文字列を生成する。
		ぴったりの長さの空白文字列が生成できないときはそれっぽい文字列を返し
		指定長さとのギャップを戻す。
[戻値]	long ギャップ [pixel] (-)
[備考]	空白文字定義を反映する。
		bEnableHeadSpace フラグをTRUEにすると、
		先頭許容しない設定になっている空白文字でも先頭に使用する。
-----------------------------------------------------------------------------*/
long	CPadding::Padding(
	CDC*								pDC,			// I: 対象DC [-] (-) :-
	long								lLength,		// I: 指定長 [pixel] (0〜) :-
	CString&							strDstLine,		// O: パディング空白文字列 [-] (-) :-
	CArray<TWhiteSpace,TWhiteSpace&>&	atCharz,		// I: 空白文字定義 [-] (-) :-
	BOOL								bEnableHeadSpace// I: 先頭有効 [-] (TRUE:=常に先頭有効、FALSE:設定反映) :FALSE
)
{
	strDstLine.Empty();
	if ( lLength <= 0)
	{
		return( 0 );
	}

	CEditDrawer*	pDrawer = CEditDrawer::GetInstance();
	pDrawer->ReflectFont( pDC );

	// 空白文字情報の生成（文字幅の取得）
	CArray<TCharItemData,TCharItemData&>	atCharDataz;
	atCharDataz.SetSize( atCharz.GetSize() );
	long	idxChar;
	long	iMinWidth = lLength;
	long	iMaxCharByte = 0;		// 空白文字バイト数最大
	for( idxChar=0 ; idxChar<atCharz.GetSize() ; idxChar++ )
	{
		atCharDataz[ idxChar ].lWidth = pDrawer->StringToPixelX( atCharz[idxChar].strChar, pDC );
		atCharDataz[ idxChar ].nCount = 0;

		iMinWidth = min( iMinWidth, atCharDataz[ idxChar ].lWidth );
		iMaxCharByte = max( iMaxCharByte, atCharz[ idxChar ].strChar.GetLength() );
	}

	f_lMax = lLength;

	// あらかじめ最大のバイト数を決め打つ
	// 最大バイト数　＝　レングス／文字の最小幅（*マルチバイト）
// ▼ (4419) BGN 2004.05.31 (moralog) フローティング開始時の高速化
	int	iWorkLength = lLength*iMaxCharByte + 2;
// ▽ oridinal BGN
//	int	iWorkLength = lLength*iMaxCharByte / iMinWidth +2;
// △ oridinal END
// ▲ (4419) END
	f_pstrDstLine = new char[ iWorkLength ];
	f_pstrWorkLine = new char[ iWorkLength ];

	memset( f_pstrDstLine, 0x00, iWorkLength );
	memset( f_pstrWorkLine, 0x00, iWorkLength );

	// パディングの実行
	f_dwStartTime = ::GetTickCount();


	for( long idxS=0 ; idxS<atCharDataz.GetSize() ; idxS++ )
	{
		TRACE( "%s - %dpix\n", atCharz[idxS].strChar, atCharDataz[idxS].lWidth );
	}

	long	lGap = PaddingCore(
				lLength,
				f_pstrWorkLine,
				NULL,
				atCharz,
				atCharDataz,
				bEnableHeadSpace
	);

	strDstLine = f_pstrDstLine;

	delete [] f_pstrWorkLine;
	delete [] f_pstrDstLine;
	f_pstrWorkLine = NULL;
	f_pstrDstLine = NULL;


	pDrawer->RestoreFont( pDC );
	return( lGap );
}


/*-----------------------------------------------------------------------------
[名称]	空白文字開始カラム取得
[機能]	指定カラム以前の空白開始位置を得る。
		見つからない場合は iTargetColumn を戻す。
[戻値]	long 空白文字開始カラム [Caretカラム] (-)
[備考]	空白文字定義を反映する。
-----------------------------------------------------------------------------*/
long	CPadding::PrevWhiteSpaceColumn(
	const char*							pstrLine,		// I: 検索対象文字列 [-] (-) :-
	long								iTargetColumn,	// I: 検索開始位置 [Caretカラム] (-) :-
	CArray<TWhiteSpace,TWhiteSpace&>&	atCharz			// I: 空白文字定義 [-] (-) :-
)
{
	long	idxColumn = 0;

	BOOL	bPrevWhiteSpace = FALSE;
	long	iWhiteSpaceColumn = 0;

	while( *pstrLine != 0x00 )
	{
		// ターゲットに達したら終了
		if( idxColumn >= iTargetColumn )
		{
			break;
		}

		// 空白文字か？
		BOOL	bWhiteSpace = FALSE;
		{
			long	idxChar;
			for( idxChar=0 ; idxChar<atCharz.GetSize() ; idxChar++ )
			{
				if ( atCharz[idxChar].bEnable )
				if ( 0 == memcmp( (LPCTSTR)(atCharz[idxChar].strChar), pstrLine, atCharz[idxChar].strChar.GetLength() ) )
				{
					bWhiteSpace = TRUE;
					break;
				}
			}
		}

		// 空白文字ならば、最期の空白文字候補とする
		if ( bWhiteSpace )
		{
			if ( !bPrevWhiteSpace )
			{
				iWhiteSpaceColumn = idxColumn;
			}
			bPrevWhiteSpace = TRUE;
		}
		else
		{
			bPrevWhiteSpace = FALSE;
		}
	
		// 次の文字へ
		{
			// マルチバイト判別
			int		iByteSize;
			if ( _istleadbyte( *pstrLine ) != 0 )
			{
				iByteSize = 2;
			}
			else
			{
				iByteSize = 1;
			}
			pstrLine += iByteSize;
			idxColumn++;
		}
	}

	// ターゲットに達するまで空白開始位置候補を見つけていれば反映
	if ( bPrevWhiteSpace )
	{
		return( iWhiteSpaceColumn );
	}
	else
	{
		return( iTargetColumn );
	}
}


/*-----------------------------------------------------------------------------
[名称]	空白以外文字開始カラム取得
[機能]	指定カラム以後の空白以外文字開始位置を得る。
[戻値]	long 空白以外文字開始カラム [Caretカラム] (-1:見つからなかった)
[備考]	空白文字定義を反映する。
-----------------------------------------------------------------------------*/
long	CPadding::NextBlackSpaceColumn(
		const char*		pstrLine,					// I: 検索対象文字列 [-] (-) :-
		long			iTargetColumn,				// I: 検索開始位置 [Caretカラム] (-) :-
		CArray<TWhiteSpace,TWhiteSpace&>&	atCharz	// I: 空白文字定義 [-] (-) :-
)
{
	long	idxColumn = 0;

	// ターゲットカラムまで空読み
	while( *pstrLine != 0x00 )
	{
		// ターゲットに達したら抜ける
		if( idxColumn >= iTargetColumn )
		{
			break;
		}

		// カラムのカラ読み
		{
			// マルチバイト判別
			int		iByteSize;
			if ( _istleadbyte( *pstrLine ) != 0 )
			{
				iByteSize = 2;
			}
			else
			{
				iByteSize = 1;
			}
			pstrLine += iByteSize;
			idxColumn++;
		}
	}

	// ターゲットカラムから、空白で無い文字を探す
	while( *pstrLine != 0x00 )
	{
		// 空白文字か？
		BOOL	bWhiteSpace = FALSE;
		{
			long	idxChar;
			for( idxChar=0 ; idxChar<atCharz.GetSize() ; idxChar++ )
			{
				if ( atCharz[idxChar].bEnable )
				if ( 0 == memcmp( (LPCTSTR)(atCharz[idxChar].strChar), pstrLine, atCharz[idxChar].strChar.GetLength() ) )
				{
					pstrLine += atCharz[idxChar].strChar.GetLength();
					bWhiteSpace = TRUE;
					break;
				}
			}
		}

		// 空白文字でないならばHIT
		if ( !bWhiteSpace )
		{
			return( idxColumn );
		}
		idxColumn++;
	}
	return( -1 );
}


/*-----------------------------------------------------------------------------
[名称]	空白文字パディング
[機能]	指定のPixel長さの空白文字列を生成する。
		ぴったりの長さの空白文字列が生成できないときはそれっぽい文字列を返し
		指定長さとのギャップを戻す。
[戻値]	long ギャップ [pixel] (-)
[備考]	再帰。
		Padding()から使用される。
		// please! 高速化きぼんぬ。
-----------------------------------------------------------------------------*/
long	CPadding::PaddingCore(
	long									lLength,		// I: 指定長 [pixel] (0〜) :-
	char*									pstrCurrent,	// I: 作業用文字列の現在位置ポインタ [-] (-) :-
	char*									pstrPrev,		// I: 直前空白文字 [-] (-) :-
	CArray<TWhiteSpace,TWhiteSpace&>&		atCharz,		// I: 空白文字定義 [-] (-) :-
	CArray<TCharItemData,TCharItemData&>&	atCharDataz,	// I: 空白文字情報 [-] (-) :-
	BOOL									bEnableHeadSpace// I: 先頭有効 [-] (TRUE:=常に先頭有効、FALSE:設定反映)
)
{
	*pstrCurrent = 0x00;

	DWORD	dwNowTime = ::GetTickCount();
	if( dwNowTime > f_dwStartTime + 500 )// ←適当な閾値
	{
		// ハングる場合の対策‥‥消極的ではあるが。
		// 操作によって精度を変更するのも良いと思う please!

		// please! あらかじめ、できうる結果の最良長さだけでも計算できれば、
		// その長さに合致するパターンを見つけたときに終了というショートカットができる

//		return( 0 );
	}

	// ESCが押されたら緊急停止！
	if ( ::GetAsyncKeyState( VK_ESCAPE ) != 0x00 )
	{
		return( 0 );
	}

	// 終了検査
	{
		// つぶれていたら失格
		if ( lLength < 0 )
		{
			return( lLength );
		}

		// Hitに近い内容であれば保存
		if ( lLength < f_lMax )
		{
			strcpy( f_pstrDstLine, f_pstrWorkLine );
			f_lMax = lLength;
		}

		// Hitしていれば終了
		if ( lLength == 0 )
		{
			return( 0 );
		}
	}

	// キャラクタを優先順に１文字おいてみる
	long	idxChar;
	for( idxChar=0 ; idxChar<atCharz.GetSize() ; idxChar++ )
	{
		// 有効チェック
		if ( !atCharz[idxChar].bEnable )
			continue;

		// 先頭チェック
		if ( !bEnableHeadSpace )
		if ( !atCharz[idxChar].bCanHead )
		if ( pstrPrev == NULL )
			continue;

		// 連続チェック
		if ( !atCharz[idxChar].bCanContinuation )
		if ( pstrPrev != NULL )
		if ( 0 == memcmp( pstrPrev, (LPCTSTR)atCharz[idxChar].strChar, atCharz[idxChar].strChar.GetLength() ) )
			continue;

		// 個数チェック
		if ( atCharz[idxChar].nCount > 0)
		if ( atCharDataz[idxChar].nCount >= atCharz[idxChar].nCount )
			continue;

		// ためしに文字を入れる
		long	lCharlength = atCharz[idxChar].strChar.GetLength();
		strcpy( pstrCurrent, (LPCTSTR)atCharz[idxChar].strChar );
		atCharDataz[idxChar].nCount++;
		{
		// 以降を試行
			long	lGap = PaddingCore(
						lLength - atCharDataz[idxChar].lWidth,
						pstrCurrent + lCharlength,
						pstrCurrent,
						atCharz,
						atCharDataz,
						bEnableHeadSpace
			);
			// ヒットすれば終了
			if ( lGap == 0 )
			{
				return( 0 );
			}
		}
		atCharDataz[idxChar].nCount--;
	}
	return( f_lMax );
}


// ▼ (4419) BGN 2004.06.10 (moralog) 右余白の除去
/*-----------------------------------------------------------------------------
[名称]	右余白除去
[機能]	行末の余白を除去する。
[戻値]	CString	-	除去済み文字列 [-] (-)
[備考]	なし
-----------------------------------------------------------------------------*/
CString	CPadding::TrimRight(
	CString									strSrc,		// I: 元文字列 [-] (-) :-
	CArray<TWhiteSpace,TWhiteSpace&>&		atCharz		// I: 空白文字定義 [-] (-) :-
)
{
	long	lMaxColumn = ::StringToColumn( strSrc );

	long	lTrimColumn = PrevWhiteSpaceColumn(
		strSrc,
		lMaxColumn,
		atCharz
	);

	// そのカラム以後を除去
	return( strSrc.Left( ::CaretColumnToByteColumn( strSrc, lTrimColumn ) ) );

}
// ▲ (4419) END

