/*<Header>==============================================================
*
*	負荷表示 / "MAIN.C"
*
*		[ EIN(tm) project : サンプルプログラム ]
*
*	COPYRIGHT  Nam  1994, All rights reserved.
*
*	配付・組込・改変・商利用すべて自由。ただし無保証っす
*
*-----------------------------------------------------------------------
*	V1.0L01α	94.07.19/Nam	プロトタイプ
*	V1.0L30		94.07.21/Nam	EIN(TM)公開版
*	V1.0L31		94.09.05/Nam	GM_QUIT時に自分でWINDOWを消去
*</Header>==============================================================*/
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<snd.h>
#include	<winb.h>
#include	<te.h>
#include	<fntb.h>
#include	<gui.h>
#include	<egb.h>
//#include	<file_dlg.h>
//#include	<tifflib.h>
#include	<msdos.cf>
#include	<loader.h>
#include	<math.h>
#include	<io.h>
#include	<guidbg.h>
//#include	<wgb.h>
// ※ ここまでが平均的に使用されるヘッダ

#include	<mos.h>			// このサンプルでのみ必要(MOS_getTimeの為)

#include	"..\ein\eintm.h"	// EIN(TM)関連ライブラリのヘッダ


extern unsigned char ICON02[];		// アイコンパターン

#define	ERROR	(-1)
#define	PSET	(0)

#define	DISPMODE_ICON	(0)
#define	DISPMODE_PANEL	(1)

#define	pattBuf		(&ICON02[4])	// パターン書換え用ワークエリア
#define	ID_ICONBASE	(0)				// アイコン部品のベースid
#define	ID_ICONPAT	(1)				// アイコン部品のid
#define	ID_NUMGET	(5)				// 数値入力BOXのid

int		ID_ICON[2];					// アイコン部品のID
int		ID_PANEL[6];				// パネル部品のID
int		dispMode = 0;				// 0:icon / 1:win

/*= リジューム用設定=================================================*/
unsigned int	RSM_INT = 50*3;
FRAME	RSM_ICONFR;
char	RSMID[]="%%% LOAD icon %%%";
char	RSMTITLE[]="負荷表示  V0.1 L31";
/*===================================================================*/

//char	pattBuf[2*32];				// パターン書換え用ワークエリア
int		updateFlag = NOERR;			// 画面更新中か否かを示すフラグ

unsigned int	loadInt = 50*3;				// 負荷計測間隔(20ms単位)
unsigned int	loadMin = 3;				// 最小負荷間隔(20ms単位)
unsigned int	loadBfr = 0;				// 前回計測時間(20ms単位)

char	*GV_egbWork ;
int		GV_apliLot ;
void	( *GV_defIdleTask )() ;


char	*guiEgbPtr ;				//	EGB のワークアドレス
MMICTRL	mmic = {
				 SCREEN16 | SCREENIGNORE,		//	resolution
				 SCREENUNUSED,
				 0, SCREENAVAILABLE, 0,	//	write_page,display_page,priority
				 SCREENAVAILABLE,	//	mode
				 SCREENEXPAND,		//	vram_x
				 0,0,0,0,			//	size,*ptr,asize,*atpr
				 0,0,0,0,			//	frame
				 -32767,			//	move
				 -32767,
				  32767,
				  32767,
				 15,8,7,15			//	color
				} ;

/*===================================================================*/
/*  メインループ                                                     */
/*===================================================================*/
void main()
{
	extern int APL_init() ;

	int		kobj;

	// 初期化処理
	if (MMI_Open( &mmic ) == NOERR){
		// 二重起動のcheck
		if ( (kobj = MMI_CallMessage( MMI_GetApliId(), GM_QUERYID, QM_KIND, 1)) > NOERR ){
			MMI_CallMessage( MMI_GetApliId(), GM_SWITCH, FALSE, kobj );
		} else {
			// アイドルタスクに登録
			setIdleTask();
			// 初期化に成功すればメインループに
			if ( APL_init()==NOERR ){
				// 無負荷時のインターバルを計測
				initInterval();
				// メインループ
				MMI_ExecSystem();
			}
			// アイドルの解除
			resetIdleTask();
			// ウィンドゥ消去(※SHELLに消去させると遅いので)
			eraseWin();
		}
	}
	// 終了処理
	MMI_Close() ;
}

/*===================================================================*/
/*  初期化処理                                                       */
/*===================================================================*/
int APL_init()
{
	extern	MMIINIT	initDataRES_ICON ;
	extern	MMIINIT	initDataRES_PANL ;

	extern	int ICONmaxId;
	extern	unsigned char *ICONTbl[];

	extern int	userFunc();
	extern int	initMemRes();

	HYPER	hyp;
	register int	ret ;

	// EGBワークアドレス取得/ローカルワークにコピー/仮想画面情報更新
	if ( (ret = initMemRes()) != NOERR ){
		return ret;
	}

	// GUI部品の初期化
	if ((ret = MMI_initHyper()) < 0)			//	ハイパー型
		return ret;
	if ((ret = MMI_initDialogL40()) < 0)		//	ダイアログ型
		return ret;
	if ((ret = MMI_initWindowL40()) < 0)		//	ウィンドウ型
		return ret;
	if ((ret = MMI_initMessageL40()) < 0)		//	メッセージ型
		return ret;
	if ((ret = MMI_initButtonL40()) < 0)		//	ボタン型
		return ret;
	if ((ret = MMI_initDrawButtonL40()) < 0)	//	ドロウボタン型
		return ret;
	if ((ret = MMI_initIconL40()) < 0)			//	アイコン型
		return ret;
	if ((ret = MMI_initNumBoxL40()) < 0)		//	数値入力box型
		return ret;

	// リソースを登録
	if ((ret = MMI_Init(&initDataRES_ICON)) < 0)
		return ret ;
	if ((ret = MMI_Init(&initDataRES_PANL)) < 0)
		return ret ;

	// ユーザアイコンを登録
	MMI_SetIconTable( ICONTbl, ICONmaxId ) ;

	// GUI色をメニュー色に設定
	EIN_initGuiColor();

	// ウィンドウ類をベース部品にATTACH
	MMI_SendMessage( ID_ICON[0], MM_ATTACH, 1, MMI_GetBaseObj() ) ;

	// スイッチャー対応関数の登録
	MMI_SendMessage( MMI_GetBaseObj(), MM_SETEXEC, 1, userFunc ) ;

	// タスクリストにタイトルを登録
	MMI_CallMessage( MMI_GetApliId(), GM_TITLE, (int)RSMTITLE, 0 ) ;

	/*----------------------------------------------------------------*/
	/*  リジューム読み込み (※のついた処理は必ず行ってください)       */
	/*----------------------------------------------------------------*/
	char	work[RSMWORKSIZE];	// リジュームマネージャのワークエリア
	char	buf[1024];
	char	*ptr;
	int		size, x, y, z;

	// ※リジュームマネージャの初期化(アプリのIDタグ登録)
	EIN_rsmInit( work, RSMID );
	// ※読み込みバッファ設定
	EIN_rsmBufSet( work, buf, 1023 );
	// ※リジューム情報読み込み
	size = EIN_rsmLoad( work );
	#ifdef DEBUG
	printf(" resume size(%d)\n",size);
	#endif
	if ( size > 0 ){
		// ICON表示位置
		if ( ((ptr = strstr(buf,"\nICON: ")) != NULL ) &&
			 (sscanf(ptr,"\nICON: %d %d  INTERVAL: %d\n", &x, &y, &z) > 2 ) ){
			// 枠座標を得る
			MMI_SendMessage( ID_ICON[0], MM_GETHYPER, 1, &hyp );
			RSM_ICONFR.lupx = x;
			RSM_ICONFR.lupy = y;
			RSM_ICONFR.rdwx = x+(hyp.fr.rdwx-hyp.fr.lupx);
			RSM_ICONFR.rdwy = y+(hyp.fr.rdwy-hyp.fr.lupy);
			#ifdef DEBUG
			printf("ICON: move to (%d,%d)\n", x, y);
			#endif
			if ( x<-31 ){
				x=0;
			}
			if ( y<-31 ){
				y=0;
			}
			MMI_SendMessage( ID_ICON[0], MM_MOVE, 1, &RSM_ICONFR );
			// 計測間隔を設定
			if ( z>0 && z<601 ){
				loadInt = z * (1000/20);	// /秒→/20ms変換
				RSM_INT = loadInt;
			}
		}
	} else {
		#ifdef DEBUG
		printf("Can't find resume file.\n");
		#endif
	}

	//	背景を表示
	MMI_SendMessage( MMI_GetBaseObj(), MM_SHOW, 0 ) ;

	return NOERR ;
}

/*===================================================================*/
/* リジューム情報更新 (※がついた処理は必ず行って下さい)             */
/*===================================================================*/
void	SaveResumeFile()
{
	char	work[RSMWORKSIZE];
	char	buf[1024];
	char	aplpath[128];
	HYPER	hyp1;
	register int ret;
	
	// 枠座標を得る
	MMI_SendMessage( ID_ICON[0], MM_GETHYPER, 1, &hyp1 );
	
	// 移動していたらセーブ
	if ( (RSM_INT          != loadInt     ) ||
		 (RSM_ICONFR.lupx  != hyp1.fr.lupx) ||
		 (RSM_ICONFR.lupy  != hyp1.fr.lupy) ){
		// ※マネージャ初期化
		EIN_rsmInit( work, RSMID );
		// ※バッファ設定(アプリ識別ID,更新時刻は自動設定されます)
		EIN_rsmBufSet( work, buf, 1023 );
		// ※アプリディレクトリ保存
		MMI_CallMessage( MMI_GetApliId(), GM_QUERYID, QM_PATH, (int)aplpath );
		EIN_rsmBufPrintf( work, "APLPATH: %s", aplpath );
		// ※アプリ名保存
		EIN_rsmBufPrintf( work, "TITLE:   %s", RSMTITLE );
		// ※コメント保存
		EIN_rsmBufCat( work, "COMMENT: シェルタスクの負荷表示です。");
		// アイコン位置と計測間隔保存
		EIN_rsmBufPrintf( work, "ICON: %d %d  INTERVAL: %d", hyp1.fr.lupx, hyp1.fr.lupy, loadInt/(1000/20) );
		// ※バッファ終端(^A)設定
		EIN_rsmBufTail( work );
		// ※バッファをセーブ
		ret = EIN_rsmSave( work );
		#ifdef DEBUG
		printf(" resume save size(%d)   ptr:(%d,%d)\n",ret, hyp.fr.lupx, hyp.fr.lupy);
		#endif
		RSM_ICONFR.lupx = hyp1.fr.lupx;
		RSM_ICONFR.lupy = hyp1.fr.lupy;
		RSM_ICONFR.rdwx = hyp1.fr.rdwx;
		RSM_ICONFR.rdwy = hyp1.fr.rdwy;
		RSM_INT         = loadInt;
	}
}

/*===================================================================*/
/*  バックグラウンドに                                               */
/*===================================================================*/
int		func_sleep()
{
	MMI_CallMessage( MMI_GetApliId(), GM_SLEEP, 0 ,0 ) ;
	return NOERR;
}

/*===================================================================*/
/*  イベントループ関数                                               */
/*===================================================================*/
int		userFunc( int apliId, int messId, int info, int data )
{
	register	int		ret ;

	ret = ILLEGAL_FUNCTION ;
	switch( messId ){
		//	他タスクからスイッチしてきた
		case GM_WAKE :
			break ;

		//	他タスクにスイッチする
		case GM_SLEEP :
			break ;

		//	一時停止要求が出た
		case GM_PAUSE :
			updateFlag = ERROR;
			break ;

		//	一時停止解除要求が出た
		case GM_CONTINUE :
			updateFlag = NOERR;
			break ;

		//	終了要求が出た
		case GM_QUIT :
			// リジューム
			SaveResumeFile();
			//	正常終了
			MMI_SetHaltFlag( TRUE ) ;	//	イベントループを終了させるフラグ
			ret = NOERR ;
			break ;

	}

	return ret ;
}

/*===================================================================*/
/*  無負荷時のインターバルを計測(MOS_getTimeの結果を返す)            */
/*===================================================================*/
int		initInterval()
{
	EVENT	*pev;
	register int	ntime, avr, sec;
	register int	ret;

	avr = MOS_getTime();
	// アイドルループを回す
	ret = MMI_CallMessage( MMI_GetApliId(), GM_IDLETASK, 0, 0 );
	if( MMI_GetEvnt( EVALL, &pev ) == NOERR ){
		MMI_ExecEvnt( pev );
	}
	ntime = MOS_getTime();
	sec   = abs(ntime - avr);
	#ifdef DEBUG
	printf("loadMin = %d   ret=%d\n", sec, ret);
	#endif
	
	// 各時刻を初期化
	loadMin = (sec < 1) ?1 :sec;
	loadBfr = ntime;		// 前回計測時間(20ms単位)
	
	return NOERR;
}

/*===================================================================*/
/*  アイドルタスク関数(ウィンドゥ上に負荷表示)                       */
/*===================================================================*/
void	userIdleTask()
{
	static unsigned int	loadAvr = 0;		// 瞬間最大負荷
	static unsigned int	loadOld = 0;		// 

	typedef	struct {
		short	sum;
		short	x1,y1;
		short	x2,y2;
		} EGB_LINE;

	EGB_LINE		line;
	HYPER			hyp ;
	int				btn, bx, by;
	Rect			rmos;
	register unsigned int	ntime, atime, btime, count;

	ntime = MOS_getTime();
	atime = abs(ntime - loadOld);
	loadOld = ntime;
	loadAvr = (atime < loadAvr) ?loadAvr :atime;
	// まだ指定秒経っていないなら終了
	btime = abs(ntime - loadBfr);
	if ( btime < loadInt ){
		return;
	}
	loadBfr = ntime;

	// 仮想画面に描画開始
	line.sum= 2;
	line.x1 = 30;
	line.y1 = 30 - ((loadAvr / loadMin) % 30) + 1;
	line.x2 = 30;
	line.y2 = 30;
	count = btime / loadInt;
	if ( count > 30 ){ count = 30; }
	do {
		EGB_scroll( GV_egbWork, 1, -1, 0 );
		EGB_connect( GV_egbWork, (char *)&line );
		count--;
	} while( count>0 );
	loadAvr = 0;
	
	// SCREENCHANGE中なので更新しない
	if ( updateFlag != NOERR ){
		return;
	}
	// 余分なMOS_OFFを回避(893だからやらんほうがいいか?)
	MMI_SendMessage( ID_ICON[ID_ICONBASE], MM_GETHYPER, 1, &hyp );
	MOS_rdpos( &btn, &bx, &by );
	rmos.left = bx;
	rmos.up   = by;
	rmos.right= rmos.left + 32;
	rmos.down = rmos.up   + 32;
	// グラフ描画(アイコン再描画)
	if ( SectRect( &rmos, (Rect *)&hyp.fr ) ){
		MMI_SendMessage( ID_ICON[ID_ICONPAT], MM_SHOW, 0 );
	} else {
		// 外側なのでマウスを消さないように
		MG_mosDisp(3);	// 表示++ (^^;)
		MMI_SendMessage( ID_ICON[ID_ICONPAT], MM_SHOW, 0 );
		MG_mosDisp(2);	// 消去++
	}
}

/*===================================================================*/
/*  アイドルタスクに登録                                             */
/*===================================================================*/
int		setIdleTask()
{
	void	( *idleTask )() ;

	/*	アイドル処理関数の取得	*/
	idleTask = MMI_GetIdleTaskFunc() ;

	/*	二重登録禁止チェック	*/
	if( idleTask != userIdleTask ){
		GV_defIdleTask = idleTask ;
		MMI_SetIdleTaskFunc( (void ((*)(void)))userIdleTask );
	}

	return ERROR ;
}

/*===================================================================*/
/*  アイドルタスクから削除                                           */
/*===================================================================*/
int		resetIdleTask()
{
	void	( *IdleTask )() ;

	/*	アイドル処理関数の取得	*/
	IdleTask = MMI_GetIdleTaskFunc() ;

	/*	USER関数登録のチェック	*/
	if( IdleTask == userIdleTask ){
		MMI_SetIdleTaskFunc( GV_defIdleTask ) ;
		GV_defIdleTask = 0 ;
	}

	return ERROR ;
}

/*===================================================================*/
/*  現在の画面モードに応じて仮想画面再設定                           */
/*===================================================================*/
int		initMemRes()
{
	// ローカルワーク取得
	if ( (GV_apliLot = TL_getLot()) < 1 ){
		return ERROR ;
	}
	if( (GV_egbWork = TL_mallocMemory( GV_apliLot, EgbWorkSize )) == NULL ){
		return ERROR ;
	}
	// EGB ワークアドレスを取得し、ローカルワークにコピー
	guiEgbPtr = MMI_GetEgbPtr() ;
	FM_memcpy( GV_egbWork, guiEgbPtr, EgbWorkSize ) ;
	// EGB 仮想画面再初期化
	EGB_resolutionRam( GV_egbWork, 0x80, 1, 32, 32, pattBuf );
	EGB_writePage( GV_egbWork, 0x80 );
	EGB_color(     GV_egbWork, 0, 1 );
	EGB_writeMode( GV_egbWork, PSET );
	EGB_paintMode( GV_egbWork, 0x02 );
	EGB_pen(       GV_egbWork, 0 );
	
	return NOERR;
}

/*===================================================================*/
/*  アイコン化されたウィンドゥの関数                                 */
/*===================================================================*/
int		func_ICON( int kobj, int messId, int ac, EVENT *pev, int trigger )
{
	HYPER	hyp;
	FRAME	fr;

	// ※ ここでダブルクリックなら関数起動 / それ以外ならMOVE
	if( messId == MM_MOUSEON ){
		// 枠座標を得る
		MMI_SendMessage( kobj, MM_GETHYPER, 1, &hyp );
		fr = hyp.fr;
		do {
			MMI_iosense();
		} while( (MMI_GetEvnt((EVMOSUP|EVMOSDRAG), &pev)) < NOERR);
		if ( pev->what == EVMOSUP ){
			// ダブルクリックチェック
			if ( MMI_DoubleClickCheck(&fr, pev) != FALSE ){
				// 「アイコン→ウィンドゥ表示」
				func_Panel();
			}
			return NOERR;
		}
		// ダブルクリックでは無いのでMOVE処理へ
		static int	argv[2] = { 1, 1 } ;
		EVENT	ev ;
		ev.what = EVEXEC ;
		ev.shift = 0 ;
		ev.info = (int)func_sleep;
		ev.data = (int)argv ;
		MMI_SetEvnt(&ev) ;
	}
	// エラーで抜けるとWINDOW部品本来の動作をしまっする♪
	return ERROR;
}

/*===================================================================*/
/* 終了処理                                                              */
/*===================================================================*/
int		func_Quit()
{
	extern void SaveResumeFile();

	/* リジューム */
	SaveResumeFile();

	MMI_SetHaltFlag( TRUE ) ;	/*	イベントループを終了させるフラグ	*/

	return NOERR ;
}

/*===================================================================*/
/*  パネル表示                                                       */
/*===================================================================*/
int	func_Panel()
{
	int	var, min, max, delta, ptColum;
	char		clp[EINCLIPSIZE];
	register int	ret;

	// 既にパネルが表示されているなら何もしない
	if ( dispMode==DISPMODE_PANEL){
//		printf("dispMode!=0\n");
		return NOERR;
	}
	dispMode=DISPMODE_PANEL;

	// 計測間隔を数値入力BOXに反映する
	if ( MMI_SendMessage( ID_PANEL[ID_NUMGET], MM_GETNUMBOX, 5,
						  &var, &min, &max, &delta, &ptColum ) == NOERR ){
		var = loadInt / (1000/20);
		MMI_SendMessage( ID_PANEL[ID_NUMGET], MM_SETNUMBOX, 5,
						 var, min, max, delta, ptColum );
	}
	//	パネルをATTACHする
	ret = MMI_SendMessage( ID_PANEL[0], MM_ATTACH, 1, MMI_GetBaseObj() ) ;
	// パネルを表示する
	EIN_clipOpen( clp );
	MMI_SendMessage( ID_PANEL[0], MM_SHOW, 0 ) ;
	MMI_SendMessage( ID_PANEL[0], MM_WAKE, 0 ) ;
	EIN_clipClose( clp );

	return NOERR;
}

/*===================================================================*/
/*  パネル消去                                                       */
/*===================================================================*/
int	func_Close()
{
	// パネルが表示されていないなら何もしない
	if ( dispMode!=DISPMODE_PANEL){
		return ERROR;
	}
	dispMode=DISPMODE_ICON;
	//	PANELウィンドゥをDETACHする
	MMI_SendMessage( ID_PANEL[0], MM_SLEEP, 0 ) ;
	MMI_SendMessage( ID_PANEL[0], MM_ERASE, 0 ) ;
	MMI_SendMessage( ID_PANEL[0], MM_DETACH, 0 ) ;

	return NOERR;
}

/*===================================================================*/
/*  ウィンドゥ消去                                                   */
/*===================================================================*/
int		eraseWin()
{
	func_Close();
	MMI_SendMessage( ID_ICON[0], MM_SLEEP, 0 );
	MMI_SendMessage( ID_ICON[0], MM_ERASE, 0 );
	MMI_SendMessage( ID_ICON[0], MM_DETACH, 0 );
	return NOERR;
}

/*===================================================================*/
/*  計測間隔取得(数値入力BOXの関数)                                  */
/*===================================================================*/
int	func_getInt()
{
	int	var, min, max, delta, ptColum;

	if ( MMI_SendMessage( ID_PANEL[ID_NUMGET], MM_GETNUMBOX, 5,
						  &var, &min, &max, &delta, &ptColum ) == NOERR ){
		loadInt = abs( var * (1000/20) );		// 負荷計測間隔(20ms単位)
	}
	return NOERR;
}
