// ------------------------------------------------------------------------------------------------
// mware.c
//
// This encapsulates the middleware functionality for the mw_snd64 testbed.
//
// COPYRIGHT (C) SEGA OF AMERICA, INC. 1998
// All rights reserved.
//
// Information and source code samples contained herein are 
// provided "as-is", without representations or warranties, and 
// are subject to change without notice.
//
// SEGA OF AMERICA, INC. cannot support modifications or 
// derivative works created from the sample source code provided. 
// Developers may use, reproduce, and modify portions or entire 
// sections of the sample source code for the purposes of creating
// Dreamcast applications.  
//
// Sample source code contained herein may not be appropriate 
// for use under all circumstances.  Please consult Developer 
// documentation provided along with your Dreamcast unit for
// additional information and suggestions.
//
//
// Author: Sean Hunt
//
// Rev:			06/08/99	File Originated (based on existing Strsfx and MW_ply01 samples)
//				06/08/99	Modified by Sean Hunt
//				06/09/99	Added appropriate background color changes upon entering
//								or exiting movie playback
//				06/21/99	Stripped out Duck (unnecessary to switch)
//								
// 
// ------------------------------------------------------------------------------------------------

#include <shinobi.h>
#include <sg_sd.h>
#include <sg_mw.h>

#define AUDIO_SW		ON	//	play audio if interleaved (default = ON)

//	TrueMotion defines
#define TM_VB_OP		100000
#define TM_VB_OM		0
#define TM_VB_TP		100000
#define TM_VB_TM		0
#define TM_VB_PT		0

/*	NINJA			*/
#define	TEX_NUM			100
#define	TEXNAME_NUM		2
#define	TEX_BUFSIZE		0x20800

/*	NINJA				*/
NJS_TEXMEMLIST	tex[TEX_NUM];
NJS_TEXINFO		info[TEXNAME_NUM];
NJS_TEXNAME		texname[TEXNAME_NUM];
NJS_TEXLIST		texlist = {texname, TEXNAME_NUM};
Sint8			texbuf[TEX_BUFSIZE];
Uint16			framebuf[1024*512*2];

#define SYS_MODE		NJD_RESOLUTION_640x480_NTSCI
#define SYS_FRAME		NJD_FRAMEBUFFER_MODE_ARGB8888
#define SYS_COUNT		1

//	Vertex Buffer
#define SFD_VB_OP		-500000
#define SFD_VB_OM		0
#define SFD_VB_TP		20000
#define SFD_VB_TM		0
#define SFD_VB_PT		0

/*	Buffer size for play Audio Decoder	*/
#define	AUDIO_BUF_SIZE	MWD_PLY_CALC_AWORK(MWD_PLY_MIN_AWORK)
#define	MPA_BUF_SIZE	AUDIO_BUF_SIZE

/*	Turn over flag(ON/OFF)	*/
#define NOT_FLAG(flag)		(((flag) == OFF) ? ON : OFF)

/*	The number of error	*/
Sint32 errcnt = 0;

/*	Error message		*/
char errmsg[256] = "(OK)";

/*	Flag for a show of character	*/
Sint32 disp_flag = ON;

/*	Pause status	*/
Sint32 pause = OFF;

void play_sfd(char *fname);
void play_tm(char *fname);
void play_mpa(char *fname);
void play_main(MWPLY ply, char *fname);
void MwExit(void);
static void UsrMwErrFunc(void *obj, char *ecode);
static void DispInfo(MWPLY ply, char *fname);
static void Msec2Time(long mstime, long *hh, long *mm, long *ss, long *ff);
static void initMiddleWare( void );
static void *LoadFile(char *fname, long *len);
static void SoundInit(void *sndptr, void *mltptr);

void initMiddleWare( void )
{
	void	*snddrv;
	void	*mltfile;

	MWS_PLY_INIT_SFD		iprm;
	
	/*	Prepare sound library		*/
	snddrv  = LoadFile("manatee.drv", NULL);
	mltfile = LoadFile("stream.mlt", NULL);
	SoundInit(snddrv, mltfile);
	syFree( snddrv );
	syFree( mltfile );

	memset(&iprm, 0, sizeof(iprm));	
									/*	To initialize reserved member		*/

	iprm.mode		= SYS_MODE;
	iprm.frame		= SYS_FRAME;
	iprm.count		= SYS_COUNT;
	iprm.latency	= MWD_PLY_LATENCY(SFD_VB_OP, SFD_VB_OM,
											SFD_VB_TP, SFD_VB_TM, SFD_VB_PT);

	mwPlyInitSofdec(&iprm);
	mwPlyInitTM();
	mwPlyInitMpa(MWD_PLY_SVR_VSYNC);
}

void play_sfd(char *fname)
{
	MWPLY					ply;
	MWS_PLY_CPRM_SFD		cprm;

	initMiddleWare();
	/*	Set background color to black so it looks like fullscreen	*/
	njSetBackColor( 0x00000000, 0x00000000, 0x00000000 );
	/*	Create handle	*/
	memset(&cprm, 0, sizeof(cprm));	/*	予約メンバのゼロ設定のために必要	*/
									/*	To initialize reserved member		*/
	cprm.ftype			= MWD_PLY_FTYPE_SFD;
	cprm.dtype			= MWD_PLY_DTYPE_AUTO;
	cprm.max_bps = 450*1024*8;
	cprm.max_width = 320;
	cprm.max_height = 480;
	cprm.nfrm_pool_wk = 3;
	cprm.wksize = mwPlyCalcWorkSofdec(
								cprm.ftype,
								cprm.max_bps,
								cprm.max_width,
								cprm.max_height,
								cprm.nfrm_pool_wk);
	cprm.work = (Sint8 *)syMalloc(cprm.wksize);
	ply = mwPlyCreateSofdec(&cprm);
	if (ply == NULL) {
		for (;;) ;
	}
	mwPlySetAudioSw(ply, AUDIO_SW);
	play_main(ply, fname);
	syFree(cprm.work);
	/*	Set background color back to gradient	*/
	njSetBackColor( 0x00000000, 0x00000000, 0x000000FF );
	MwExit();

	return;
}

void play_tm(char *fname)
{
	MWPLY				ply;
	MWS_PLY_CPRM_TM		cprm;

	initMiddleWare();
	/*	NINJA初期化			*/
	/*	Initialize NINJA	*/
	njInitVertexBuffer(TM_VB_OP, TM_VB_OM, TM_VB_TP, TM_VB_TM, TM_VB_PT);
	/*	Set background color to black so it looks like fullscreen	*/
	njSetBackColor( 0x00000000, 0x00000000, 0x00000000 );
	njInitPrint(NULL, 0, NJD_TEXFMT_ARGB_8888);
	njPrintSize(12);
	njInitTexture(tex, TEX_NUM);
	njInitTextureBuffer(texbuf, TEX_BUFSIZE);
	njSetTextureInfo(&info[0], framebuf,
					(NJD_TEXFMT_YUV_422 | NJD_TEXFMT_STRIDE), 1024, 512);
	njSetTextureInfo(&info[1], framebuf + 1024 * 512,
					(NJD_TEXFMT_YUV_422 | NJD_TEXFMT_STRIDE), 1024, 512);
	njSetTextureName(&texname[0], &info[0], 0,
					(NJD_TEXATTR_TYPE_MEMORY | NJD_TEXATTR_GLOBALINDEX));
	njSetTextureName(&texname[1], &info[1], 1,
					(NJD_TEXATTR_TYPE_MEMORY | NJD_TEXATTR_GLOBALINDEX));
	njLoadTexture(&texlist);
	njSetTexture(&texlist);

	/*	Create handle	*/
	ply = mwPlyCreateTM(&cprm);
	if (ply == NULL) {
		for (;;) ;
	}
	play_main(ply, fname);

	/*	Finish NINJA	*/
	njReleaseTextureAll();
	njExitTexture();

	/*	Initialize NINJA	*/
	njInitVertexBuffer(SFD_VB_OP, SFD_VB_OM, SFD_VB_TP, SFD_VB_TM, SFD_VB_PT);
	/*	Set background color back to gradient	*/
	njSetBackColor( 0x00000000, 0x00000000, 0x000000FF );
	njInitPrint(NULL, 0, NJD_TEXFMT_ARGB_8888);
	njPrintSize(12);
	MwExit();
	return;
}

void play_mpa(char *fname)
{
	MWPLY			ply;
	MWS_PLY_CPRM	cprm;

	initMiddleWare();
	/*	ハンドルの生成	*/
	/*	Create handle	*/
	cprm.buf     = syMalloc(MPA_BUF_SIZE);
	cprm.size    = MPA_BUF_SIZE;
	cprm.libwork = syMalloc(MWD_MPA_CALC_WORK(1));
	ply = mwPlyCreateMpa(&cprm);
	if (ply == NULL) {
		for (;;) ;
	}
	play_main(ply, fname);
	syFree(cprm.libwork);
	syFree(cprm.buf);

	MwExit();
	return;
}


void play_main(MWPLY ply, char *fname)
{
	MWE_PLY_STAT			stat;
	const PDS_PERIPHERAL	*per;

	mwPlyEntryErrFunc((void *)UsrMwErrFunc, &ply);
	mwPlyPause(ply, pause);				/*	前回再生のポーズ状態を維持		*/
										/*	Keep the last time pause status	*/
	mwPlyStartFname(ply, fname);		/*	再生開始						*/
										/*	Start to play middleware		*/
	while (1) {
		njWaitVSync();
		mwPlyStartFrame();
		per = pdGetPeripheral(PDD_PORT_A0);
		if (per->press & PDD_DGT_TA) {
			disp_flag = NOT_FLAG(disp_flag);
		}
		if (per->press & PDD_DGT_TX) {
			pause = ON;
			mwPlyPause(ply, ON);		/*	一時停止				*/
										/*	Pause					*/
		}
		if (per->press & PDD_DGT_TY) {
			pause = OFF;
			mwPlyPause(ply, OFF);		/*	一時停止解除			*/
										/*	Release pause status	*/
		}
		if (per->press & PDD_DGT_ST) {
			break;
		}
		stat = mwPlyGetStat(ply);
		if ((stat == MWE_PLY_STAT_PLAYEND) || (stat == MWE_PLY_STAT_ERROR)) {
			break;
		}
		if (disp_flag == ON) {
			DispInfo(ply, fname);
		}
		mwPlyExecServer();
	}
	mwPlyStop(ply);						/*	再生停止 					*/
										/*	Stop to play middleware		*/
	njWaitVSync();						/*	パッドの状態取得のため		*/
										/*	for getting status of PAD	*/
	mwPlyDestroy(ply);					/*	ハンドル消去				*/
										/*	Destroy handle				*/

	return;
}

/*	Callback function when an error occur in middleware	*/
void UsrMwErrFunc(void *obj, char *ecode)
{
	errcnt++;
	strncpy(errmsg, ecode, 256);

	return;
}

void MwExit(void)
{
	/*	ライブラリ終了処理					*/
	/*	Finalize Middleware					*/
	
	mwPlyFinishSofdec();
	mwPlyFinishTM();
	mwPlyFinishMpa();
	mwPlyFinishWav();

}

/*	Display infomation	*/
void DispInfo(MWPLY ply, char *fname)
{
	long			time, tunit, hh, mm, ss, ff, time_ms;
	float			timef;

	mwPlyGetTime(ply, &time, &tunit);
	timef = ((float)time / (float)tunit) * 1000.0;
	time_ms = (long)timef;
	Msec2Time(time_ms, &hh, &mm, &ss, &ff);
	ff = ff*30/1000;
	njPrint(NJM_LOCATION(0, 3), "File: %s  Time:%02d:%02d:%02d.%02d",
							fname, hh, mm, ss, ff);
	njPrint(NJM_LOCATION(0, 4), "Error%2d:%s", errcnt, errmsg);

	return;
}

/*	msec -> time(hour,min,sec,frame)	*/
void Msec2Time(long mstime, long *hh, long *mm, long *ss, long *ff)
{
	*hh = mstime / (60*60*1000);
	*mm = (mstime - *hh*60*60*1000) / (60*1000);
	*ss = (mstime - *hh*60*60*1000 - *mm*60*1000) / 1000;
	*ff = mstime % 1000;

	return;
}

/*	Load data to memory	*/
void *LoadFile(char *fname, long *len)
{
	GDFS			gdfs;
	long			fsize, nsct;
	void			*dat;

	gdfs = gdFsOpen(fname, NULL);
	if (gdfs == NULL) {
		for (;;) ;
	}
	gdFsGetFileSize(gdfs, &fsize);
	nsct = (fsize + 2047) / 2048;
	dat = syMalloc(nsct * 2048);
	if (dat == NULL) {
		for (;;) ;
	}
	gdFsRead(gdfs, nsct, dat);
	gdFsClose(gdfs);
	if (len != NULL) {
		*len = fsize;
	}

	return dat;
}

/*	Initialize sound library and load sound driver	*/
void SoundInit(void *sndptr, void *mltptr)
{
	SDMEMBLK	memblk = NULL;

	/*	Initialize Sound library	 */
	sdLibInit(NULL, 0, 0);		/*	This api's argument is dummy now.  */

	/* 	Download and Initialize Sound driver.	 */
	/*	Create Memory block handle  */
	sdMemBlkCreate(&memblk);
	/*  Setting Memory block handle  */
	sdMemBlkSetPrm(memblk, sndptr, 0, SDD_MEMBLK_SYNC_FUNC, NULL);
	/*	Initialize Sound driver  */
	sdDrvInit(memblk);

	/*	Download and Initialize Multi unit	*/
	/*	Setting Memory block handle	*/
	sdMemBlkSetPrm(memblk, mltptr, 0, SDD_MEMBLK_SYNC_FUNC, NULL);
	sdDownloadMultiUnit(memblk);

	sdMemBlkDestroy(memblk);

	/*	Truemotion Only CPU mode	*/
#if	0
	/*	Setting transfer mode	*/
	sdMemBlkSetTransferMode(SDE_MEMBLK_TRANSFER_MODE_DMA);
#endif

	return;
}
