#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winb.h>
#include <te.h>
#include <fntb.h>
#include <gui.h>
#include <egb.h>
#include <guidbg.h>
#include <file_dlg.h>
#include <eintm.h>	// EIN(TM)関連ライブラリのヘッダ
#include "music.h"

int mplayStatus ;

char fdgSavePath[2][MP_PATHLEN] ;

int dispMode ;
int loopMode ;
int tmmMode ;
int autoIconicMode ;

/*	exitFunc関数の復帰値					*/
int		exitFuncRet = ILLEGAL_FUNCTION ;
void	(*GV_defIdleTask)() ;

char	*guiEgbPtr ;			/*	EGB のワークアドレス	*/
//char	*guiSndPtr ;			/*	SND のワークアドレス	*/

// リジュームマネージャに通知する、アプリ識別ID（63文字以下のuniqな文字列）
static char RSMID[]="%%% EupPlayer μPlay for TownsShell %%%";
// リジューム情報に埋め込むタイトル文字列（タスクリストにも併用）
static char RSMTITLE[]="μPlay V1.02a" ;
// リジューム情報読み書きバッファのサイズ
#define RBUFSIZE (1024)
static int RSM_DISPMODE ;
static int RSM_LOOPMODE ;
static int RSM_AUTOICONICMODE ;
static char RSMMUSICPATH[MP_PATHLEN] ;
static char RSMTONEPATH[MP_PATHLEN] ;
static FRAME RSM_ICONFR ;
static FRAME RSM_PANELFR ;
void loadResumeFile(){
	int	x, y, size ;
	char	*ptr;
	HYPER	hyp;
	char	work[RSMWORKSIZE] ;
	char	buf[RBUFSIZE] ;
	extern int mainPanelId ;
	extern int mainIconId ;
	extern int autoIconicBtnId ;
	/*----------------------------------*/
	/*	リジューム読み込み (※のついた処理は必ず行ってください)	*/
	/*----------------------------------*/
	size = 0 ;
	// ※リジュームマネージャの初期化(アプリのIDタグ登録)
	if (EIN_rsmInit(work, RSMID) == 0){
		// ※読み込みバッファ設定
		EIN_rsmBufSet(work, buf, RBUFSIZE) ;
		// ※リジューム情報読み込み
		size = EIN_rsmLoad(work) ;
	}
	if ( size > 0 ){
		// 表示モード( 0:PANEL / 1:ICON,  ... )
		if (((ptr = strstr(buf,"\nMODE: ")) != NULL) &&
			(sscanf(ptr,"\nMODE: %d %d\n", &x, &y) > 1) ){
			dispMode = ((x<0)||(x>1)) ?0 :x ;
			loopMode = ((y<0)||(y>1)) ?0 :y ;
			RSM_DISPMODE = dispMode ;
			RSM_LOOPMODE = loopMode ;
			musLoopMode(loopMode) ;
		}else{
			loopMode = LOOPMODE_LOOP ;
			musLoopMode(loopMode) ;
			dispMode = DISPMODE_PANEL ;
		}
		// ICON表示位置
		if (((ptr = strstr(buf,"\nICON: ")) != NULL) &&
			(sscanf(ptr,"\nICON: %d %d\n", &x, &y) > 1)){
			// 枠座標を得る
			MMI_SendMessage(mainIconId, 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) ;
			if ( x<-31 ) x=0 ;
			if ( y<-31 ) y=0 ;
			MMI_SendMessage(mainIconId, MM_MOVE, 1, &RSM_ICONFR) ;
		}
		// PANEL表示位置
		if (((ptr = strstr(buf,"\nPANEL: ")) != NULL ) &&
			(sscanf(ptr,"\nPANEL: %d %d\n", &x, &y) > 1 ) ){
			// 枠座標を得る
			MMI_SendMessage(mainPanelId, MM_GETHYPER, 1, &hyp) ;
			RSM_PANELFR.lupx = x ;
			RSM_PANELFR.lupy = y ;
			RSM_PANELFR.rdwx = x+(hyp.fr.rdwx-hyp.fr.lupx) ;
			RSM_PANELFR.rdwy = y+(hyp.fr.rdwy-hyp.fr.lupy) ;
			if ( (x>=0)&&(x<1024)&&(y>=0)&&(y<712) ){
				MMI_SendMessage(mainPanelId, MM_MOVE, 1, &RSM_PANELFR) ;
			}
		}else{
			centeringObj(mainPanelId) ;
		}
		if ((ptr = strstr(buf,"\nMUSICPATH: ")) != NULL){
			sscanf(ptr,"\nMUSICPATH: %s\n", fdgSavePath[0]) ;
			strcpy(RSMMUSICPATH, fdgSavePath[0]) ;
		}
		if ((ptr = strstr(buf,"\nTONEPATH: ")) != NULL){
			sscanf(ptr,"\nTONEPATH: %s\n", fdgSavePath[1]) ;
			strcpy(RSMTONEPATH, fdgSavePath[1]) ;
		}
		if ((ptr = strstr(buf,"\nAUTOICONIC: ")) != NULL){
			sscanf(ptr,"\nAUTOICONIC: %d\n", &autoIconicMode) ;
			RSM_AUTOICONICMODE = autoIconicMode ;
			if (autoIconicMode != 0)
				MMI_SendMessage(autoIconicBtnId, MM_SETSHAPE, 1, 529);
		} else {
			autoIconicMode = 0;
		}
	} else {
		loopMode = LOOPMODE_LOOP ;
		musLoopMode(loopMode) ;
		dispMode = DISPMODE_PANEL ;
		centeringObj(mainPanelId) ;
	}
}

void saveResumeFile(){
	extern int noResumeFlag;
	char	work[RSMWORKSIZE] ;
	char	buf[RBUFSIZE] ;
	char	aplpath[128] ;
	HYPER	hyp1, hyp2 ;
	extern int mainPanelId ;
	extern int mainIconId ;

	if (noResumeFlag) return ;

	// 枠座標を得る
	MMI_SendMessage(mainIconId, MM_GETHYPER, 1, &hyp1) ;
	MMI_SendMessage(mainPanelId,MM_GETHYPER, 1, &hyp2) ;
	// 変化していたらセーブ
	if ((RSM_DISPMODE     != dispMode )||
		(RSM_LOOPMODE     != loopMode )||
		(RSM_AUTOICONICMODE != autoIconicMode )||
		(RSM_ICONFR.lupx  != hyp1.fr.lupx)||
		(RSM_ICONFR.lupy  != hyp1.fr.lupy)||
		(RSM_PANELFR.lupx != hyp2.fr.lupx)||
		(RSM_PANELFR.lupy != hyp2.fr.lupy)||
		(strcmp(RSMMUSICPATH, fdgSavePath[0]) != 0)||
		(strcmp(RSMTONEPATH, fdgSavePath[1]) != 0)){
		// ※マネージャ初期化
		if (EIN_rsmInit(work, RSMID) != 0)
			return ;
		// ※バッファ設定(アプリ識別ID,更新時刻は自動設定されます)
		EIN_rsmBufSet(work, buf, RBUFSIZE) ;
		// ※アプリディレクトリ保存
		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(TM)ｱｲｺﾝｻｲｽﾞEUPﾌﾟﾚｰﾔｰ") ;
		// 表示モード保存
		EIN_rsmBufPrintf(work, "MODE: %d %d", dispMode, loopMode) ;
		// アイコン位置保存
		EIN_rsmBufPrintf(work, "ICON: %d %d", hyp1.fr.lupx, hyp1.fr.lupy) ;
		// パネル位置保存
		EIN_rsmBufPrintf(work, "PANEL: %d %d", hyp2.fr.lupx, hyp2.fr.lupy) ;
		// デフォルトパス保存
		EIN_rsmBufPrintf(work, "MUSICPATH: %s", fdgSavePath[0]) ;
		EIN_rsmBufPrintf(work, "TONEPATH: %s", fdgSavePath[1]) ;
		EIN_rsmBufPrintf(work, "AUTOICONIC: %d", autoIconicMode) ;
		// ※バッファをセーブ
		EIN_rsmSave(work);
		RSM_DISPMODE= dispMode;
		RSM_LOOPMODE= loopMode;
		RSM_ICONFR  = hyp1.fr;
		RSM_PANELFR = hyp2.fr;
		strcpy(RSMMUSICPATH, fdgSavePath[0]) ;
		strcpy(RSMTONEPATH, fdgSavePath[1]) ;
	}
}

/****************************************************/
/*	プールデータ解析とパラメータ取得 				*/
/*	実はこの関数は、ＧＷＡＶＥソースとほぼ同じもの	*/
/****************************************************/
int cbAnalysis(POOLDATA *ptr, char *para){
	int				ret = ILLEGAL_FUNCTION ;
	size_t			size, len ;
	unsigned char	*rec, *work ;
	unsigned short	tag ;

	if (ptr->Kind & POOL_REAL)
	{
		/*	テキストブロックの先頭アドレスを探す						*/
		if (POL_cbSearchBlock((unsigned char *)ptr->dPtr, PCBF_TEXT, 1,
															&rec, &size))
		{
			work = POL_cbGetRecSize(rec, &len) ;
			/*	タグ解析中												*/
			do
			{
				tag = *(unsigned short *)work ;
				work = POL_cbGetRecSize(work, &len) ;

				/*	テキストファイル名か？								*/
				if ((tag & 0xff) == PCBF_TEXTFILE)
				{
					/*	ファイル名設定									*/
					strcpy(para, (char *)work) ;
					ret = 0 ;
				}
				work += len ;
			}
			while ((tag & 0xff) != 0x1f) ;	/*	データレコード終了		*/
		}
	}
	return ret ;
}

/************************************************************************/
/*	プールデータヘッダ用領域とCB用領域を確保/初期化/パラメータ設定 		*/
/*	実はこの関数も、ＧＷＡＶＥソースとほぼ同じもの						*/
/************************************************************************/
char *cbSetupArea(char *setupStr){
	typedef struct _CB_SMPTEXT {
		CB_TAG_LONG		Main ;
		CB_TAG			Sub ;
		unsigned short	SubId ;
		CB_TAG			Text ;
	} CB_SMPTEXT ;

	char			*ret = NULL ;
	char 			*ptr ;
	POOLDATA		*pol ;
	unsigned char	*text, *addrec, *cb ;
	size_t			hSize, cbSize, tSize, nSize = 80 ;
	char			str[16] ;

	//	テキストブロックの大きさを計算する
	tSize = sizeof(CB_SMPTEXT) + sizeof(CB_TAG) + nSize ;

	//	"CB"型データ全体の大きさを計算する
	cbSize = PCB_DEF_SIZE + PCB_MREC_SIZE * 2 + tSize ;

	//	プールデータヘッダ用領域の大きさ
	hSize = sizeof(POOLDATA) ;

	//	メモリ獲得時に一定のメモリを残しておく
	if ((TL_checkMemory(1) * 4096) > (1024 * 16 + hSize + cbSize)){
		if ((ptr = TL_malloc(hSize + cbSize))==NULL)
			return ret ;

		//	確保したメモリの先頭をプールデータヘッダ用に
		//	残りを"CB"領域に使用する
		pol = (POOLDATA *)ptr ;
		cb = (unsigned char *)(ptr + hSize);

		//	プールヘッダの設定
		MMI_CallMessage(MMI_GetApliId(),GM_QUERYID, QM_FILENAME, (int)str) ;
		strcpy((char *)&pol->Creator, str) ;		//	ﾃﾞｰﾀ本体編集者
		strcpy((char *)&pol->Owner, str) ;			//	ﾌﾟｰﾙﾃﾞｰﾀ作成者
		strcpy((char *)&pol->Type, POOL_STR_CB) ;	//	ﾃﾞｰﾀ型
		pol->apliId = MMI_GetApliId() ;				//	ﾌﾟｰﾙﾃﾞｰﾀ作成者
													//		ｱﾌﾟﾘｹｰｼｮﾝID
		pol->Kind = POOL_REAL ;						//	ﾃﾞｰﾀ種別
		pol->dPtr = (void *)cb ;					//	ﾃﾞｰﾀｻｲｽﾞ
		pol->dSize = cbSize ;						//	ﾃﾞｰﾀ本体ﾎﾟｲﾝﾀ

		//	CB型アクセス用関数を使用して
		//	CB用に確保した領域をCB領域として初期化する
		if (!POL_cbInit(cb, 2, cbSize, 0)){
			//	"CB"領域にテキストブロックを登録する
			if (!POL_cbAddBlock(cb, PCBF_TEXT, tSize, &text)){
				//	テキストブロックにレコードを追加する
				POL_cbAddRec(text, PCBF_TEXTKIND, 0, 2, &addrec) ;
				*(unsigned short *)addrec = 0 ;

				POL_cbAddRec(text, PCBF_TEXTFILE, 0, nSize, &addrec) ;
				strcpy((char *)addrec, setupStr) ;

				ret = ptr ;
			}
		}
		//	CB領域の設定に失敗した場合は、確保した領域を解放する
		if (!ret)
			TL_free(ptr) ;
	}
	return ret ;
}
static int mplayPauseStatus ;// ポーズ時の状態の保存
static int userFunc(int apliId, int messId, int info, int data){
	register int	ret;

	ret = ILLEGAL_FUNCTION;

	switch(messId){
		case	GM_PAUSE :
			ret = NOERR ;
			mplayPauseStatus = mplayStatus ;
			// アイドルの解除
			resetIdleTask();
			// 演奏を停止する
			musPlayStop() ;
			// 資源を復元する
			musEnd() ;
			break ;
		case	GM_CONTINUE :
			extern char fmbPathName[MP_PATHLEN] ;
			extern char pmbPathName[MP_PATHLEN] ;
			ret = NOERR ;
			// 資源を再初期化する
			musInit() ;
			// 再ロードする
			musLoad(fmbPathName) ;
			musLoad(pmbPathName) ;
			// アイドルタスクに登録
			setIdleTask() ;
			// 可能な限り状態を復元する
			if ((mplayPauseStatus & MPLAY_LOAD) == 0) break ;
			if (mplayPauseStatus & MPLAY_PLAY) musPlayStart() ;
			if (mplayPauseStatus & MPLAY_PAUSE) musPlayPause() ;
			break ;
		case	GM_WAKE :
			//extern int panelic(void);
			//if (autoIconicMode) panelic();
			// ミュートを解除する
			musMuteOff() ;
			break ;
		case	GM_SLEEP :
			// 自動アイコン化する
			extern int iconic(void);
			if (autoIconicMode) iconic();
			break ;
		case	GM_QUIT :
			extern int exitBtnId ;
			MMI_SendMessage(exitBtnId, MM_EXEC, 2, 0, 0) ;
			ret = exitFuncRet ;
			break ;
		case	GM_SENDDATA :
			char para[128] ;
			POOLDATA *ptr ;
			ret = NOERR ;
			//	パラメータの受け取り
			if ((ptr = (POOLDATA *)MMI_CallMessage(MMI_GetApliId(),
								GM_POOLDATA, PM_POOLID, data)) != NULL){
				if (!strcmp((char *)&ptr->Type, POOL_STR_CB)){
					if (cbAnalysis(ptr, para) != ILLEGAL_FUNCTION){
						if (info == MMI_GetApliId()){
							MMI_CallMessage(MMI_GetApliId(), GM_STACKDATA,
															FALSE, data) ;
						}
						//	paraにパラメータ文字列が入っている
						if (musLoad(para) == MPLAY_PLAY_OK)
							musPlayStart() ;
					}
				}
			}
			break ;
	}

	return(ret);
}

int APL_init(){
	extern MMIINIT	initDataMP_PANEL ;
	extern MMIINIT	initDataMP_ICON2 ;
	extern MMIINIT	initDataMP_HELP ;

	register int	ret ;

	/*	EGB ワークアドレスの取得.	*/
	guiEgbPtr = MMI_GetEgbPtr() ;
	/*	SND ワークアドレスの取得.	*/
	// guiSndPtr = MMI_GetSndPtr() ;

	/*	ユーザアイコン設定		*/
	extern unsigned char *iconTblMP_P_ICN[];
	extern int maxidMP_P_ICN;
	MMI_SetIconTable(iconTblMP_P_ICN,maxidMP_P_ICN);

	//	＜＜ ファイルダイアログで使用する部品の初期化 ＞＞
	//	ハイパ型部品の初期化
	if ((ret = MMI_initHyper()) < 0) return ret ;
	//	ダイアログ型部品の初期化
	if ((ret = MMI_initDialogL40()) < 0) return ret ;
	//	アラート型部品の初期化
	if ((ret = MMI_initAlertL40()) < 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_initScrollBarL40()) < 0) return ret ;
	//	テキスト型部品の初期化
	if ((ret = MMI_initTextL40()) < 0) return ret ;
	//	リストメニュー型部品の初期化
	if ((ret = MMI_initListMenuL40()) < 0) return ret ;

	//	＜＜ アプリで使用する部品の初期化 ＞＞
	//	ウインドウ型部品の初期化
	if ((ret = MMI_initWindowL40()) < 0) return ret ;
	//	トグルアイコン型部品の初期化
	if ((ret = MMI_initToggleIconL40()) < 0) return ret ;

	//	背景データの初期化

	//	データの登録
	if ((ret = MMI_Init(&initDataMP_PANEL)) < 0) return ret ;
	if ((ret = MMI_Init(&initDataMP_ICON2)) < 0) return ret ;
	if ((ret = MMI_Init(&initDataMP_HELP)) < 0) return ret ;

	// 256,32kの時のGUI色を設定
	EIN_initGuiColor();

	//	スイッチャー対応関数の登録
	MMI_SendMessage(MMI_GetBaseObj(), MM_SETEXEC, 1, userFunc);

	//	タイトルの登録
	MMI_CallMessage(MMI_GetApliId(), GM_TITLE, (int)RSMTITLE, 0);

	loadResumeFile() ;
	// ヘルプを中央に
	extern int helpPanelId ;
	centeringObj(helpPanelId) ;

	if (dispMode == DISPMODE_PANEL){
		extern int mainPanelId ;
		//	ウィンドウをATTACHする
		MMI_SendMessage(mainPanelId, MM_ATTACH, 1, MMI_GetBaseObj()) ;
		//	ウィンドウをWAKEする
		MMI_SendMessage(mainPanelId, MM_WAKE, 0) ;
	} else {
		extern int mainIconId ;
		//	アイコンをATTACHする
		MMI_SendMessage(mainIconId, MM_ATTACH, 1, MMI_GetBaseObj()) ;
		//	アイコンをWAKEする
		MMI_SendMessage(mainIconId, MM_WAKE, 0) ;
	}
	//	背景を表示する
	MMI_SendMessage(MMI_GetBaseObj(), MM_SHOW, 0) ;

	return NOERR ;
}

void main(int argc, char **argv){
	static MMICTRL mmi ={
				SCREEN16 | \
				SCREENIGNORE,	// ページ0側解像度
				SCREENUNUSED,	// ページ1側解像度
				0, 				// 書き込みページ
				SCREENAVAILABLE,// 表示ページ
				0, 				// 表示プライオリティ
				SCREENAVAILABLE,// 色数
				SCREENEXPAND, 	// VRAMの横の長さ
				0, 				// メモリ領域の大きさ
				NULL, 			// メモリ領域のアドレス
				0, 				// ユーザ領域の大きさ
				NULL, 			// ユーザ領域のアドレス
				0, 0, 			// 画面枠	lupx,lupy
				0, 0,			//			rdwx,rdwy
				-16384, 0,		// 移動枠	lupx,lupy
				16383, 16383,	//			rdwx,rdwy
				15, 			// 白色
				8,  			// 黒色
				7,  			// 灰色
				7	 			// 反転色
	};

	int kobj, i, id ;
	char *ptr ;

	tmmMode = TMM_UNAVAILABLE ;

	// 二重起動のcheck
	kobj = MMI_CallMessage( MMI_GetApliId(), GM_QUERYID, QM_KIND, 1) ;
	if (kobj > NOERR){
		if (argc > 1){
			for(i = 1;i < argc;i++)
				if ((ptr = cbSetupArea(argv[i])) != NULL){
					// プールへデータを送る
					id = MMI_CallMessage(MMI_GetApliId(), GM_STACKDATA,
														TRUE, (int)ptr) ;
					TL_free(ptr) ;
					// μPlayにパラメータを送信する
					if (id > NOERR)
						MMI_CallMessage(kobj, GM_SENDDATA, kobj, id) ;
				}
		}
		MMI_CallMessage( MMI_GetApliId(), GM_SWITCH, FALSE, kobj ) ;
		return ;
	}

	/*	初期化処理	*/
	if (MMI_Open( &mmi ) == NOERR){
		/*	サウンドの初期化	*/
		musInit() ;
		// アイドルタスクに登録
		setIdleTask() ;
		/*	初期化に成功すればメインループに入る.	*/
		if (APL_init() == NOERR) {
			for(i = 1; i < argc; i++)	//	パラメータの読込
				if (musLoad(argv[i]) == MPLAY_PLAY_OK)
					musPlayStart() ;
			/*	ファイルダイアログの初期化	*/
			if (FDG_InitFileDlg() >= 0){
				/*	イベントループ(メイン)に入る	*/
				MMI_ExecSystem() ;
				/*	ファイルダイアログのメモリ開放	*/
				FDG_FreeFileDlg() ;
			}
		}
		// アイドルの解除
		resetIdleTask();
		/*	サウンドの終了処理	*/
		musEnd() ;
	}

	/*	終了処理	*/
	MMI_Close() ;
}

/*===================================================================*/
/*  アイドル関数                                                     */
/*===================================================================*/
void	userIdleTask(){
	extern	int	updateMeas() ;
	extern	int	dispFreeMem() ;
	extern	int	helpPage ;

	if (helpPage == 1)
		dispFreeMem() ;
	if (tmmMode == TMM_UNAVAILABLE)
		updateMeas() ;
}

/*===================================================================*/
/*  アイドルタスクに登録                                             */
/*===================================================================*/
int		setIdleTask(){
	void	(*idleTask)() ;

	//	アイドル処理関数の取得
	idleTask = MMI_GetIdleTaskFunc() ;

	//	二重登録禁止チェック
	if(idleTask != userIdleTask){
		GV_defIdleTask = idleTask ;
		MMI_SetIdleTaskFunc((void ((*)(void)))userIdleTask);
	}

	return NOERR ;
}

/*===================================================================*/
/*  アイドルタスクから削除                                           */
/*===================================================================*/
int		resetIdleTask(){
	void	(*IdleTask)() ;

	//	アイドル処理関数の取得
	IdleTask = MMI_GetIdleTaskFunc() ;

	//	USER関数登録のチェック
	if(IdleTask == userIdleTask){
		MMI_SetIdleTaskFunc(GV_defIdleTask) ;
		GV_defIdleTask = 0 ;
	}

	return NOERR ;
}
