/* Tera Term
 Copyright(C) 1994-1998 T. Teranishi
 All rights reserved. */

/* TTMACRO.EXE, main */

#include "stdafx.h"
#include "teraterm.h"
#include "ttm_res.h"
#include "ttmdlg.h"
#include "ttl.h"
#include "ttmparse.h"
#include "ttmdde.h"

#include "ttmbuff.h"
#include "ttmlib.h"

#include "ttlib.h"

#include "wait4all.h"
#include "ttmmain.h"
#include "MacroLog.h"
// #include <pthread.h> 
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

SrunMacroParam gsThreadParam;
std::string g_EngineName;

static bool ReadRegistry(HKEY Hive, const char* Path, const char* Item, char* Buffer, unsigned BufferCount)
{
	bool Result = false;

	HKEY Key;
	if (RegOpenKeyEx(Hive, Path, 0, KEY_READ, &Key) == ERROR_SUCCESS)
	{
		memset(Buffer, 0, BufferCount);

		DWORD dwType = REG_SZ;
		DWORD dwDataLen = (BufferCount - 1) * sizeof(wchar_t); //※バイト単位！

		Result = (RegQueryValueEx(Key, Item, NULL, &dwType, reinterpret_cast<LPBYTE>(Buffer), &dwDataLen) == ERROR_SUCCESS);

		RegCloseKey(Key);
	}
	return Result;
}
static void MacroError(BSTR Description, BSTR Source, void *Data)
{
	CCtrlWindow *win = reinterpret_cast<CCtrlWindow*>(Data);
	if ( gsThreadParam.bErrOutFlg ){
		MessageBoxW(win->GetSafeHwnd(), Description, Source, MB_ICONERROR);
	}
}

static unsigned __stdcall runMacroProc( LPVOID lpParameter )
{
	SrunMacroParam* pParam = (SrunMacroParam*) lpParameter;
	pParam->pEngine = NULL;
	ResetEvent(pParam->hEvent3);
	bool bRet = false;
	CoInitializeEx(0,COINIT_MULTITHREADED);
	CWSHClient* Engine;
	Engine = new CWSHClient(strtools::MStrToWStr(g_EngineName).c_str(), MacroError, pParam->mainFrame);
	if(Engine->m_Valid)
	{
		//インタフェースオブジェクトの登録
		CWSHIfObj* obj = new CWSHIfObj( L"Mcro", true);
		obj->ReadyMethods(pParam->mainFrame );
		Engine->AddInterfaceObject( obj );
		bool bRet = Engine->Execute(pParam);
		MCRLOGF(RT_D_DEBUG, "Engine->Execute() bRet=%d", bRet);
	}
	// TTLStatus=IdTTLEnd;
	OutputDebugString(_T("AbortMacro: Exit\n"));
	delete Engine;
	pParam->pEngine = NULL;
	CoUninitialize();
	TTLStatus=IdTTLEnd;
	gsThreadParam.ID = F_FUNCTION_END;
	gsThreadParam.Source = NULL;
	gsThreadParam.Source2 = NULL;
	SetEvent(gsThreadParam.hEvent1);
	// Sleep(10000);
	MCRLOGF(RT_D_DEBUG, "runMacroProcexit TTLStatus=%d", TTLStatus);
	//delete Engine;
	SetEvent(gsThreadParam.hEvent3);
	return 0;
}

bool		jsRun;
// CCtrlWindow dialog
CCtrlWindow::CCtrlWindow()
	: CDialog()
{

	m_hIcon = AfxGetApp()->LoadIcon(IDI_TTMACRO);
	/*
	int nPrity = sched_get_priority_max(SCHED_FIFO);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_max(SCHED_FIFO)=%d", nPrity);
	nPrity = sched_get_priority_min(SCHED_FIFO);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_min(SCHED_FIFO)=%d", nPrity);
	nPrity = sched_get_priority_max(SCHED_RR);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_max(SCHED_RR)=%d", nPrity);
	nPrity = sched_get_priority_min(SCHED_RR);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_min(SCHED_RR)=%d", nPrity);
	nPrity = sched_get_priority_max(SCHED_OTHER);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_max(SCHED_OTHER)=%d", nPrity);
	nPrity = sched_get_priority_min(SCHED_OTHER);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_min(SCHED_OTHER)=%d", nPrity);
	*/
	/*
	nPrity = sched_get_priority_max(SCHED_BATCH);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_max(SCHED_BATCH)=%d", nPrity);
	nPrity = sched_get_priority_min(SCHED_BATCH);
	MCRLOGF(RT_D_DEBUG, "sched_get_priority_min(SCHED_BATCH)=%d", nPrity);
	*/



}

BOOL CCtrlWindow::Create()
{
	if (! CDialog::Create(CCtrlWindow::IDD, NULL)) {
		PostQuitMessage(0);
		return FALSE;
	}
	return TRUE;
}

// TTMACRO main engine
BOOL CCtrlWindow::OnIdle()
{
	int ResultCode;
	char Temp[2];
	WORD Err;
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnIdle()TTLStatus=%d", TTLStatus);
	if (TTLStatus==IdTTLEnd) {
		if (MacroMode == ModeJscript){
			if (gsThreadParam.hThread > 0){
				MCRLOGF(RT_D_DEBUG, "WaitForSingleObject();hThread=(0x%X)", gsThreadParam.hThread);
				WaitForSingleObject(gsThreadParam.hThread, INFINITE);
				CloseHandle(gsThreadParam.hThread);
			}
			WaitForSingleObject(gsThreadParam.hEvent3, INFINITE);
			CloseHandle(gsThreadParam.hEvent3);
			if (gsThreadParam.hEvent1){
				CloseHandle(gsThreadParam.hEvent1);
				CloseHandle(gsThreadParam.hEvent2);
			}
		}
		::DestroyWindow(m_hStatus);
		DestroyWindow();
		return FALSE;
	}

	SendSync(); // for sync mode

	if( MacroMode==ModeJscript )
	{
		if (!jsRun) {



			//ソース読み込み -> m_Source
			std::string      sFileName(FileName);
			CFileMgr fMgr(strtools::MStrToWStr(sFileName));
			MCRLOGF(RT_D_DEBUG,"open file[%s] from fMgr.open().",sFileName.c_str());
			int ret = fMgr.open();
			if (ret < 0) {

				if (-1 == ret) {
				    //std::string temp = strtools::format(L"the [%s] not exist%d", FileName);
					std::string temp = "the [" + sFileName + "] not exist";
					MCRLOGF(RT_D_DEBUG, temp.c_str());
				    AfxMessageBox(temp.c_str(), MB_OK | MB_ICONINFORMATION);
				}
				else{
					MCRLOGF(RT_D_DEBUG,"file[%s] error(%d)",sFileName.c_str(),ret);
				}
				MacroMode=ModeTTL;
				TTLStatus = IdTTLEnd;
				return FALSE;
			}
			gsThreadParam.m_Source = fMgr.pBuf;
			gsThreadParam.bErrOutFlg = true;

			// sThreadParam.pEngine = m_Engine;
			gsThreadParam.nCancelTimer = 10;
			gsThreadParam.mainFrame = this;
			gsThreadParam.idx = 0;

			HANDLE hThread = NULL;
			unsigned int nThreadId = 0;
			gsThreadParam.HMainWin=GetSafeHwnd();

			gsThreadParam.hEvent1 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
			gsThreadParam.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
			gsThreadParam.hEvent3 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
			gsThreadParam.TimeOut = -1;
			MCRLOGF(RT_D_DEBUG, "hEvent1=(0x%X),hEvent2=(0x%X)", gsThreadParam.hEvent1, gsThreadParam.hEvent2);
			gsThreadParam.hThread = (HANDLE)_beginthreadex(NULL, 0, runMacroProc, (LPVOID)&gsThreadParam, 0, &nThreadId);
			DebugOut(_T("Start _beginthreadex hThread=0x%08x,0x%08x\n"), gsThreadParam.hThread, nThreadId);
			MCRLOGF(RT_D_DEBUG, "Start _beginthreadex hThread=(0x%X),nThreadId=(0x%X)", gsThreadParam.hThread, nThreadId);


/*

			CWSHClient* Engine;
			
			Engine = new CWSHClient(strtools::MStrToWStr(g_EngineName).c_str(), MacroError, this);
			if(Engine->m_Valid)
			{
				//インタフェースオブジェクトの登録
				CWSHIfObj* obj = new CWSHIfObj( L"Editor", true);
				obj->ReadyMethods(this );
				Engine->AddInterfaceObject( obj );
				bool bRet = Engine->Execute(m_Source.c_str());
			}
*/
			jsRun=true;
			return TRUE;
		}
		MCRLOGF(RT_D_DEBUG, "before run MsgWaitForMultipleObjects");
		bool bRet = FALSE;
		DWORD dwResult = MsgWaitForMultipleObjects( 1, &gsThreadParam.hEvent1, FALSE, INFINITE, QS_ALLINPUT );
		if(dwResult == WAIT_OBJECT_0){
			ResetEvent(gsThreadParam.hEvent1);
			MCRLOGF(RT_D_DEBUG, "LOWORD(gsThreadParam.ID)=[%s(0x%X)]", CSMacroMgr::GetFuncInfoByID(gsThreadParam.ID)->m_nFuncID_name, LOWORD(gsThreadParam.ID));
			if (gsThreadParam.ID != F_FUNCTION_END){
				char msg[100 + 3 + 1];
				memset(msg, '.', sizeof(msg));
				msg[103] = '\0';
				if (gsThreadParam.Source){
					strncpy_s(msg, 100, gsThreadParam.Source, _TRUNCATE);
					MCRLOGF(RT_D_DEBUG, "gsThreadParam.Source=[%s]", msg);
				}
				else{
					strncpy_s(msg, 100, "NULL", _TRUNCATE);
					MCRLOGF(RT_D_DEBUG, "gsThreadParam.Source=%s", msg);
				}
				memset(msg, '.', sizeof(msg));
				msg[103] = '\0';
				if (gsThreadParam.Source2){
					strncpy_s(msg, 100, gsThreadParam.Source2, _TRUNCATE);
					MCRLOGF(RT_D_DEBUG, "gsThreadParam.Source2=[%s]", msg);
				}
				else{
					strncpy_s(msg, 100, "NULL", _TRUNCATE);
					MCRLOGF(RT_D_DEBUG, "gsThreadParam.Source2=%s", msg);
				}
			}
			switch(LOWORD(gsThreadParam.ID))
			{
				
				case F_SCPSEND:
				case F_SCPRECV:
					{
						char  Cmd;
						if ( LOWORD(gsThreadParam.ID) == F_SCPSEND ){
							Cmd = CmdScpSend;
						}
						else if ( LOWORD(gsThreadParam.ID) == F_SCPRECV ){
							Cmd = CmdScpRcv;
						}
						SetFile(gsThreadParam.Source);
						if (gsThreadParam.Source2){
							SetSecondFile(gsThreadParam.Source2);
						}
						else{
							SetSecondFile("");
						}
						SendCmnd(Cmd,IdTTLWaitCmndEnd);
						bRet = true;
						break;
					}
				case F_CONNECT:
				case F_CHANGEDIR:
				case F_SETTITLE:
				case F_CALL_MENU:
				case F_LOGPAUSE:
				case F_LOGSTART:
				case F_LOGCLOSE:
					{
						char  Cmd;
						bRet = true;
						switch(LOWORD(gsThreadParam.ID))
						{
							case F_CONNECT:
								Cmd=CmdConnect; break;
							case F_CHANGEDIR:
								Cmd=CmdChangeDir; break;
							case F_SETTITLE:
								Cmd=CmdSetTitle; break;
							case F_CALL_MENU:
								Cmd=CmdCallMenu; break;
							case F_LOGPAUSE:
								Cmd=CmdLogPause; break;
							case F_LOGSTART:
								Cmd=CmdLogStart; break;
							case F_LOGCLOSE:
								Cmd=CmdLogClose; break;
							default:
								Cmd='\0';	bRet = FALSE; break;
						}
						if (gsThreadParam.Source){
							SetFile(gsThreadParam.Source);
						}
						Err = SendCmnd(Cmd, 0);
						break;
					}
				case F_SETDEBUG:
					{
						SetDebug(gsThreadParam.flag[0]);
						bRet = true;
						break;
					}
				case F_SENDLN:
					{
						
						DDEOut(gsThreadParam.Source);
						DDEOut("\r\n");
						DDESend();
						//DDEJSSend(gsThreadParam.Source,gsThreadParam.SourceLength,1000);
						//DDEJSSend("\r\n", 2, 1000);
						bRet = true;
						break;
					}
				case F_SEND:
					{
						DDEOut(gsThreadParam.Source);
						DDESend();
						bRet = true;
						break;
					}
				case F_LOGOPEN:
					{
						char ret[2];
						SetFile(gsThreadParam.Source);
						SetBinary(gsThreadParam.flag[0]);
						SetAppend(gsThreadParam.flag[1]);
						memset(ret, 0, sizeof(ret));
						Err = GetTTParam(CmdLogOpen,ret,sizeof(ret));
						if (Err==0) {
							if (ret[0] == 0x31) {
								gsThreadParam.flag[0]=0;
							}
							else {
								gsThreadParam.flag[0]=1;
							}
						}
						bRet = true;
						break;
					}
				case F_GETTITLE:
					{
						
						Err = GetTTParam(CmdGetTitle,gsThreadParam.Source,gsThreadParam.SourceLength);

						bRet = true;
						break;
					}
				case F_WAIT:
				{
							   //
							   bRet = true;
							   break;
				}
				case F_FUNCTION_END:
				{
						if (gsThreadParam.pEngine && gsThreadParam.hThread>0){
							TTLStatus = IdTTLEnd;
							gsThreadParam.bErrOutFlg=false;
							KillTimer(IdTimeOutTimer);
							MCRLOGF(RT_D_DEBUG, "gsThreadParam.pEngine->InterruptScriptThread()");
							int ret = gsThreadParam.pEngine->InterruptScriptThread(SCRIPTTHREADID_ALL, NULL, 0);
							gsThreadParam.hThread = 0;
							SetEvent(gsThreadParam.hEvent2);
						}
				}
				default:
					{
						//
						bRet = FALSE;
						break;
					}
			}
			if (gsThreadParam.ID != F_FUNCTION_END){
				MCRLOGF(RT_D_DEBUG, "SetEvent(gsThreadParam.hEvent2)");
				SetEvent(gsThreadParam.hEvent2);
			}
			else{
				bRet = true;
			}
		}
		else{
			MSG msg;
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
				TranslateMessage(&msg);
				MCRLOGF(RT_D_DEBUG, "hwnd=0x%X,message=0x%X,wParam=0x%X,lParam=0x%X", msg.hwnd, msg.message, msg.wParam, msg.lParam );
				DispatchMessage(&msg);
			}
			bRet = true;
		}
		Sleep(200);
		MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnIdle() end  bRet=%d", bRet);
		return bRet;
	}

	if (OutLen>0) {
		DDESend();

		// Windows2000/XP（シングルプロセッサ環境）においてマクロ実行中にCPU使用率が100%になってしまう
		// 現象への暫定処置。ttermpro.exeへのDDE送信後に無条件に100ミリ秒のスリープを追加。
		// (2005.5.25 yutaka)
		// 以下の暫定処置は解除。
		// (2006.10.13 yutaka)
		// 以下の暫定処置は復活。Tera Term本体側のOnIdle()において、XTYP_ADVREQが頻繁に飛ぶことが
		// CPUをストールさせてしまう要因と思われるのだが、根本的原因はいまだ不明。
		// (2006.11.4 yutaka)
		// 以下の暫定処置は DDESend() で行うため、削除。
		// (2006.11.6 yutaka)
#if 0
		Sleep(100);
#endif
		return TRUE;
	}
	else if (! Pause && (TTLStatus==IdTTLRun)) {
		Exec();

		// 更新対象のマクロコマンドの場合のみ、ウィンドウに更新指示を出す。
		// 毎度 WM_PAINT を送っているとマクロの動作が遅くなるため。(2006.2.24 yutaka)
		if (IsUpdateMacroCommand()) {
			Invalidate(TRUE);
		}
		return TRUE;
	}
	else if (TTLStatus==IdTTLWait) {
		ResultCode = Wait();
		if (ResultCode>0) {
			KillTimer(IdTimeOutTimer);
			TTLStatus = IdTTLRun;
			LockVar();
			SetResult(ResultCode);
			UnlockVar();
			ClearWait();
			return TRUE;
		}
		else if (ComReady==0) {
			SetTimer(IdTimeOutTimer,0, NULL);
		}
	}
	else if (TTLStatus==IdTTLWaitLn) {
		ResultCode = Wait();
		if (ResultCode>0) {
			LockVar();
			SetResult(ResultCode);
			UnlockVar();
			Temp[0] = 0x0a;
			Temp[1] = 0;
			if (CmpWait(ResultCode,Temp)==0) { // new-line is received
				KillTimer(IdTimeOutTimer);
				ClearWait();
				TTLStatus = IdTTLRun;
				LockVar();
				SetInputStr(GetRecvLnBuff());
				UnlockVar();
			}
			else { // wait new-line
				ClearWait();
				SetWait(1,Temp);
				TTLStatus = IdTTLWaitNL;
			}
			return TRUE;
		}
		else if (ComReady==0) {
			SetTimer(IdTimeOutTimer,0, NULL);
		}
	}
	else if (TTLStatus==IdTTLWaitNL) {
		ResultCode = Wait();
		if (ResultCode>0) {
			KillTimer(IdTimeOutTimer);
			TTLStatus = IdTTLRun;
			LockVar();
			SetInputStr(GetRecvLnBuff());
			UnlockVar();
			return TRUE;
		}
		else if (ComReady==0) {
			SetTimer(IdTimeOutTimer,0, NULL);
		}
	}
	else if (TTLStatus==IdTTLWait2) {
		if (Wait2()) {
			KillTimer(IdTimeOutTimer);
			TTLStatus = IdTTLRun;
			LockVar();
			SetInputStr(Wait2Str);
			SetResult(1);
			UnlockVar();
			return TRUE;
		}
		else if (ComReady==0) {
			SetTimer(IdTimeOutTimer,0, NULL);
		}
	}
	else if (TTLStatus==IdTTLWaitN) {
		if (WaitN()) {
			KillTimer(IdTimeOutTimer);
			TTLStatus = IdTTLRun;
			LockVar();
			SetResult(1);
			SetInputStr(GetRecvLnBuff());
			UnlockVar();
			ClearWaitN();
			return TRUE;
		}
		else if (ComReady==0) {
			SetTimer(IdTimeOutTimer,0, NULL);
		}
	}
	else if (TTLStatus==IdTTLWait4all) {
		ResultCode = Wait4all();
		if (ResultCode>0) {
			KillTimer(IdTimeOutTimer);
			TTLStatus = IdTTLRun;
			LockVar();
			SetResult(ResultCode);
			UnlockVar();
			ClearWait();
			return TRUE;
		}
		else if (ComReady==0) {
			SetTimer(IdTimeOutTimer,0, NULL);
		}
	}
	

	return FALSE;
}

BEGIN_MESSAGE_MAP(CCtrlWindow, CDialog)
	//{{AFX_MSG_MAP(CCtrlWindow)
	ON_WM_CLOSE()
	ON_WM_DESTROY()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_GETMINMAXINFO()
	ON_WM_QUERYDRAGICON()
	ON_WM_SYSCOLORCHANGE()
	ON_WM_TIMER()
	ON_MESSAGE(WM_USER_DDECMNDEND,OnDdeCmndEnd)
	ON_MESSAGE(WM_USER_DDECOMREADY,OnDdeComReady)
	ON_MESSAGE(WM_USER_DDEREADY,OnDdeReady)
	ON_MESSAGE(WM_USER_MACROBRINGUP,OnMacroBringup)
	ON_MESSAGE(WM_USER_DDEEND,OnDdeEnd)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// CCtrlWindow message handler

BOOL CCtrlWindow::OnInitDialog()
{
	HDC TmpDC;
	int CRTWidth, CRTHeight;
	RECT Rect;
	char Temp[MAX_PATH + 8]; // MAX_PATH + "MACRO - "(8)
	BOOL IOption, VOption;
	int CmdShow;
	char uimsg[MAX_UIMSG], uimsg2[MAX_UIMSG];
	LOGFONT logfont;
	HFONT font;
	int fuLoad = LR_DEFAULTCOLOR;
	RECT rc_dlg, rc_filename, rc_lineno;
	LONG dlg_len, len;

	CDialog::OnInitDialog();
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnInitDialog()");
	font = (HFONT)SendMessage(WM_GETFONT, 0, 0);
	GetObject(font, sizeof(LOGFONT), &logfont);
	if (get_lang_font("DLG_SYSTEM_FONT", m_hWnd, &logfont, &DlgFont, UILanguageFile)) {
		SendDlgItemMessage(IDC_CTRLPAUSESTART, WM_SETFONT, (WPARAM)DlgFont, MAKELPARAM(TRUE,0));
		SendDlgItemMessage(IDC_CTRLEND, WM_SETFONT, (WPARAM)DlgFont, MAKELPARAM(TRUE,0));
	}

	GetDlgItemText(IDC_CTRLPAUSESTART, uimsg2, sizeof(uimsg2));
	get_lang_msg("BTN_PAUSE", uimsg, sizeof(uimsg), uimsg2, UILanguageFile);
	SetDlgItemText(IDC_CTRLPAUSESTART, uimsg);
	GetDlgItemText(IDC_CTRLEND, uimsg2, sizeof(uimsg2));
	get_lang_msg("BTN_END", uimsg, sizeof(uimsg), uimsg2, UILanguageFile);
	SetDlgItemText(IDC_CTRLEND, uimsg);

	Pause = FALSE;

	TmpDC = ::GetDC(GetSafeHwnd());
	CRTWidth = GetDeviceCaps(TmpDC,HORZRES);
	CRTHeight = GetDeviceCaps(TmpDC,VERTRES);
	GetWindowRect(&Rect);
	::ReleaseDC(GetSafeHwnd(), TmpDC);
	::SetWindowPos(GetSafeHwnd(),HWND_TOP,
	               (CRTWidth-Rect.right+Rect.left) / 2,
	               (CRTHeight-Rect.bottom+Rect.top) / 2,
	               0,0,SWP_NOSIZE | SWP_NOZORDER);

	if (is_NT4()) {
		fuLoad = LR_VGACOLOR;
	}
	::PostMessage(GetSafeHwnd(),WM_SETICON,ICON_SMALL,
	              (LPARAM)LoadImage(AfxGetInstanceHandle(),
	                                MAKEINTRESOURCE(IDI_TTMACRO),
	                                IMAGE_ICON,16,16,fuLoad));
	::PostMessage(GetSafeHwnd(),WM_SETICON,ICON_BIG,
	              (LPARAM)LoadImage(AfxGetInstanceHandle(),
	                                MAKEINTRESOURCE(IDI_TTMACRO),
	                                IMAGE_ICON,0,0,fuLoad));

	ParseParam(&IOption,&VOption);


	MacroMode = ModeTTL;
	g_EngineName.clear();
	//---------------------------------
	if (TopicName[0] != 0) {
		InitDDE(GetSafeHwnd());
	}

	if ((FileName[0]==0) && (! GetFileName(GetSafeHwnd()))) {
		EndDDE();
		PostQuitMessage(0);
		return TRUE;
	}

	const char *ext = strrchr(FileName, '.');
	if (ext != NULL){
		char FileType[1024], EngineName[1024];
		memset(FileType, 0x00, sizeof(FileType));
		if (ReadRegistry(HKEY_CLASSES_ROOT, ext, NULL, FileType, sizeof(FileType)))
		{
			strcat(FileType, "\\ScriptEngine");
			memset(EngineName, 0x00, sizeof(EngineName));
			if (ReadRegistry(HKEY_CLASSES_ROOT, FileType, NULL, EngineName, 1024))
			{
				g_EngineName = EngineName;
				GetDlgItem(IDC_CTRLPAUSESTART)->ShowWindow(SW_HIDE);
				// GetDlgItem(IDC_FILENAME)->ShowWindow(SW_HIDE);
				// GetDlgItem(IDC_LINENO)->ShowWindow(SW_HIDE);
				// return TRUE;
			}
		}
	}

	MCRLOGF(RT_D_DEBUG, "g_EngineName)=%s", g_EngineName.c_str());

	if (!g_EngineName.empty())
	{
		MacroMode = ModeJscript;
		jsRun = false;
		// TTLStatus =IdTTLJscript;
	}

	if (!InitTTL(GetSafeHwnd())) {
		EndDDE();
		PostQuitMessage(0);
	}

	// hJsMutex = CreateMutex(NULL, FALSE, MUTEX_JS);
	::InitializeCriticalSection(&m_lock);
	// m_queue_not_empty = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	m_queue_not_full = ::CreateEvent(NULL, TRUE, FALSE, NULL);

	// wait4all
	register_macro_window(GetSafeHwnd());

	strncpy_s(Temp, sizeof(Temp), "MACRO - ", _TRUNCATE);
	strncat_s(Temp, sizeof(Temp), ShortName, _TRUNCATE);
	SetWindowText(Temp);

	// send the initialization signal to TT
	SendCmnd(CmdInit,0);

	// ダイアログの初期サイズを保存
	GetWindowRect(&rc_dlg);
	m_init_width = rc_dlg.right - rc_dlg.left;
	m_init_height = rc_dlg.bottom - rc_dlg.top;

	/* マクロウィンドウをリサイズ可能にする。
	 * (2015.1.2 yutaka)
	 */
	// 現在サイズから必要な値を計算
	GetClientRect(&rc_dlg);
	ClientToScreen(&rc_dlg);
	dlg_len = rc_dlg.right - rc_dlg.left;

	GetDlgItem(IDC_FILENAME)->GetWindowRect(&rc_filename);
	len = rc_filename.right - rc_filename.left;
	m_filename_ratio = len*100 / dlg_len;

	GetDlgItem(IDC_LINENO)->GetWindowRect(&rc_lineno);
	len = rc_lineno.right - rc_lineno.left;
	m_lineno_ratio = len * 100 / dlg_len;

	// リサイズアイコンを右下に表示させたいので、ステータスバーを付ける。
	m_hStatus = CreateStatusWindow(
		WS_CHILD | WS_VISIBLE |
		CCS_BOTTOM | SBARS_SIZEGRIP, NULL, GetSafeHwnd(), 1);

	if (VOption) {
		return TRUE;
	}
	if (IOption) {
		CmdShow = SW_SHOWMINIMIZED;
	}
	else {
		CmdShow = SW_SHOWDEFAULT;
	}
	ShowWindow(CmdShow);

	return TRUE;
}

void CCtrlWindow::OnCancel( )
{
	::DestroyWindow(m_hStatus);
	DestroyWindow();
}

BOOL CCtrlWindow::OnCommand(WPARAM wParam, LPARAM lParam)
{
	char uimsg[MAX_UIMSG];
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnCommand(WPARAM %d, LPARAM %d)", wParam, lParam);
	switch (LOWORD(wParam)) {
		case IDC_CTRLPAUSESTART:
			if (Pause) {
				get_lang_msg("BTN_PAUSE", uimsg, sizeof(uimsg),  "Pau&se", UILanguageFile);
				SetDlgItemText(IDC_CTRLPAUSESTART, uimsg);
			}
			else {
				get_lang_msg("BTN_START", uimsg, sizeof(uimsg),  "&Start", UILanguageFile);
				SetDlgItemText(IDC_CTRLPAUSESTART, uimsg);
			}
			Pause = ! Pause;
			return TRUE;
		case IDC_CTRLEND:
			// TTLStatus = IdTTLEnd;
			int ret;
			if (gsThreadParam.pEngine){
				MCRLOGF(RT_D_DEBUG, "gsThreadParam.ID = F_FUNCTION_END;");
				// ret = gsThreadParam.pEngine->InterruptScriptThread(SCRIPTTHREADID_BASE, NULL, 0);
				// gsThreadParam.hThread = 0;
				gsThreadParam.ID = F_FUNCTION_END;
				SetEvent(gsThreadParam.hEvent1);
			}
			return TRUE;
		default:
			return (CDialog::OnCommand(wParam,lParam));
	}
}

void CCtrlWindow::OnClose()
{
	EndTTL();
	EndDDE();
	CDialog::OnClose();
}

void CCtrlWindow::OnDestroy()
{
	unregister_macro_window(GetSafeHwnd());
	// if (hJsMutex)
	//	CloseHandle(hJsMutex);
	// ::CloseHandle(m_queue_not_empty);
	::CloseHandle(m_queue_not_full);
	::DeleteCriticalSection(&m_lock);

	EndTTL();
	EndDDE();
	CDialog::OnDestroy();
}

// for icon drawing in Win NT 3.5
BOOL CCtrlWindow::OnEraseBkgnd(CDC* pDC)
{
	if (IsIconic()) {
		return TRUE;
	}
	else {
		return CDialog::OnEraseBkgnd(pDC);
	}
}

// for icon drawing in Win NT 3.5
void CCtrlWindow::OnPaint()
{
	int OldMapMode;
	CPaintDC dc(this);
	char buf[128];

	// line number (2005.7.18 yutaka)
	// added line buffer (2005.7.22 yutaka)
	// added MACRO filename (2013.9.8 yutaka)
	// ファイル名の末尾は省略表示とする。(2014.12.30 yutaka)
	memset(buf, 0x00, sizeof(buf));
	if (MacroMode == ModeJscript)
	{
		if (gsThreadParam.ID){
			std::wstring  FuncName(CSMacroMgr::GetFuncInfoByID(gsThreadParam.ID)->m_pszFuncName);
			SetDlgItemText(IDC_FILENAME, strtools::WStrToMStr(FuncName).c_str());
		}
		if (gsThreadParam.Source && gsThreadParam.Source2){
			_snprintf_s(buf, sizeof(buf), _TRUNCATE, ":[%s][%s]", gsThreadParam.Source, gsThreadParam.Source2);
		}
		else if (gsThreadParam.Source ){
			_snprintf_s(buf, sizeof(buf), _TRUNCATE, ":[%s]", gsThreadParam.Source);
		}
		SetDlgItemText(IDC_LINENO, buf);
	}
	else{
		SetDlgItemText(IDC_FILENAME, GetMacroFileName());
		_snprintf_s(buf, sizeof(buf), _TRUNCATE, ":%d:%s", GetLineNo(), GetLineBuffer());
		SetDlgItemText(IDC_LINENO, buf);
	}

	OldMapMode = dc.GetMapMode();
	dc.SetMapMode(MM_TEXT);

	if (!IsIconic()) return;
	SendMessage(WM_ICONERASEBKGND,(UINT)(dc.m_hDC));
	dc.DrawIcon(0, 0, m_hIcon);
	dc.SetMapMode(OldMapMode);
}

// マクロウィンドウをリサイズ可能とするために、OnSizeハンドラをoverrideする。
// (2015.1.1 yutaka)
void CCtrlWindow::OnSize(UINT nType, int cx, int cy)
{
	RECT rc_dlg, rc_filename, rc_lineno;
	LONG new_w, new_h, new_x, new_y;
	LONG len;

	GetClientRect(&rc_dlg);
	ClientToScreen(&rc_dlg);
	len = rc_dlg.right - rc_dlg.left;

	// TTLファイル名の再配置
	GetDlgItem(IDC_FILENAME)->GetWindowRect(&rc_filename);
	ScreenToClient(&rc_filename);
	new_w = (len * m_filename_ratio) / 100;
	new_h = rc_filename.bottom - rc_filename.top;
	GetDlgItem(IDC_FILENAME)->SetWindowPos(&CWnd::wndBottom,
		0, 0, new_w, new_h,
		SWP_NOMOVE | SWP_NOZORDER
	);
	new_x = rc_filename.left + new_w;

	// 行番号の再配置
	GetDlgItem(IDC_LINENO)->GetWindowRect(&rc_lineno);
	ScreenToClient(&rc_lineno);
	new_w = (len * m_lineno_ratio) / 100;
	new_h = rc_lineno.bottom - rc_lineno.top;
	new_y = rc_lineno.top;
	GetDlgItem(IDC_LINENO)->SetWindowPos(&CWnd::wndBottom,
		new_x, new_y, new_w, new_h,
		SWP_NOZORDER
		);

	// status bar
	::SendMessage(m_hStatus, WM_SIZE, cx, cy);
}

// マクロウィンドウをリサイズ可能とするために、OnGetMinMaxInfoハンドラをoverrideする。
// (2015.1.1 yutaka)
void CCtrlWindow::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
	// 下記の処理があると、Release build版で Tera Term から ttpmacro.exe を呼び出すと、
	// ストールするため、いったん無効化する。
#if 0
	LPMINMAXINFO lpmmi;

	// ダイアログの初期サイズより小さくできないようにする
	lpmmi = (LPMINMAXINFO)lpMMI;
	lpmmi->ptMinTrackSize.x = m_init_width;
	lpmmi->ptMinTrackSize.y = m_init_height;
#endif
}

// for icon drawing in Win NT 3.5
HCURSOR CCtrlWindow::OnQueryDragIcon()
{
	return m_hIcon;
}

void CCtrlWindow::OnSysColorChange()
{
	CDialog::OnSysColorChange();
}

void CCtrlWindow::OnTimer(UINT nIDEvent)
{
	BOOL TimeOut;
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnTimer(UINT %d)", nIDEvent);
	KillTimer(nIDEvent);
	if (nIDEvent!=IdTimeOutTimer) {
		return;
	}
	if (TTLStatus==IdTTLRun) {
		return;
	}

	TimeOut = CheckTimeout();
	MCRLOGF(RT_D_DEBUG, "TTLStatus=(%d),TimeOut=(%d)",TTLStatus, TimeOut);
	LockVar();

	if ((TTLStatus == IdTTLWait) ||
	    (TTLStatus == IdTTLWaitLn) ||
	    (TTLStatus == IdTTLWaitNL)) {
		if ((! Linked) || (ComReady==0)|| TimeOut) {
			SetResult(0);
			if (TTLStatus==IdTTLWaitNL) {
				SetInputStr(GetRecvLnBuff());
			}
			ClearWait();
			TTLStatus = IdTTLRun;
		}
	}
	else if (TTLStatus == IdTTLWait2) {
		if ((! Linked) || (ComReady==0) || TimeOut) {
			if (Wait2Found) {
				SetInputStr(Wait2Str);
				SetResult(-1);
			}
			else {
				SetInputStr("");
				SetResult(0);
			}
			TTLStatus = IdTTLRun;
		}
	}
	else if (TTLStatus == IdTTLWaitN) {
		if ((! Linked) || (ComReady==0) || TimeOut) {
			PCHAR p = GetRecvLnBuff();
			SetInputStr(p);
			if (strlen(p) > 0) {
				SetResult(-1);
			}
			else {
				SetResult(0);
			}
			TTLStatus = IdTTLRun;
		}
	}
	else if (TTLStatus==IdTTLPause) {
		if (TimeOut) {
			TTLStatus = IdTTLRun;
		}
	}
	else if (TTLStatus==IdTTLSleep) {
		if ((TimeOut) && TestWakeup(IdWakeupTimeout)) {
			SetResult(IdWakeupTimeout);
			TTLStatus = IdTTLRun;
		}
	}
	else {
		TTLStatus = IdTTLRun;
	}

	UnlockVar();

	if (TimeOut || (TTLStatus==IdTTLRun)) {
		return;
	}

	SetTimer(IdTimeOutTimer, TIMEOUT_TIMER_MS, NULL);
}

void CCtrlWindow::PostNcDestroy()
{
	delete this;
	PostQuitMessage(0);
}

BOOL CCtrlWindow::PreTranslateMessage(MSG* pMsg)
{
	if ((pMsg->message==WM_KEYDOWN) && (pMsg->wParam==VK_ESCAPE)) { // ignore ESC key
		return FALSE;
	}
	return CDialog::PreTranslateMessage(pMsg);
}

LONG CCtrlWindow::OnDdeCmndEnd(UINT wParam, LONG lParam)
{
	if (TTLStatus == IdTTLWaitCmndResult) {
		LockVar();
		SetResult(wParam);
		UnlockVar();
	}

	if ((TTLStatus == IdTTLWaitCmndEnd) ||
		(TTLStatus == IdTTLWaitCmndResult)) {
		TTLStatus = IdTTLRun;
	}
	return 0;
}

LONG CCtrlWindow::OnDdeComReady(UINT wParam, LONG lParam)
{
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnDdeComReady(wParam= %d, lParam= %d)", wParam, lParam);
	ComReady = wParam;
	if ((TTLStatus == IdTTLWait) ||
	    (TTLStatus == IdTTLWaitLn) ||
	    (TTLStatus == IdTTLWaitNL) ||
	    (TTLStatus == IdTTLWait2) ||
	    (TTLStatus == IdTTLWaitN)) {
		if (ComReady==0) {
			SetTimer(IdTimeOutTimer,0, NULL);
		}
	}
	else if (TTLStatus==IdTTLSleep) {
		LockVar();
		if (TestWakeup(IdWakeupInit)) {
			if (ComReady!=0) {
				SetResult(2);
			}
			else {
				SetResult(1);
			}
			TTLStatus = IdTTLRun;
		}
		else if ((ComReady!=0) && TestWakeup(IdWakeupConnect)) {
			SetResult(IdWakeupConnect);
			TTLStatus = IdTTLRun;
		}
		else if ((ComReady==0) && TestWakeup(IdWakeupDisconn)) {
			SetResult(IdWakeupDisconn);
			TTLStatus = IdTTLRun;
		}
		UnlockVar();
	}
	return 0;
}

LONG CCtrlWindow::OnDdeReady(UINT wParam, LONG lParam)
{
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnDdeReady(wParam= %d, lParam= %d)", wParam, lParam);
	if (TTLStatus != IdTTLInitDDE) {
		return 0;
	}
	SetWakeup(IdWakeupInit);
	TTLStatus = IdTTLSleep;

	if (! InitDDE(GetSafeHwnd())) {
		LockVar();
		SetResult(0);
		UnlockVar();
		TopicName[0] = 0;
		TTLStatus = IdTTLRun;
	}
	return 0;
}

LONG CCtrlWindow::OnDdeEnd(UINT wParam, LONG lParam)
{
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnDdeEnd(wParam= %d, lParam= %d)", wParam, lParam);
	EndDDE();
	if ((TTLStatus == IdTTLWaitCmndEnd) ||
		(TTLStatus == IdTTLWaitCmndResult)) {
		TTLStatus = IdTTLRun;
	}
	else if ((TTLStatus == IdTTLWait) ||
	         (TTLStatus == IdTTLWaitLn) ||
	         (TTLStatus == IdTTLWaitNL) ||
	         (TTLStatus == IdTTLWait2) ||
	         (TTLStatus == IdTTLWaitN)) {
		SetTimer(IdTimeOutTimer,0, NULL);
	}
	else if (TTLStatus==IdTTLSleep) {
		LockVar();
		if (TestWakeup(IdWakeupInit)) {
			SetResult(0);
			TTLStatus = IdTTLRun;
		}
		else if (TestWakeup(IdWakeupUnlink)) {
			SetResult(IdWakeupUnlink);
			TTLStatus = IdTTLRun;
		}
		UnlockVar();
	}
	return 0;
}

LONG CCtrlWindow::OnMacroBringup(UINT wParam, LONG lParam)
{
	DWORD pid;
	DWORD thisThreadId;
	DWORD fgThreadId;
	MCRLOGF(RT_D_DEBUG, "CCtrlWindow::OnMacroBringup(wParam= %d, lParam= %d)", wParam, lParam);
	thisThreadId = GetWindowThreadProcessId(GetSafeHwnd(), &pid);
	fgThreadId = GetWindowThreadProcessId(::GetForegroundWindow(), &pid);

	if (thisThreadId == fgThreadId) {
		SetForegroundWindow();
		BringWindowToTop();
	} else {
		AttachThreadInput(thisThreadId, fgThreadId, TRUE);
		SetForegroundWindow();
		BringWindowToTop();
		AttachThreadInput(thisThreadId, fgThreadId, FALSE);
	}
	return 0;
}
