////////////////////////////////////////////////////////////////////////////
// CTextEdit クラスのインプリメンテーション
//
////////////////////////////////////////////////////////////////////////////

#include "TextEdit.h"
#include "TaEditDoc.h"
#include "TaEditDraw.h"
#include "TaEditShell.h"
#include "TaEditJPEXT.h"
#include "TaEditMovetoDlg.h"
#include "TaEditProcessingDlg.h"
#include "FindText.h"
#include "ReplaceAll.h"

////////////////////////////////////////////////////////////////////////////
// 構築/破棄
////////////////////////////////////////////////////////////////////////////

CTextEdit::CTextEdit( CTaEditDraw *argpDraw, VClsPtr< CCursor > argpCursorInfo, CTaEditDoc *argpDoc ) :
	pDraw( argpDraw ),
	pCursorInfo( argpCursorInfo ),
	pDoc( argpDoc )
{

}

CTextEdit::~CTextEdit()
{
	pCursorInfo = NULL;
	pDraw = NULL;
	pDoc = NULL;
}

////////////////////////////////////////////////////////////////////////////
// メソッド
////////////////////////////////////////////////////////////////////////////

// 現在選択されている範囲にテキストを張り付ける
void CTextEdit::PasteText( const wstring& rStr, bool DoRedraw )
{
	T_CurPos NextPos;
	ScrollInfoPacket ScrPacket;
	T_SelRange SR = pCursorInfo->GetSelRange();
	T_CurPos CP = pCursorInfo->GetCurPos();

	// 文字列を挿入
	m_TaEditShell->ProcErrorMsg( pDoc->Insert( rStr, SR.GetSPos(), SR, &NextPos ) );
	pDoc->EndEdit();

	// カーソルを設定
	pCursorInfo->SetCurPos( NextPos, &ScrPacket, T_SelRange( NextPos, NextPos ) );

	// 再描画
	if ( DoRedraw ) pDraw->AfterEditRedraw( ScrPacket );
}

// アンドゥ
void CTextEdit::Undo()
{
	ScrollInfoPacket scrPacket;
	T_SelRange wSR;	// アンドゥ後の選択範囲
	T_CurPos wCP;	// アンドゥ後のカーソル位置

	ClearSelect();	// 一旦範囲選択を解除する。

	pDoc->Undo( &wSR, &wCP );
	pCursorInfo->SetCurPos( wCP, &scrPacket, wSR );	// カーソル位置を設定
	pDraw->AfterEditRedraw( scrPacket );	// 再描画処理
}

// リドゥ
void CTextEdit::Redo()
{
	ScrollInfoPacket scrPacket;
	T_SelRange wSR;	// リドゥ後の選択範囲
	T_CurPos wCP;	// リドゥ後のカーソル位置

	ClearSelect();	// 一旦範囲選択を解除する。

	pDoc->Redo( &wSR, &wCP );
	pCursorInfo->SetCurPos( wCP, &scrPacket, wSR );	// カーソル位置を設定
	pDraw->AfterEditRedraw( scrPacket );	// 再描画処理
}

// 選択範囲の削除
void CTextEdit::DelSelRange()
{
	ScrollInfoPacket scrPacket;
	T_CurPos wCP;

	// 選択されている範囲を削除する
	CErrorBool r = pDoc->InsertSpecialCode( XK_Delete, pCursorInfo->GetCurPos(), pCursorInfo->GetSelRange(), &wCP );
	pDoc->EndEdit();
	m_TaEditShell->ProcErrorMsg( r );

	// カーソル位置を設定
	pCursorInfo->SetCurPos( wCP, &scrPacket, false );

	// 再描画処理
	pDraw->AfterEditRedraw( scrPacket );
}

// 全て選択
void CTextEdit::FullSelect()
{
	ScrollInfoPacket scrPacket;

	// 現在のカーソル位置を取得
	T_CurPos wCP = pCursorInfo->GetCurPos();

	// 行数を取得
	const int LineCnt = pDoc->RefData().size();

	// 最後の行に含まれる文字数を取得
	T_LineDataItr_c LastLineItr = pDoc->RefData().end();
	LastLineItr--;
	const int LastLineCharCnt = LastLineItr->length();

	// 設定すべき選択範囲を作成
	T_SelRange wSR( T_CurPos( 0, 0 ), T_CurPos( LineCnt - 1, LastLineCharCnt ) );

	// カーソル位置を設定
	pCursorInfo->SetCurPos( wCP, &scrPacket, wSR );

	// 再描画
	pDraw->AfterSelRangeRedraw( scrPacket.ChangeSLine, scrPacket.ChangeELine );
}

// カット
void CTextEdit::Cut()
{
	if ( !IsEnableCopy() ) return ;

	Copy();	// 現在の選択範囲をコピーする
	DelSelRange();	// 選択範囲を削除する
}

// コピー
void CTextEdit::Copy()
{
	T_SelRange SR = pCursorInfo->GetSelRange();
	wstring wStr;

	if ( !IsEnableCopy() ) return ;

	// コピー対象の文字列を取得
	pDoc->GetRangeStr( SR, &wStr );

	// クリップボードへ設定
	JPEXT_SetClipboardData( m_TaEditShell->m_TaEditShell, wStr );
}

// ペースト
void CTextEdit::Paste()
{
	wstring wStr;

	// クリップボードからデータを取得する
	JPEXT_GetClipboardData( m_TaEditShell->m_TaEditShell, &wStr );

	// テキストを設定
	PasteText( wStr, true );
}

// クリップボードへのコピーが可能か否か
bool CTextEdit::IsEnableCopy() const
{
	if ( pCursorInfo->GetSelRange().GetLength() <= T_CurPos( 0, 0 ) )
		return false;
	else
		return true;
}

// 現在のカーソル位置からパターンのテキストと一致する部分を検索する。
void CTextEdit::FindText( const wstring &rPattern )
{
	ScrollInfoPacket scrPacket;
	T_SelRange nSelRange;
	T_CurPos nCurPos;
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	int ShowDlgDelay = pConf->GetShowPrecessingDlgDelay();
	CFindText *pFindTextObj = new CFindText();
	if ( NULL == pFindTextObj ) {
		m_TaEditShell->ShowErrorMsgBox( pConf->RefOutOfMemoryErrorMsg() );
		return ;
	}

	if ( pDoc->RefData().size() == 1 && pDoc->RefData().begin()->length() == 0 ) {
		// 検索すべき文字列が一文字もない
		m_TaEditShell->ShowInformationMsgBox( pConf->RefCantFindTextMsg() );
		goto ERR_EXIT;
	}

	// 検索オブジェクトに情報を設定
	if ( !pFindTextObj->SetInfo( pDoc, pCursorInfo->GetCurPos(), rPattern, pConf ) )
		goto ERR_EXIT;

	// 検索
	if ( m_ProcessingShell->ShowDlg( pFindTextObj, ShowDlgDelay ) )
		goto ERR_EXIT;
	if ( !m_TaEditShell->ProcErrorMsg( pFindTextObj->GetErrorInfo() ) )
		goto ERR_EXIT;

	if ( !pFindTextObj->IsMatched() ) {
		// 見つからない
		m_TaEditShell->ShowInformationMsgBox( pConf->RefCantFindTextMsg() );
		goto ERR_EXIT;
	}

	// カーソル位置を設定
	nSelRange = pFindTextObj->GetMatchRange();
	nCurPos = nSelRange.GetEPos();
	pCursorInfo->SetCurPos( nCurPos, &scrPacket, nSelRange );

	// 再描画
	if ( scrPacket.NeedXScroll || scrPacket.NeedYScroll )
		pDraw->AfterEditRedraw( scrPacket );
	else
		pDraw->AfterSelRangeRedraw( scrPacket.ChangeSLine, scrPacket.ChangeELine );

ERR_EXIT:
	delete pFindTextObj;
}


// パターンに一致する文字列を全て置換する。
void CTextEdit::ReplaceAll( const wstring &rPattern, const wstring& rAfterText )
{
	ScrollInfoPacket scrPacket;
	T_CurPos wCurPos;
	T_SelRange nSelRange;
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	int ShowDlgDelay = pConf->GetShowPrecessingDlgDelay();
	CReplaceAll *pReplaceAllObj = NULL;
	CTaEditDoc *wpDoc = NULL;

	if ( pDoc->RefData().size() == 1 && pDoc->RefData().begin()->length() == 0 ) {
		// 検索すべき文字列が一文字もない
		m_TaEditShell->ShowInformationMsgBox( pConf->RefCantFindTextMsg() );
		return ;
	}

	// 置換用オブジェクトを構築
	pReplaceAllObj = new CReplaceAll();
	if ( NULL == pReplaceAllObj ) {
		m_TaEditShell->ShowErrorMsgBox( pConf->RefOutOfMemoryErrorMsg() );
		return ;
	}

	// 排他的な編集用に、ドキュメントオブジェクトを複製する
	wpDoc = pDoc->CopyForExclusiveEditing();
	if ( NULL == wpDoc ) {
		m_TaEditShell->ShowErrorMsgBox( pConf->RefOutOfMemoryErrorMsg() );
		goto ERR_EXIT;
	}

	// 検索オブジェクトに情報を設定
	if ( !pReplaceAllObj->SetInfo( wpDoc, rPattern, pConf, rAfterText ) )
		goto ERR_EXIT;

	// 検索
	m_ProcessingShell->ShowDlg( pReplaceAllObj, ShowDlgDelay );
	wpDoc->EndEdit();

	// エラー処理
	m_TaEditShell->ProcErrorMsg( pReplaceAllObj->GetErrorInfo() );

	// 編集内容を反映する
	pDoc->ReflectionExclusiveEditing( wpDoc );

	// カーソル位置を設定
	wCurPos = T_CurPos( 0, 0 );	// 処理後のカーソルは、テキストの先頭に設定する
	nSelRange = T_SelRange( wCurPos, wCurPos );
	pCursorInfo->SetCurPos( wCurPos, &scrPacket, nSelRange );

	// 再描画
	pDraw->AfterEditRedraw( scrPacket );

ERR_EXIT:
	delete pReplaceAllObj;
	delete wpDoc;
}

// 文字列の移動を行う
void CTextEdit::MoveText( T_SelRange SourceRange, int DestXPos, int DestYPos )
{
	ScrollInfoPacket scrPacket;
	wstring wStr;
	T_CurPos wCurPos;
	CErrorBool r;

	// 移動対象の文字列を取得
	pDoc->GetRangeStr( SourceRange, &wStr );

	// 挿入位置を計算
	T_CurPos DestPos = pDraw->PixPosToCurPos( DestXPos, DestYPos );

	// 挿入位置が移動元より上の方の場合は、削除してから挿入する。
	if ( SourceRange.GetSPos() > DestPos ) {
		r = pDoc->InsertSpecialCode( XK_Delete, SourceRange.GetEPos(), SourceRange, &wCurPos );
		if ( r ) r = pDoc->Insert( wStr, DestPos, T_SelRange( DestPos, DestPos ), &wCurPos );
	}
	else {
		r = pDoc->Insert( wStr, DestPos, T_SelRange( DestPos, DestPos ), &wCurPos );
		if ( r ) r = pDoc->InsertSpecialCode( XK_Delete, SourceRange.GetEPos(), SourceRange, &wCurPos );
	}
	m_TaEditShell->ProcErrorMsg( r );
	pDoc->EndEdit();

	// 新しいカーソル位置と選択範囲を設定
	T_SelRange nSelRange = T_SelRange( DestPos, DestPos );
	pCursorInfo->SetCurPos( DestPos, &scrPacket, nSelRange );

	// 再描画
	pDraw->AfterEditRedraw( scrPacket );
}

// 指定行へ移動
// MoveToダイアログを表示し、入力された行へ移動する
void CTextEdit::OnMoveto()
{
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();

	// 現在のカーソル位置を取得
	int CurLine = pCursorInfo->GetCurPos().GetLine();
	CurLine += pConf->GetLineNumberOrigin();

	// 移動先の行番号を取得・移動
	OnMoveto( m_MovetoShell->ShowDlg( CurLine ) );
}

// 指定された行へ移動する
void CTextEdit::OnMoveto( int Line )
{
	T_CurPos wCurPos;
	ScrollInfoPacket scrPacket;
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	int LineCnt = pDoc->RefData().size();	// 現在の総行数

	Line -= pConf->GetLineNumberOrigin();
	if ( Line < 0 ) Line = 0;
	if ( Line >= LineCnt ) Line = LineCnt - 1;

	// 移動・再描画
	wCurPos.SetLine( Line );
	wCurPos.SetCPos( 0 );
	pCursorInfo->SetCurPos( wCurPos, &scrPacket, false );
	pDraw->AfterEditRedraw( scrPacket );
}


// 範囲選択を解除する
void CTextEdit::ClearSelect()
{
	T_SelRange wSR;
	ScrollInfoPacket scrPacket;

	// 範囲選択されていた場合は、範囲選択を解除して再描画する
	wSR = pCursorInfo->GetSelRange();
	if ( wSR.GetLength() == T_CurPos( 0, 0 ) )
		return ;

	const T_CurPos &wCP = pCursorInfo->GetCurPos();
	wSR.SetPos( wCP, wCP );
	pCursorInfo->SetCurPos( wCP, &scrPacket, wSR );
	pDraw->AfterSelRangeRedraw( scrPacket.ChangeSLine, scrPacket.ChangeELine );
}

