/*-----------------------------------------------------------------------------
[名称]	ShitarabianPlugInHTMLSim.ヘッダ
[概要]	htmlシミュレータプラグインクラスの実装
[備考]	なし
[履歴]	2003-03-12	NC		コメント付加(moralog)
-----------------------------------------------------------------------------*/

//-----------------------------------------------------------------------------
// インクルード
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "ShitarabianPlugInHTMLSim.h"
#include "DocumentIF.h"
#include "Common.h"
#include "PlugInSimDlg.h"

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


namespace {
//-----------------------------------------------------------------------------
// 定数
//-----------------------------------------------------------------------------
#define VERSION			_T("HTMLシミュレータ ver2.3")			// バージョン情報
#define	WINDOW_TITLE	_T("したらびあん　HTMLシミュレータ")	// ブラウザのタイトル


/*-----------------------------------------------------------------------------
[名称]	スレッドパラメータ
[用途]	HTML構築スレッドに渡すパラメータ。
[備考]	なし
-----------------------------------------------------------------------------*/
typedef struct {
	BOOL			bBrowserUpdate;		// ブラウザ更新 [-] (TRUE:更新、FALSE:更新しない)
	EHTMLMode		eHTMLMode;			// シミュレートモード [-] (-)
	CStringList*	plistStrz;			// 文書文字列群 [-] (-)
} TSimulateThreadParam;

}

//-----------------------------------------------------------------------------
// ファイル内変数
//-----------------------------------------------------------------------------
volatile static	BOOL f_Running = FALSE;		// HTML構築スレッド実行中 [-] (TRUE:実行中、FALSE:アイドル中)
volatile static	long f_iRequestNumber = 0;	// HTML構築スレッド番号 [-] (-)
static CWnd* f_pwndSim = NULL;				// 表示更新対象ブラウザ [-] (-)


//-----------------------------------------------------------------------------
// ファイル内関数
//-----------------------------------------------------------------------------
CString GetTmpPathName();
CWnd*	GetSimWindow();
BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam );
UINT f_RefreshCore( void* pParam );


/*-----------------------------------------------------------------------------
[名称]	テンポラリファイルパス取得
[機能]	テンポラリファイルパスを取得する。
[戻値]	なし
[備考]	Windouzuno テンポラリフォルダを使用する。
-----------------------------------------------------------------------------*/
CString GetTmpPathName()
{
	const	iBuffSize = 256;
	char	pPath[ iBuffSize ];
	::GetTempPath( iBuffSize, pPath );

	CString		str( pPath );
	::GetLongPathName( str, pPath, iBuffSize );
	str = pPath;

	str += _T("shitarabian_tmp.htm");
	return( str );
}


/*-----------------------------------------------------------------------------
[名称]	ブラウザ取得
[機能]	すでに開いているブラウザを取得する。
[戻値]	なし
[備考]	ウィンドウタイトルで探してる。please!
-----------------------------------------------------------------------------*/
CWnd*	GetSimWindow()
{
	f_pwndSim = NULL;

	::EnumWindows(
		EnumWindowsProc,
		(LPARAM)NULL
	);
	return( f_pwndSim );
}


/*-----------------------------------------------------------------------------
[名称]	ブラウザ検索
[機能]	すでに開いているブラウザを取得する。
[戻値]	なし
[備考]	ウィンドウ列挙コールバック
-----------------------------------------------------------------------------*/
BOOL CALLBACK EnumWindowsProc(
	HWND	hwnd,		// トップレベルウィンドウ（親を持たないウィンドウ）のハンドルが格納されます。
	LPARAM	lParam		// アプリケーションによって定義された値
)
{
	CWnd*	pWnd = CWnd::FromHandle( hwnd );
	CString	strCaption;
	pWnd->GetWindowText( strCaption );
	long	lKeywordLength = strlen( WINDOW_TITLE );
	if ( strCaption.Left(lKeywordLength) == WINDOW_TITLE )
	{
		f_pwndSim = pWnd;
	}
	return( TRUE );
}


/*-----------------------------------------------------------------------------
[名称]	HTML更新（ワーカスレッド）
[機能]	HTML内容を再構築、必要であればブラウザを更新する。
[戻値]	なし
[備考]	処理中のスレッドがある場合は、待つ。
		待機中に新しいスレッドが立った場合は、処理せずに終了する。
		これによって、実際に処理をするのは最新スレッドのみ。
-----------------------------------------------------------------------------*/
UINT f_RefreshCore(
	void*	pParam
)
{
	// 受け受け番号取得
	f_iRequestNumber++;
	long	iRequestNumber = f_iRequestNumber;

	// 他のスレッドの終了を待つ
	BOOL	bAborted = FALSE;
	while( f_Running )
	{
		// 新しいスレッドが立ったのでこのスレッドの処理は不要となった
		if ( iRequestNumber != f_iRequestNumber )
		{
			bAborted = TRUE;
			break;
		}
		Sleep(100);	// ひっきりなしにポーリングするのもなんなので適当なウェイト
	}

	f_Running = TRUE;

	// 更新処理
	TSimulateThreadParam*	pSimulate = (TSimulateThreadParam*)pParam;
	if ( !bAborted )
	{
		// HTMLの更新
		if ( pSimulate->plistStrz != NULL )
		{
			BOOL	bSuccess;
			bSuccess = CShitarabianPlugInHTMLSim::Simulate(
				pSimulate->eHTMLMode,
				pSimulate->plistStrz
			);

			if ( bSuccess )
			if ( pSimulate->bBrowserUpdate )
			{
				// ウィンドウを列挙する。
				// ウィンドウがブラウザであれば、リロードさせる
				CWnd*	pSim = GetSimWindow();
				if ( pSim != NULL )
				if ( ::IsWindow( pSim->m_hWnd) )
				{
					pSim->SendMessageToDescendants( WM_MOUSEMOVE, 0, 0 );
				}
			}
		}
		f_Running = FALSE;
	}

	// 確保されたメモリブロックの解放
	delete pSimulate->plistStrz;
	delete pSimulate;

	return( 0 );
}


//-----------------------------------------------------------------------------
// メンバ関数
//-----------------------------------------------------------------------------


/*-----------------------------------------------------------------------------
[名称]	コンストラクタ
[機能]	インスタンスの生成。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
CShitarabianPlugInHTMLSim::CShitarabianPlugInHTMLSim()
: m_eHTMLMode( HTMLMode2Ch )
, m_bAutoUpdate( TRUE )
, m_bInitialized( FALSE )
{
}


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


/*-----------------------------------------------------------------------------
[名称]	プラグイン名称取得
[機能]	プラグイン名称の取得。
[戻値]	const char*		プラグイン名称 [-] (-)
[備考]	なし
-----------------------------------------------------------------------------*/
const char* CShitarabianPlugInHTMLSim::GetPlugInName()
{
	return( VERSION );
}


/*-----------------------------------------------------------------------------
[名称]	HTML更新
[機能]	HTML内容更新のスレッドを起動する。
[戻値]	なし
[備考]	確保したメモリは f_RefreshCore() 内で解放される。
-----------------------------------------------------------------------------*/
void CShitarabianPlugInHTMLSim::RefreshCore(
	CDocumentIF*	pDoc,			// I  :ドキュメントID [-] (-) :-
	BOOL			bForceUpdate	// I  :ブラウザ強制更新 [-] (TRUE:更新、FALSE:更新しない) :FALSE
)
{
	// 文書の取得
	CStringList*		plistStrz = new CStringList;
	{
		plistStrz->RemoveAll();
		// please!ここに時間がかかるわけだが。どうしょうもない？
		for( int idxLine=0 ; idxLine<pDoc->GetLineCount() ; idxLine++ )
		{
			plistStrz->AddTail( pDoc->GetLine( idxLine ) );
		}
	}

	// スレッドパラメータの構築
	TSimulateThreadParam	*pParam = new TSimulateThreadParam;
	{
		pParam->bBrowserUpdate = m_bAutoUpdate || bForceUpdate;
		pParam->eHTMLMode = m_eHTMLMode;
		pParam->plistStrz = plistStrz;
	}

	// スレッドの開始
	::AfxBeginThread(
		f_RefreshCore,
		pParam
	);
}


/*-----------------------------------------------------------------------------
[名称]	設定ハンドラ
[機能]	設定ダイアログを表示する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CShitarabianPlugInHTMLSim::OnSetting(
	CDocumentIF*	pDocIF		// I  :ドキュメントID [-] (-) :-
)
{
	// 設定ダイアログ用意
	CPlugInSimDlg	dlg;
	{
		dlg.m_strTmpPathName = GetTmpPathName();
		dlg.m_eHTMLMode = m_eHTMLMode;
		dlg.m_bAutoUpdate = m_bAutoUpdate;
		dlg.m_bToolBar = m_bToolBar;
	}

	// HTML再構築
	RefreshCore( pDocIF );

	// 設定ダイアログ表示
	if ( dlg.DoModal() == IDOK )
	{
		// 設定取り込み
		m_eHTMLMode = dlg.m_eHTMLMode;
		m_bAutoUpdate = dlg.m_bAutoUpdate;
		m_bToolBar = dlg.m_bToolBar;
		if ( m_bToolBar )
		{
			pDocIF->EntryToolBar( IDR_SIM_TOOLBAR );
			pDocIF->ShowToolBar( TRUE );
		}
		else
		{
			pDocIF->ShowToolBar( FALSE );
		}
	}
}


/*-----------------------------------------------------------------------------
[名称]	HTML生成
[機能]	HTMLを生成する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL	CShitarabianPlugInHTMLSim::Simulate(
	EHTMLMode		eMode,			// I  :シミュレートモード [-] (-) :-
	CStringList*	plistStrz		// I  :文書文字列群 [-] (-) :-
)
{
	BOOL	bSuccess;

	CString		strMode;
	switch( eMode )
	{
	case HTMLModeShitaraba:
		bSuccess = RemakeHTMLShitaraba( plistStrz );
		strMode = _T("したらば");
		break;

	case HTMLMode2Ch:
	default:
		bSuccess = RemakeHTML2Ch( plistStrz );
		strMode = _T("２ちゃんねる");
		break;
	}

	return( bSuccess );
}


/*-----------------------------------------------------------------------------
[名称]	HTML生成（したらば）
[機能]	したらば用のHTMLを生成する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CShitarabianPlugInHTMLSim::RemakeHTMLShitaraba(
	CStringList*	plistStrz		// I  :文書文字列群 [-] (-) :-
)
{
	try
	{
		// ファイルオープン
		CStdioFile	fileDst(
			GetTmpPathName(),
			CFile::modeWrite | CFile::modeCreate
		);

		// ヘッダ部生成
		GenerateHead( &fileDst, "[したらば]" );

		// ボディ部開始
		fileDst.WriteString( "<body bgcolor=\"#FF9933\">");
		fileDst.WriteString( "<DL><a name=1></a><table border cellspacing=0 cellpadding=10 width=95% bgcolor=\"#EEEEEE\" align=center>" );
		fileDst.WriteString( "<tr><td>" );
		fileDst.WriteString( "<font color=red size=4><b>★したらばスレッド</b></font><br>\n" );

		// 名前欄生成
		{
			COleDateTime	time = COleDateTime::GetCurrentTime();
			CString		strNameLine;
			strNameLine.Format(
				"<DT><a href=#1>1</a>　　<B>%s</B>　　%04d/%02d/%02d(%s) %02d:%02d</DT>	<DD>\n",
				"名無しさん",
				time.GetYear(), time.GetMonth(), time.GetDay(), ::DayOfWeek( time.GetDayOfWeek() ),
				time.GetHour(), time.GetMinute()
			);
			fileDst.WriteString( strNameLine );
		}

		// 本文生成
		{
			POSITION	pos = plistStrz->GetHeadPosition();
			while( pos != NULL )
			{
				CString		str = plistStrz->GetNext( pos );

				// したらばエンコード
				str.Replace( _T("&"), _T("&amp;") );
				str.Replace( _T("<"), _T("&lt;") );
				str.Replace( _T(">"), _T("&gt;") );
				str.Replace( _T(" "), _T("&nbsp;") );

				fileDst.WriteString( str + "<br>\n" );
			}
		}

		// HTML終了
		fileDst.WriteString( "</DD></td></tr></table></body></html>\n" );
	}
	catch( CFileException* e )
	{
		e->Delete();
		return( FALSE );
	}

	return( TRUE );
}


/*-----------------------------------------------------------------------------
[名称]	HTML生成（２ｃｈ）
[機能]	２ｃｈ用のHTMLを生成する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL CShitarabianPlugInHTMLSim::RemakeHTML2Ch(
	CStringList*	plistStrz		// I  :文書文字列群 [-] (-) :-
)
{
	try
	{
		// ファイルオープン
		CStdioFile	fileDst(
			GetTmpPathName(),
			CFile::modeWrite | CFile::modeCreate
		);

		// ヘッダ部生成
		GenerateHead( &fileDst, "[2ch]" );

		// ボディ部開始
		{
			fileDst.WriteString( "<body bgcolor=\"#CCFFCC\">");
			fileDst.WriteString( "<DL><a name=1></a><table border cellspacing=0 cellpadding=10 width=95% bgcolor=\"#EFEFEF\" align=center>" );
			fileDst.WriteString( "<tr><td>" );
			fileDst.WriteString( "<font color=red size=4><b>２ｃｈスレッド</b></font><br>\n" );
		}

		// 名前欄生成
		{
			COleDateTime	time = COleDateTime::GetCurrentTime();
			CString		strNameLine;
			strNameLine.Format(
				"<DT>1 名前： <B><font color=green>%s</font></B> 投稿日： %04d/%02d/%02d(%s) %02d:%02d</DT>	<DD>\n",
				"（　´∀｀）さん",
				time.GetYear(), time.GetMonth(), time.GetDay(), ::DayOfWeek( time.GetDayOfWeek() ),
				time.GetHour(), time.GetMinute()
			);
			fileDst.WriteString( strNameLine );
		}

		// 本文生成
		{
			POSITION	pos = plistStrz->GetHeadPosition();
			while( pos != NULL )
			{
				CString		str = plistStrz->GetNext( pos );

				// 2chエンコード
				str.Replace( _T("<"), _T("&lt;") );
				str.Replace( _T(">"), _T("&gt;") );

				fileDst.WriteString( str + "<br>\n" );
			}
		}

		// HTML終了
		fileDst.WriteString( "</DD></td></tr></table></body></html>\n" );
	}
	catch( CFileException* e )
	{
		e->Delete();
		return( FALSE );
	}

	return TRUE;
}


/*-----------------------------------------------------------------------------
[名称]	HTMLヘッダ生成
[機能]	HTMLのヘッダ部を生成する。
[戻値]	なし
[備考]	外部から更新をかけるため、JAVAスクリプトを埋め込む。
		マウスカーソルが(0,0)にMOVEしたらリロードするという‥‥
		更新したいときはブラウザに WM_MOUSEMOVEをポストする。
		please!  もうちょいマシな方法は？
-----------------------------------------------------------------------------*/
BOOL CShitarabianPlugInHTMLSim::GenerateHead(
	CStdioFile*		pfileDst,		//   O:出力先ファイル [-] (-) :-
	CString			strTitle		// I  :タイトル文字列 [-] (-) :-
)
{
	pfileDst->WriteString( "<html><head>\n" );
	pfileDst->WriteString( "<meta http-equiv=\"Content-Language\" content=\"ja\">\n" );
	pfileDst->WriteString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=x-sjis\">\n" );
	pfileDst->WriteString( "<title>");
	pfileDst->WriteString( (CString)WINDOW_TITLE );
	pfileDst->WriteString( strTitle );
	pfileDst->WriteString( "</title>\n" );
	pfileDst->WriteString( "<script language=\"JavaScript\">\n" );
	pfileDst->WriteString( "<!--\n" );
	pfileDst->WriteString( "function fff(){ if( event.x == 0 ){if( event.y == 0 ){document.execCommand('Refresh');}}};\n" );
	pfileDst->WriteString( "window.document.onmousemove =fff;\n" );
	pfileDst->WriteString( "// -->\n" );
	pfileDst->WriteString( "</script>\n" );
	pfileDst->WriteString( "</head>\n" );

	return( TRUE );
}


/*-----------------------------------------------------------------------------
[名称]	ツールボタン押下ハンドラ
[機能]	ツールボタン謳歌による処理。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CShitarabianPlugInHTMLSim::OnToolButton(
	CDocumentIF*	pDocIF,			// I  :ドキュメントIF [-] (-) :-
	int				idxButton		// I  :ボタン番号  [-] (-) :-
)
{
	switch( idxButton )
	{
	case 0:		// 起動
		{
			RefreshCore( pDocIF );
			ShellExecute( HWND_DESKTOP, NULL, GetTmpPathName(), NULL, NULL, SW_SHOWNORMAL );
		}
		break;

	case 1:		// 自動更新
		{
			m_bAutoUpdate = !m_bAutoUpdate;
			RefreshCore( pDocIF );
		}
		break;

	case 2:		// モード切替
		{
			switch( m_eHTMLMode )
			{
			case HTMLModeShitaraba:
				m_eHTMLMode = HTMLMode2Ch;
				break;

			case HTMLMode2Ch:
			default:
				m_eHTMLMode = HTMLModeShitaraba;
				break;
			}
			RefreshCore( pDocIF, TRUE );
		}
		break;

	default:
		ASSERT( FALSE );
		break;
	}
}


/*-----------------------------------------------------------------------------
[名称]	UI表示更新ハンドラ
[機能]	ツールボタンのUIを更新する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CShitarabianPlugInHTMLSim::OnToolButtonUI(
	CDocumentIF*	pDocIF,			// I  :ドキュメントIF [-] (-) :-
	int				idxButton,		// I  :ボタン番号  [-] (-) :-
	CCmdUI*			pCmdUI			//   O:UI [-] (-) :-
)
{
	switch( idxButton )
	{
	case 0:	// 起動
		{
			pCmdUI->Enable( TRUE );
		}
		break;

	case 1:	// 自動更新
		{
			pCmdUI->SetCheck( m_bAutoUpdate );
			pCmdUI->Enable( TRUE );
		}
		break;

	case 2:	// モード切替
		{
			pCmdUI->Enable( TRUE );
		}
		break;

	default:
		return;
		break;
	}
}


/*-----------------------------------------------------------------------------
[名称]	設定読み込みハンドラ
[機能]	設定ファイルから設定を読み込み、反映する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void	CShitarabianPlugInHTMLSim::OnLoad(
	CDocumentIF*	pDocIF		// I  :ドキュメントIF [-] (-) :-
)
{
	Load( pDocIF );

	// HTML再構築
	RefreshCore( pDocIF );

	// ツールバーの初期化
	if ( m_bToolBar )
	{
		pDocIF->EntryToolBar( IDR_SIM_TOOLBAR );
		pDocIF->ShowToolBar( TRUE );
	}
	else
	{
		pDocIF->ShowToolBar( FALSE );
	}
}


/*-----------------------------------------------------------------------------
[名称]	設定読み込み
[機能]	設定ファイルから設定を読み込む。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CShitarabianPlugInHTMLSim::Load(
	CDocumentIF*	pDocIF		// I  :ドキュメントIF [-] (-) :-
)
{
	try
	{
		CString	strIniFolderPath = pDocIF->GetIniPath();
		CString	strIniFilePath = strIniFolderPath + "htmlsim.ini";

		CStdioFile	File( strIniFilePath, CFile::modeRead | CFile::shareDenyNone );

		CString		str;
		File.ReadString( str );
		m_bToolBar = ::StringToBool( str );
		File.ReadString( str );
		m_bAutoUpdate = ::StringToBool( str );
		File.ReadString( str );
		m_eHTMLMode = (EHTMLMode)(::StringToLong( str ));

		m_bInitialized = TRUE;
	}
	catch( CFileException* e )
	{
		e->Delete();
	}
}


/*-----------------------------------------------------------------------------
[名称]	設定保存ハンドラ
[機能]	設定ファイルへ設定を保存する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void	CShitarabianPlugInHTMLSim::OnSave(
	CDocumentIF*	pDocIF		// I  :ドキュメントIF [-] (-) :-
)
{
	Save( pDocIF );
}


/*-----------------------------------------------------------------------------
[名称]	設定保存
[機能]	設定ファイルへ設定を保存する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CShitarabianPlugInHTMLSim::Save(
	CDocumentIF*	pDocIF		// I  :ドキュメントIF [-] (-) :-
)
{
	try
	{
		CString	strIniFolderPath = pDocIF->GetIniPath();
		CString	strIniFilePath = strIniFolderPath + "htmlsim.ini";

		CStdioFile	File( strIniFilePath, CFile::modeCreate|CFile::modeWrite );
		File.WriteString( ::BoolToString( m_bToolBar )+"\n" );
		File.WriteString( ::BoolToString( m_bAutoUpdate )+"\n" );
		File.WriteString( ::LongToString( (int)m_eHTMLMode )+"\n" );
	}
	catch( CFileException* e )
	{
		e->Delete();
	}
}


/*-----------------------------------------------------------------------------
[名称]	編集ハンドラ
[機能]	文書編集に応じてHTMLを更新する。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CShitarabianPlugInHTMLSim::OnEdit(
	CDocumentIF*	pDoc
)
{
	// ブラウザが開いているときのみHTML再構築する
	CWnd*	pSim = GetSimWindow();
	if ( m_bInitialized )
	if ( pSim != NULL )
	{
		RefreshCore( pDoc );
	}
}
