/*-----------------------------------------------------------------------------
[名称]	Reverse.cpp
[概要]	文字列反転クラスの実装
[備考]	なし
[履歴]	2003-02-27	NC		コメント付加(moralog)
-----------------------------------------------------------------------------*/


//-----------------------------------------------------------------------------
// インクルード
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "shitarabian.h"
#include "Reverse.h"
#include "Selection.h"
#include "ShitarabianDoc.h"
#include "CommandDelete.h"
#include "CommandInput.h"
#include "CommandMultiEdit.h"
#include "GeneralSetting.h"
#include "CommandManager.h"
#include "Padding.h"
#include "ExeCommon.h"

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


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

}


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

}


/*-----------------------------------------------------------------------------
[名称]	選択範囲の上下反転
[機能]	選択範囲の文字列を上下反転する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void	CReverse::Flip(
	CShitarabianDoc*	pDoc		// I  :ドキュメント [-] (-) :-
)
{
	CArray<TSelectColumn, TSelectColumn&>*	pColumnz;
	pColumnz = pDoc->GetSelection()->GetSelectedLinez();

	int		idxStartRow = pDoc->GetSelection()->GetSelectedStartRow();
	int		idxEndRow = pDoc->GetSelection()->GetSelectedEndRow();

	if ( pColumnz->GetSize() != (idxEndRow-idxStartRow+1) )
	{
		// 選択情報がおかしい
		ASSERT( FALSE );
		return;
	}

	CCommandMultiEdit	cmdEdit;
	CStringArray		azstrLine;

	CArray<TSelectColumn, TSelectColumn&>	atReversez;

	int		idxRow;
	for( idxRow=idxStartRow ; idxRow<=idxEndRow ; idxRow++ )
	{
		CString*	pstrLine = pDoc->GetLine( idxRow );

		int idxStartColumn = pColumnz->GetAt(idxRow-idxStartRow).idxStart;
		int idxEndColumn = pColumnz->GetAt(idxRow-idxStartRow).idxEnd;

		// 選択範囲を削除
		CCommandDelete	cmdDelete;
		cmdDelete.SetAttribute(
			idxRow,
			idxStartColumn,
			idxRow,
			idxEndColumn
		);
		cmdEdit.Add( cmdDelete );

		// 選択範囲の文字列取得
		int	idxStartByte = ::CaretColumnToByteColumn( (LPCTSTR)(*pstrLine), idxStartColumn );
		int	idxEndtByte = ::CaretColumnToByteColumn( (LPCTSTR)(*pstrLine), idxEndColumn );
		CString		strLine( (LPCTSTR)(*pstrLine)+idxStartByte, idxEndtByte-idxStartByte );

		// 文字列反転
		azstrLine.Add( Flip( strLine ) );
	}
	for( idxRow=idxStartRow ; idxRow<=idxEndRow ; idxRow++ )
	{
		TSelectColumn	tColumn = pColumnz->GetAt(idxRow-idxStartRow);

		// 選択位置に文字列挿入
		CCommandInput	cmdInput;
		cmdInput.SetAttribute(
			azstrLine[idxEndRow-idxRow],
			idxRow,
			pColumnz->GetAt(idxRow-idxStartRow).idxStart
		);
		cmdEdit.Add( cmdInput );

		tColumn.idxEnd = tColumn.idxStart + ::StringToColumn( azstrLine[idxEndRow-idxRow] );
		atReversez.InsertAt( idxRow-idxStartRow, tColumn );
	}
	cmdEdit.Execute( pDoc );

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

	// 選択情報も反転させる
	for( idxRow=0 ; idxRow<atReversez.GetSize() ; idxRow++ )
	{
		pDoc->GetSelection()->Normalize();
		pDoc->GetSelection()->SetSelectedLinez( idxRow, atReversez[idxRow] );
	}
}


/*-----------------------------------------------------------------------------
[名称]	文字列の上下反転
[機能]	文字列を上下反転する。
[戻値]	CString		反転文字列 [-] (-)
[備考]	上下反転文字定義情報を使用する。
-----------------------------------------------------------------------------*/
CString	CReverse::Flip(
	CString&	strSrc		// I  :反転元文字列 [-] (-) :-
)
{
	CString	strReverce;

	int		iByteSize = 1;
	int		idxChar;
	for( idxChar=0 ; idxChar<strSrc.GetLength() ; idxChar+=iByteSize )
	{
		// マルチバイト判別
		if ( _istleadbyte( strSrc[idxChar] ) != 0 )
		{
			iByteSize = 2;
		}
		else
		{
			iByteSize = 1;
		}

		CString	strTmp( ((LPCTSTR)strSrc)+idxChar, iByteSize );
		strReverce += strTmp;
	}

	return( CGeneralSetting::GetInstance()->GetFlipz()->Flip( strReverce ) );
}


/*-----------------------------------------------------------------------------
[名称]	選択範囲の左右反転
[機能]	選択範囲の文字列を左右反転する。
[戻値]	なし
[備考]	反転時のズレを抑えるためパディングを行う。
-----------------------------------------------------------------------------*/
void	CReverse::Mirror(
	CShitarabianDoc*	pDoc,		// I  :ドキュメント [-] (-) :-
	CDC*				pDC			// I  :（描画先）DC [-] (-) :-
)
{
	CArray<TSelectColumn, TSelectColumn&>*	paColumnz = pDoc->GetSelection()->GetSelectedLinez();

	int		idxStartRow = pDoc->GetSelection()->GetSelectedStartRow();
	int		idxEndRow = pDoc->GetSelection()->GetSelectedEndRow();

	// ノーマル選択のために選択範囲をノーマライズ please! ノーマライズ関数ぐらい作っておくべき
	pDoc->GetSelection()->SetSelectedStart( idxStartRow, pDoc->GetSelection()->GetSelectedStartColumn() );

	if ( paColumnz->GetSize() != (idxEndRow-idxStartRow+1) )
	{
		// 選択情報がおかしい
		ASSERT( FALSE );
		return;
	}

	// 反転行群を均一の長さにするため最大幅を取得する
	int		idxRow;
	long	lPixelXWidth = 0;
	for( idxRow=idxStartRow ; idxRow<=idxEndRow ; idxRow++ )
	{
		CEmphasisLine*	pLine = pDoc->GetEmphasisLine( idxRow );
		if ( pLine != NULL )
		{
			lPixelXWidth = max( lPixelXWidth, pLine->GetWidth() );
		}
	}

	// 編集コマンドの構築
	CCommandMultiEdit	cmdEdit;
	for( idxRow=idxStartRow ; idxRow<=idxEndRow ; idxRow++ )
	{
		TSelectColumn	tColumn = paColumnz->GetAt(idxRow-idxStartRow);

		CEmphasisLine*	pLine = pDoc->GetEmphasisLine( idxRow );
		long			lWidth = pLine->GetWidth();

		CString*		pstrLine = pDoc->GetLine( idxRow );
		if ( pstrLine != NULL )
		{
			int idxStartColumn = paColumnz->GetAt(idxRow-idxStartRow).idxStart;
			int idxEndColumn = paColumnz->GetAt(idxRow-idxStartRow).idxEnd;

			// 選択範囲を削除
			CCommandDelete	cmdDelete;
			cmdDelete.SetAttribute(
				idxRow,
				idxStartColumn,
				idxRow,
				idxEndColumn
			);
			cmdEdit.Add( cmdDelete );

			// 選択範囲の文字列取得
			int	idxStartByte = ::CaretColumnToByteColumn( (LPCTSTR)(*pstrLine), idxStartColumn );
			int	idxEndtByte = ::CaretColumnToByteColumn( (LPCTSTR)(*pstrLine), idxEndColumn );
			CString		strLine( (LPCTSTR)(*pstrLine)+idxStartByte, idxEndtByte-idxStartByte );

			// 反転行群を均一の長さにするためパディングする
			CString		strPadding;
			{
				CPadding	Padding;
				Padding.Padding(
					pDC,
					lPixelXWidth - lWidth,
					strPadding,
					*(CGeneralSetting::GetInstance()->GetWhitespace()->GetCharz()),
					FALSE
				);
					// 反転後の行群の長さについてしなければいけない処理だが
					// 反転処理直後はAnalizeされていないので表示幅長さがわからない
					// アバウトにそろってりゃ充分であろうからこれでよし
					// please! あるいは単純に GetTextExtent()した幅を使うか？
			}

			// 反転文字列完成
			CString		strReverse;
			strReverse.Format( "%s%s", strPadding, Mirror( strLine ) );

			// 選択位置に文字列挿入
			CCommandInput	cmdInput;
			cmdInput.SetAttribute(
				strReverse,
				idxRow,
				idxStartColumn
			);
			cmdEdit.Add( cmdInput );

			// 選択情報(ながさ）も変更の必要あり
			int		iWordLength = ::StringToColumn( strReverse );

			// 選択範囲変更
			tColumn.idxStart = idxStartColumn;
			tColumn.idxEnd = idxStartColumn + iWordLength;
			pDoc->GetSelection()->SetSelectedLinez( idxRow-idxStartRow, tColumn );
		}
	}

	// 一気に編集
	cmdEdit.Execute( pDoc );

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


/*-----------------------------------------------------------------------------
[名称]	文字列の左右反転
[機能]	文字列を左右反転する。
[戻値]	CString		反転文字列 [-] (-)
[備考]	左右反転文字定義情報を使用する。
-----------------------------------------------------------------------------*/
CString	CReverse::Mirror(
	CString&	strSrc		// I  :反転元文字列 [-] (-) :-
)
{
	CString	strReverce;

	int		iByteSize = 1;
	int		idxByte;
	for( idxByte=0 ; idxByte<strSrc.GetLength() ; idxByte+=iByteSize )
	{
		// Mirror文字定義を使用して反転
		CString	strTmp( ((LPCTSTR)strSrc)+idxByte );
		CString	strChar = CGeneralSetting::GetInstance()->GetMirrorz()->FlipChar( strTmp, &iByteSize );
		strReverce = strChar + strReverce;
	}
	return( strReverce );
}
