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

#include "stdafx.h"
#include "Shitarabian.h"
#include "FloatingSelection.h"
#include "EditDrawer.h"
#include "WhitespaceDefine.h"
#include "GeneralSetting.h"
#include "ShitarabianDoc.h"
#include "Padding.h"
#include "CommandDelete.h"
#include "CommandInput.h"
#include "CommandManager.h"
#include "CommandCaret.h"
#include "ExeCommon.h"

#include "TokenReader.h"

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



	CArray< int, int >		m_aStartXz;
	CArray< int, int >		m_aEndXz;
	CStringArray			m_astrSolidLinez;
	int						m_iLeftBlankWidth;




//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////

CFloatingSelection::CFloatingSelection()
{

}

CFloatingSelection::~CFloatingSelection()
{

}


// 選択文字列設定
BOOL	CFloatingSelection::SetLinez(
	CStringArray&	astrLinez
)
{
	m_astrLinez.RemoveAll();
	m_astrLinez.Append( astrLinez );

	// 挿入文字情報作成
	// 左側空白を取り除いた左端挿入開始位置を取得
	m_iLeftBlankWidth = BuildInsertInfo(
		&m_aStartXz,
		&m_aEndXz,
		&m_astrSolidLinez
	);


// ﾃﾞﾊﾞｯｸﾞ用
/*
	int	idxSolidLine;
	for ( idxSolidLine=0 ; idxSolidLine<m_astrSolidLinez.GetSize() ; idxSolidLine++ )
	{
		CString	strMes;
		strMes.Format( "%s, %d, %d\n",
			m_astrSolidLinez[idxSolidLine],
			m_aStartXz[idxSolidLine],
			m_aEndXz[idxSolidLine]
		);
		TRACE( strMes );
	}
*/
	return( TRUE );
}



// 挿入文字情報作成
int CFloatingSelection::BuildInsertInfo(
	CArray< int, int >*		paStartXz,		// 文字中の空白以外文字開始位置 [ﾌﾟﾚｰﾝ] 
	CArray< int, int >*		paEndXz,		// 文字中の空白以外文字終了位置 [ﾌﾟﾚｰﾝ] 
	CStringArray*			pastrLinez		// 文字中の空白以外文字
)
{
/*
		return
	|　　　 :
	|　　　 :
	|		　|あいう
	|		|あいうえ
*/

	paStartXz->RemoveAll();
	paEndXz->RemoveAll();
	pastrLinez->RemoveAll();

	// 左側空白を取り除いた左端挿入開始位置を取得
	int		iLeftBlankWidth = -1;
	long		idxLine;
	for( idxLine=0 ; idxLine<m_astrLinez.GetSize() ; idxLine++ )
	{
		CString&	strLine = m_astrLinez[ idxLine ];

		// カラム情報を取得
		int iStartColumn = 0;
		int iEndColumn = 0;
		{
			CArray< TToken, TToken& >	aTokenz;
			CTokenReader::StringToCharTypez(
				strLine,
				&aTokenz
			);
			int idxToken;
			for( idxToken=0 ; idxToken<aTokenz.GetSize() ; idxToken++ )
			{
				if( aTokenz[idxToken].eCharType != CharTypeWhitespace )
				{
					iStartColumn = aTokenz[idxToken].idxStartColumn;
					break;
				}
			}
			for( idxToken=aTokenz.GetSize()-1 ; idxToken>=0 ; idxToken-- )
			{
				if( aTokenz[idxToken].eCharType != CharTypeWhitespace )
				{
					iEndColumn = aTokenz[idxToken].idxStartColumn + aTokenz[idxToken].nColumn;
					break;
				}
			}
		}

		// カラムのバイト位置を取得
		int StartByte = ::CaretColumnToByteColumn( strLine, iStartColumn );
		int EndByte = ::CaretColumnToByteColumn( strLine, iEndColumn );
		CString	strSubString = strLine.Mid(
			StartByte,
			EndByte - StartByte
		);

		// 開始・終了カラムの座標を取得
		long	iStartX = 0;
		long	iEndX = 0;
		{
			CEmphasisLine	EmphasisLine;
			EmphasisLine.SetString( strLine );
			iStartX = EmphasisLine.CaretColumnToPixel( iStartColumn );
			iEndX = EmphasisLine.CaretColumnToPixel( iEndColumn );
		}

		// 情報の格納
		paStartXz->Add( iStartX );
		paEndXz->Add( iEndX );
		pastrLinez->Add( strSubString );
		if ( !strSubString.IsEmpty() )
		if ( iLeftBlankWidth > iStartX || iLeftBlankWidth < 0 )
		if ( iStartX >= 0 )
		{
			iLeftBlankWidth = iStartX;
		}
	}

	return( iLeftBlankWidth );
}




// 挿入前半位置確定
// フローティングのとき、フロート部と、以左の長さを得るための前処理
BOOL	CFloatingSelection::BuildLeftInfo(
	int					iRow,
	int					iStartInsertX,
	int*				piStartInsertColumn,
	int*				piStartBlankX,
	int*				piStartBlankColumn
)
{
	CWhitespaceDefine*	pWhitespace = CGeneralSetting::GetInstance()->GetWhitespace();
	CArray<TWhiteSpace, TWhiteSpace&>*	paCharz = pWhitespace->GetCharz();

	// 挿入開始位置　＝　左肩＋余白を除いた左端のピクセル位置計算

	// 挿入座標のキャレット位置取得
	int		iInsertColumn = 0;
	CEmphasisLine*	pEmphasisLine = m_pDoc->GetEmphasisLine( iRow );
	if ( pEmphasisLine != NULL )
	{
		iInsertColumn = pEmphasisLine->PixelToCaretColumn(
			iStartInsertX
		);

		// そこから左へ走査。前半パディング開始カラム捜索。
		CPadding	Padding;
		int		iBlankStartColumn = Padding.PrevWhiteSpaceColumn(
			*(::GetDocument()->GetLine( iRow )),
			iInsertColumn,
			*paCharz
		);
		int		iBlankStartX = pEmphasisLine->CaretColumnToPixel(
			iBlankStartColumn
		);
		*piStartBlankX = iBlankStartX;
		*piStartBlankColumn = iBlankStartColumn;
		*piStartInsertColumn = iInsertColumn;
		return( TRUE );
	}

	return( FALSE );
}




// 挿入後半位置確定
BOOL	CFloatingSelection::BuildRightInfo(
	int					idxRow,
	int					iEndInsertX,
	int*				piEndInsertColumn,
	int*				piEndBlankX,
	int*				piEndBlankColumn
)
{
	CEmphasisLine*	pEmphasisLine = m_pDoc->GetEmphasisLine( idxRow );
	if ( pEmphasisLine != NULL )
	{
		// 終了座標のキャレット位置取得
		int		iInsertColumn;
		iInsertColumn = pEmphasisLine->PixelToCaretColumn( iEndInsertX );

		// そこから右へ走査。後半パディング終了カラム捜索。
		int		iBlankEndColumn = 0;
		{
			CPadding	Padding;
			CWhitespaceDefine*	pWhitespace = CGeneralSetting::GetInstance()->GetWhitespace();
			CArray<TWhiteSpace, TWhiteSpace&>*	paCharz = pWhitespace->GetCharz();
			iBlankEndColumn = Padding.NextBlackSpaceColumn(
				*(::GetDocument()->GetLine( idxRow )),
				iInsertColumn,
				*paCharz
			);
		}

		if ( iBlankEndColumn >= 0 )
		{
			long	iBlankEndX = 0;
			iBlankEndX = pEmphasisLine->CaretColumnToPixel( iBlankEndColumn );

			*piEndInsertColumn = iInsertColumn;
			*piEndBlankColumn = iBlankEndColumn;
			*piEndBlankX = iBlankEndX;
		}
		else
		{
			*piEndInsertColumn = iInsertColumn;
			*piEndBlankColumn = pEmphasisLine->GetColumnCount();
			*piEndBlankX = pEmphasisLine->CaretColumnToPixel( *piEndBlankColumn );
		}
		return( TRUE );
	}
	return( FALSE );
}



// 選択文字列群の反映
BOOL	CFloatingSelection::DoReflect()
{
	CEditDrawer*		pDrawer = CEditDrawer::GetInstance();

	HDC		hdc = ::GetDC( NULL );
	CDC		DC;
	DC.Attach( hdc );
	CDC*				pDstDC = &DC;

	int		iStartRow = pDrawer->PixelToCaretRow( m_ptTopLeft.y, m_pDoc );

	CWhitespaceDefine*	pWhitespace = CGeneralSetting::GetInstance()->GetWhitespace();
	CArray<TWhiteSpace, TWhiteSpace&>*	paCharz = pWhitespace->GetCharz();
	CPadding	Padding;

	// 選択範囲初期化
	m_atSelectColumnz.RemoveAll();

	// 挿入操作
	long		iFloatLeftX = -1;		// 挿入位置情報　挿入フロートの左端位置
	long		iFloatRightX = -1;
	long		iFloatMinColumn = -1;
	long		iFloatMaxColumn = -1;
	long		iFloatMinXRow = -1;
	long		iFloatMaxXRow = -1;
	{
		// CCommandInput群の組み立て
		m_cmdEdit.RemoveAll();

		// 行についてのループ
		for( int idxLine=0 ; idxLine<m_astrSolidLinez.GetSize() ; idxLine++ )
		{
			CString*	pstrLine = m_pDoc->GetLine( idxLine+iStartRow );
			if ( pstrLine == NULL )
			{
				continue;
			}

			// 空白行無視
			if ( m_astrSolidLinez[idxLine].IsEmpty() )
			{
				TSelectColumn	tColumn;
				tColumn.idxStart = 0;
				tColumn.idxEnd = 0;
				m_atSelectColumnz.Add( tColumn );
				continue;
			}

			char		aDummyLine[] = "";
			const char*	pLine = aDummyLine;

			// 前半の処理
			// 挿入開始位置の計算
			CString		strLeftPadding;		// 左側パディング文字列
			int			iStartInsertX;
			int			iStartBlankColumn;	// 挿入位置以左の空白範囲
			{
				// 挿入前半位置確定
				int		iStartInsertColumn;
				iStartInsertX =
					( m_ptTopLeft.x ) // / pDrawer->GetZoom()
					+ m_aStartXz[idxLine]
					- m_iLeftBlankWidth;
				int		iStartBlankX;
				{
					pLine = (LPCTSTR)(*pstrLine);
					BuildLeftInfo(
						idxLine+iStartRow,
						iStartInsertX,
						&iStartInsertColumn,
						&iStartBlankX,
						&iStartBlankColumn
					);
				}

				// 前半パディング開始カラムから、挿入開始位置までのパディング。
				Padding.Padding(
					pDstDC,
					iStartInsertX - iStartBlankX,
					strLeftPadding,
					*paCharz,
					(iStartBlankColumn != 0 )
				);

			}

			// 後半の処理
			CString		strRightPadding;	// 右側パディング文字列
			int			iEndBlankColumn;
			int			iEndInsertX;
			{
				int		iEndInsertColumn;
				int		iEndBlankX;
				// 挿入終了位置　＝　挿入開始位置＋余白を除いた右端のピクセル位置計算
				iEndInsertX = iStartInsertX + (m_aEndXz[idxLine]-m_aStartXz[idxLine]);
	// please! Pixel+文字列のExtent。タブ文字許容すると難しい。Extentするときに開始Xのオフセットを取る必要あり。
				BuildRightInfo(
					idxLine+iStartRow,
					iEndInsertX,
					&iEndInsertColumn,
					&iEndBlankX,
					&iEndBlankColumn
				);

				// 挿入終了位置から後半パディング終了カラムまでのパディング。
				CPadding	Padding;
				Padding.Padding(
					pDstDC,
					iEndBlankX - iEndInsertX,
					strRightPadding,
					*paCharz,
					TRUE
				);
			}

			// 更新行を合成
			CString	strNewLine;
			{
				CString	strLeft(
					pLine,
					::CaretColumnToByteColumn( pLine, iStartBlankColumn)
				);

				int	iEndByte = ::CaretColumnToByteColumn( pLine, iEndBlankColumn );
				CString	strEndLine = pLine;
				CString	strRight = 
					strEndLine.Right( 
						strlen( pLine ) - iEndByte
					);

				strNewLine.Format(
					"%s%s%s%s%s",
					strLeft,
					strLeftPadding,
					m_astrSolidLinez[idxLine],
					strRightPadding,
					strRight
				);

				// フロート位置記録（オートスクロールのため）
				if ( iFloatLeftX<0 )
				{
					iFloatLeftX = iStartInsertX;
				}
				if ( iFloatLeftX >= iStartInsertX )
				{
					iFloatMinXRow = idxLine+iStartRow;
					iFloatLeftX = iStartInsertX;
					iFloatMinColumn = ::StringToColumn(strLeft) +
										::StringToColumn(strLeftPadding);
				}
				if ( iFloatRightX<0 )
				{
					iFloatRightX = iEndInsertX;
				}
				if ( iFloatRightX <= iEndInsertX )
				{
					iFloatMaxXRow = idxLine+iStartRow;
					iFloatRightX = iEndInsertX;
					iFloatMaxColumn = ::StringToColumn(strLeft) +
										::StringToColumn(strLeftPadding) +
										::StringToColumn(m_astrSolidLinez[idxLine]);
				}
			}

			// 行更新コマンド構築
			{
				// 行削除
				CCommandDelete	cmdDelete;
				cmdDelete.SetAttribute(
					idxLine+iStartRow,
					0,
					idxLine+iStartRow,
					::StringToColumn( pLine )
				);

				// 行挿入
				CCommandInput	cmdInput;
				cmdInput.SetAttribute(
					strNewLine,
					idxLine+iStartRow,
					0
				);

				// CMultiEditに登録
				m_cmdEdit.Add( cmdDelete );
				m_cmdEdit.Add( cmdInput );
			}

			// 選択範囲をアップデート(IsSelectedの対応)
			{
				long	nLeftBlankColumn = ::StringToColumn(
					(LPCTSTR)strLeftPadding
				);
				long	nSolidColumn = ::StringToColumn(
					(LPCTSTR)m_astrSolidLinez[idxLine]
				);

				TSelectColumn	tColumn;
				tColumn.idxStart = iStartBlankColumn + nLeftBlankColumn;
				tColumn.idxEnd = tColumn.idxStart + nSolidColumn;
				m_atSelectColumnz.Add( tColumn );
			}
		}
	}

	// CMultiEditを実行
	m_cmdEdit.Execute( m_pDoc );

	// 選択範囲をアップデート
	SetSelectedStart( iStartRow, iFloatMinColumn );
	SetSelectedEnd( iStartRow+m_astrSolidLinez.GetSize() -1, iFloatMaxColumn );

	// オートスクロールのために、フロートの行･列バウンド矩形を保存する
	m_LeftColumn = iFloatMinColumn;
	m_LeftRow = iFloatMinXRow;
	m_RightColumn = iFloatMaxColumn;
	m_RightRow = iFloatMaxXRow;

	pDstDC->Detach();
	::ReleaseDC( NULL, hdc );

	return( TRUE );

}

BOOL	CFloatingSelection::UnDoReflect()
{
	// 取り消しコマンド実行
	m_cmdEdit.UnExecute( m_pDoc );
	m_cmdEdit.RemoveAll();

	return( TRUE );

}



BOOL	CFloatingSelection::Confirm()
{
	CCommandManager*	pCmd = CCommandManager::GetInstance();
	pCmd->Add( m_cmdEdit );

	m_cmdEdit.RemoveAll();
	SetSelectedNutral();

	return( TRUE );

}

void CFloatingSelection::SetPosition(
	CPoint ptTopLeft
)
{
	m_ptTopLeft.x = max( 0, ptTopLeft.x );
	m_ptTopLeft.y = max( 0, ptTopLeft.y );
}
