/*****************************************************************************
*
* FILE NAME:    SampleAPP.c
*
* DESCRIPTION:    メインプログラムモジュール
*
\*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <rt.h>

#include "SampleAPP.h"

// グローバル変数
	RTHANDLE hRootProcess;
	DWORD    dwKtickInUsecs;
// ポーリングスレッドの宣言

void     PollThread1(void);

// モジュール情報構造体
struct _INIT {
	enum States
		{	BEFORE_INIT,
			INIT_BUSY,
			INIT_DONE,
			CLEANUP_BUSY 
		} state;
	RTHANDLE hMain;
	BOOL     bCataloged;
// ポーリングスレッド用の宣言
	RTHANDLE hPollThread1;
} strInit;

static void     Cleanup(void);

/*****************************************************************************
*
* FUNCTION:     main
*
* DESCRIPTION:
*  これはメインプログラムモジュールです
*  共有メモリや、すべてのスレッド、すべてのオブジェクトを生成します
*  このmain関数が終了するまでの間プロセスとして存在し、終了時にそれらの
*  すべては削除されます
*
\*****************************************************************************/

void        main(void)
{
	SYSINFO   sysinfo;
	EVENTINFO eiEventInfo;
	HRESULT   hr;


	// ログレベルをDEBUGに設定
	SetLogLevel(LOG_DEBUG);
	PrintLog(LOG_DEBUG, "debug  : LogLevel = %d \n", GetLogLevel());

#ifdef _DEBUG
	PrintLog(LOG_INFO, "info   : SampleAPP started. \n");
#endif


	PrintLog(LOG_DEBUG, "debug  : Listener status = %d \n", ListenerGetStatus());

	// RSL初期化
	hr = ListenerInitialize(TRUE);	// 強制カタログ有効
	if (hr != S_OK) {
		PrintLog(LOG_ERROR, "error  : Library initialization failure. (hr=0x%x) \n", hr);
		// プロセス終了
		exit(0);
	}

	PrintLog(LOG_DEBUG, "debug  : Listener status = %d \n", ListenerGetStatus());


	// ルートプロセスを取得する（失敗しません）
	hRootProcess = GetRtThreadHandles(ROOT_PROCESS);

	// モジュール情報構造体をクリアします
	memset(&strInit, 0, sizeof(strInit));
	strInit.state = BEFORE_INIT;

	// 低レベルティック値（マイクロ秒）を取得します
	if (!CopyRtSystemInfo(&sysinfo))
		Fail("Cannot copy system info");
	dwKtickInUsecs = 10000 / sysinfo.KernelTickRatio;
	if (dwKtickInUsecs == 0)
		Fail("Invalid low level tick length");

	// プロセスの最大プライオリティを調整します（失敗は無視します）
	SetRtProcessMaxPriority(NULL_RTHANDLE, 150);

	// メインスレッド（この関数）のハンドルを取得します
	strInit.hMain = GetRtThreadHandles(THIS_THREAD);
	strInit.state = INIT_BUSY;

	// このプロセスのハンドルをルートプロセスにカタログします
	if (!Catalog(hRootProcess,
		GetRtThreadHandles(THIS_PROCESS),
		"SampleAPP"))
		Fail("Cannot catalog process name");
	strInit.bCataloged = TRUE;

	// ポーリングスレッドを生成します

	strInit.hPollThread1 = CreateRtThread(170, PollThread1, 4096, 0);
	if (strInit.hPollThread1 == BAD_RTHANDLE)
	{
		strInit.hPollThread1 = NULL_RTHANDLE;
		Fail("Cannot create poll thread PollThread1");
	}

	// 初期化が完了したことを示します
	strInit.state = INIT_DONE;
#ifdef _DEBUG
	PrintLog(LOG_INFO, "info   : SampleAPP finished initialization. \n");
#endif

	// イベントを待ちます
	while (RtNotifyEvent(RT_SYSTEM_NOTIFICATIONS | RT_EXIT_NOTIFICATIONS,
					WAIT_FOREVER,
					&eiEventInfo))
	{
		switch(eiEventInfo.dwNotifyType)
		{
		case TERMINATE:
		// TODO: このプロセスを終了するときの処理
		// 環境をクリーンアップします
			Cleanup();  // ここから戻ることはありません

		case RT_CLIENT_DOWN:
		// TODO: NTホストにおいてRTクライアントのダウンに反応したときの処理
			break;

		case RT_CLIENT_UP:
		// TODO: NTホストにおいてRTクライアントの復帰に反応したときの処理
			break;

		case NT_HOST_DOWN:
		// TODO: RTクライアントにおいてNTホストのダウンに反応したときの処理
			break;

		case NT_HOST_UP:
		// TODO: RTクライアントにおいてNTホストの復帰に反応したときの処理
			break;

		case NT_BLUESCREEN:
		// TODO: NTのブルースクリーンを検出したときの処理
			break;
		}
	}
	Fail("Notify failed");
}


/*****************************************************************************
*
* FUNCTION:     Fail
*
* PARAMETERS:   printf同様のパラメータを期待します
*
* RETURNS:      この関数から処理が戻ることはありません
*
* DESCRIPTION:
*  デバッグモードにおいてメッセージを表示します
*  このとき現在のプロセスは無条件に削除されます
\*****************************************************************************/

void        Fail(
  LPSTR       lpszMessage, ...)
{
	EXCEPTION   eh;
	RTHANDLE    hDelMbx;
	DWORD     dwTerminate;

#ifdef _DEBUG
	va_list     ap;

	PrintLog(LOG_ERROR, "error  : -------------------------------->\n");
	va_start(ap, lpszMessage);
	PrintLog(LOG_ERROR, lpszMessage, ap);
	va_end(ap);
	PrintLog(LOG_ERROR, "\nError nr=%x \n", GetLastRtError());
	PrintLog(LOG_ERROR, "error  : <--------------------------------\n");
#endif

	// 例外を調査します
	GetRtExceptionHandlerInfo(THREAD_HANDLER, &eh);
	eh.ExceptionMode = 0;
	SetRtExceptionHandler(&eh);

	// 初期化がまだ開始されていない状態では直ちに終了します
	if (strInit.state == BEFORE_INIT)
		exit(0);

	if (strInit.hMain == GetRtThreadHandles(THIS_THREAD))
	{
		// メインスレッドの場合：
		// もしも初期化作業中ならばクリーンアップします
		if (strInit.state == INIT_BUSY)
			Cleanup();  // 戻ることはありません

		// メインスレッドの場合：
		// 現在初期化作業を行っていないときreturnにより終了します
		return;
	}

	// メインスレッド以外：
	// メインスレッドにクリーンアップの実行を要求します
	// （削除要求メールを送る）エラーは無視します
	hDelMbx = LookupRtHandle(NULL_RTHANDLE, "R?EXIT_MBOX", 5000);
	dwTerminate = TERMINATE;
	SendRtData(hDelMbx, &dwTerminate, 4);
	SuspendRtThread(NULL_RTHANDLE);

	// ここはサスペンド失敗を意味します
	while (1)
		RtSleep(655349);  // 最大限までスリープします
}


/*****************************************************************************
*
* FUNCTION:   Cleanup (local function)
*
* DESCRIPTION:
*  生成した全てのオブジェクトを削除します 
*
\*****************************************************************************/

void      Cleanup(void)
{
	// クリーンアップ作業中を示します
	strInit.state = CLEANUP_BUSY;

	// ポーリングスレッドを削除します
	if (strInit.hPollThread1 != NULL_RTHANDLE)
		if (!DeleteRtThread(strInit.hPollThread1))
			Fail("Cannot delete poll thread PollThread1");

	// ルートプロセスのカタログからこのプロセスのカタログ名を削除します
	if (strInit.bCataloged)
		if (!UncatalogRtHandle(hRootProcess, "SampleAPP"))
			Fail("Cannot remove my own name");

#ifdef _DEBUG
	PrintLog(LOG_INFO, "info   : SampleAPP finished cleaning up \n");
#endif

	// プロセス終了
	exit(0);
}


void PrintVariant(VARIANT vntData, int num);
void PrintVtArray(VARIANT vntData)
{
	HRESULT hr;
	int i, element;
	LONG lBound, uBound;
	void *pvData;

	PrintLog(LOG_DEBUG, "debug  : ++++++++++++++++++++++++++++++++++++++++\n");
	PrintLog(LOG_DEBUG, "debug  :   VT_ARRAY ===> \n");

	// 配列要素数取得
	hr = SafeArrayGetLBound(vntData.parray, 1, &lBound);
	hr = SafeArrayGetUBound(vntData.parray, 1, &uBound);
	element = (uBound - lBound + 1);
	// 配列アクセス
	hr = SafeArrayAccessData(vntData.parray, &pvData);
	for (i=0; i<element; i++) {
		PrintVariant(*((VARIANT*)pvData + i), i);
		if (i < (element - 1)) {
			PrintLog(LOG_DEBUG, "debug  : ----------------------------------------\n");
		}
	}
	SafeArrayUnaccessData(vntData.parray);

	PrintLog(LOG_DEBUG, "debug  : ++++++++++++++++++++++++++++++++++++++++\n");
}

void PrintVariant(VARIANT vntData, int num)
{
	char tmp[255];

	PrintLog(LOG_DEBUG, "debug  :   [%d] vt=0x%x \n", num, vntData.vt);

	if (vntData.vt & VT_ARRAY) {
		// 配列の場合
		PrintVtArray(vntData);
		return;
	}

	switch (vntData.vt) {
	case VT_I2:
		PrintLog(LOG_DEBUG, "debug  :   iVal=0x%x \n", vntData.iVal);
		break;
	case VT_I4:
		PrintLog(LOG_DEBUG, "debug  :   lVal=0x%x \n", vntData.lVal);
		break;
	case VT_R4:
		PrintLog(LOG_DEBUG, "debug  :   fltVal=%f \n", vntData.fltVal);
		break;
	case VT_R8:
		PrintLog(LOG_DEBUG, "debug  :   dblVal=%f \n", vntData.dblVal);
		break;
	case VT_BSTR:
		memset(tmp, 0, sizeof(tmp));
		wcstombs(tmp, vntData.bstrVal, wcslen(vntData.bstrVal));
		PrintLog(LOG_DEBUG, "debug  :   bstrVal=%s \n", tmp);
		break;
	case VT_ERROR:
		PrintLog(LOG_DEBUG, "debug  :   scode=0x%x \n", vntData.scode);
		break;
	case VT_BOOL:
		if (vntData.boolVal) {
			PrintLog(LOG_DEBUG, "debug  :   boolVal=TRUE \n");
		}
		else {
			PrintLog(LOG_DEBUG, "debug  :   boolVal=FALSE \n");
		}
		break;
	case VT_UI1:
		PrintLog(LOG_DEBUG, "debug  :   bVal=0x%x \n", vntData.bVal);
		break;
	case VT_UI2:
		PrintLog(LOG_DEBUG, "debug  :   uiVal=0x%x \n", vntData.uiVal);
		break;
	case VT_UI4:
		PrintLog(LOG_DEBUG, "debug  :   ulVal=0x%x \n", vntData.ulVal);
		break;
	default:
		break;
	}
}

void PrintReceivePacket(VARIANT *vntRecv, int nRecvNum)
{
#if 1
	int i;

	PrintLog(LOG_DEBUG, "debug  : _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/\n");
	PrintLog(LOG_DEBUG, "debug  : <RecvNum:%d> \n", nRecvNum);

	for (i=0; i<nRecvNum; i++) {
		// コンソール出力
		PrintVariant(vntRecv[i], i);
		if (i < (nRecvNum - 1)) {
			PrintLog(LOG_DEBUG, "debug  : ****************************************\n");
		}
	}

	PrintLog(LOG_DEBUG, "debug  : _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/\n");
	PrintLog(LOG_DEBUG, "\n\n");
#endif
}

void ExitApp(void)
{
	// クリーンアップ作業中を示します
	strInit.state = CLEANUP_BUSY;

	// ルートプロセスのカタログからこのプロセスのカタログ名を削除します
	if (strInit.bCataloged)
		if (!UncatalogRtHandle(hRootProcess, "SampleAPP"))
			Fail("Cannot remove my own name");

#ifdef _DEBUG
	PrintLog(LOG_INFO, "info   : SampleAPP finished cleaning up \n");
#endif

	// プロセス終了
	exit(0);
}
