/***************************************************************************
 *
 * CRI Middleware SDK
 *
 * Copyright (c) 2005-2006 CRI-MW
 *
 * Appli.   : CRI Middleware Sample Program
 * Module   : Functions of PS3 PPU Oparation
 * File     : usr_ps3ppu.c
 *
 ***************************************************************************/
/***************************************************************************
 *	NOTE : 
 *	This program is for ...
 *		PLAYSTATION(R)3 Programmer Tool Runtime Library 082.006
 *		DEH-R10xx
 *
 *	Please set necessary sound file in home directory of logical
 *	consolle server.
 ***************************************************************************/

/***************************************************************************
 *      インクルードファイル
 *      Include files
 ***************************************************************************/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

#include <sdk_version.h>
#include <sys/process.h>
#include <sys/paths.h>
#include <sys/spu_initialize.h>
#include <sysutil/sysutil_sysparam.h>

#include <cell/cell_fs.h>
#include <cell/audio.h>
#include <cell/mixer.h>

#include <PSGL/psgl.h>
#include <PSGL/psglu.h>

#include <cell/dbgfont.h>

#include <cri_mw.h>
#include <cri_ut.h>
#include <cri_ut_ps3.h>
#include "usr.h"
#include "usr_ps3ppu.h"

/***************************************************************************
 *      定数
 *      Constants
 ***************************************************************************/
/* libfsのマウントポイント */
/* Mount point for libfs */
#define MOUNT_POINT		SYS_APP_HOME

/* サラウンドミキサの設定 */
/* Configurations for SurMixer */
#define SUR_MIXER_PRIORITY		(400)
#define SUR_MIXER_CHSTRIPS1		(0)
#define SUR_MIXER_CHSTRIPS2		(0)
#define SUR_MIXER_CHSTRIPS6		(0)
#define SUR_MIXER_CHSTRIPS8		(0)

/* ヘッドホンを使用するかどうか */
/* Using headphone */
#define USE_HEADPHONE	(1)

/* Configurations for CRI's middlewares ========================= */
/* ミドルウェアフレームワーク */
//#define MW_FRAMEWORK	(ADXM_FRAMEWORK_PS3PPU_SINGLE_THREAD)	/* single thread */
//#define MW_FRAMEWORK	(ADXM_FRAMEWORK_PS3PPU_MULTI_THREAD)	/* multi thread */
#define MW_FRAMEWORK	(ADXM_FRAMEWORK_DEFAULT)	/* default = multi thread */
/* 出力チャンネル数 */
/* The number of output channels */
#define NUM_OUTPUT_CHANNELS		(2)
/* 最大ボイス数 */
/* The maximum number of voices("voice" means one sound) */
#define NUM_MAX_VOICES			(8)
/* ============================================================== */

/* パッドのデバッグ情報を表示するかどうか */
#define PAD_DEBUG_SW			(ON)

#define SCREEN_WIDTH	(1280)	/* 横方向画面サイズ（ピクセル） */
#define SCREEN_HEIGHT	(720)	/* 縦方向画面サイズ（ピクセル） */

/* 表示文字列の最大長				*/
/* Max length of displayed string	*/
#define DISP_STR_LEN_MAX	(256)

/***************************************************************************
 *      処理マクロ
 *      Macros
 ***************************************************************************/
#if !defined UNUSED
#define UNUSED(arg)		{ if ((arg) == (arg)) {} }
#endif

/***************************************************************************
 *      データ型宣言
 *      Data Type Declarations
 ***************************************************************************/

/***************************************************************************
 *      関数宣言
 *      Function Declarations
 ***************************************************************************/
/* Initialize Video */
static Sint32 initializeVideo();
/* Finalize Video */
static Sint32 finalizeVideo();
/* Initialize Audio */
static Sint32 initializeAudio();
/* Finalize Audio */
static Sint32 finalizeAudio();

/* サラウンドミキサにオーディオデータを入力するための通知コールバック関数 */
/* Sound notification callback for SurMixer */
static Sint32 soundNotifyCallback(void *arg, Uint32 counter, Uint32 num_samples);

/***************************************************************************
 *      変数定義
 *		Variable Definitions
 ***************************************************************************/
/* Vsyncカウンタ */
static Sint32 g_vblank_count = 0;

static PSGLdevice *g_psgl_device;		// PSGL device

/***************************************************************************
 *      関数定義
 *      Function Definitions
 ***************************************************************************/
/*	システムの初期化										*/
/*	Initialize System										*/
void UsrInitSystem(const void *in, void *out)
{
	Sint32 err;
	Adxps3SprmPs3Fs sprm_ps3fs;

	UNUSED(out);
	UNUSED(in);

	err = cellSysutilInit();
	if (err < 0) {
		printf("cellSysutilInit() error : %#x\n", err);
		return;
	}

	/* タイマライブラリの初期化 */
	/* Initialize timer library */
	UTTMR_Init();
	printf("UTTMR_GetFreq: %llu\n", UTTMR_GetFreq());

	/* パッドライブラリの初期化 */
	/* Initialize pad library */
	UTPAD_SetDebugSw(PAD_DEBUG_SW);
	UTPAD_Init();

	/* ADXファイルシステムのセットアップ */
	/* Set up middleware file streaming I/O */
	memset(&sprm_ps3fs, 0, sizeof(sprm_ps3fs));
	sprm_ps3fs.rtdir = MOUNT_POINT;
	ADXPS3_SetupPs3Fs(&sprm_ps3fs);

	/* CRIミドルウェアフレームワークのセットアップ */
	/* Set up middleware framework */
	ADXM_SetupFramework(MW_FRAMEWORK, NULL);

	return;
}

/*	システムの終了										*/
/*	Finish System										*/
void UsrFinishSystem(void)
{
	/* CRIミドルウェアのフレームワークの終了 */
	/* Shutdown middleware framework */
	ADXM_ShutdownFramework();

	/* ADXファイルシステムのシャットダウン */
	/* Shutdown middleware streaming file I/O */
	ADXPS3_ShutdownPs3Fs();

	/* パッドライブラリの終了処理 */
	/* Finalize pad library */
	UTPAD_Finish();

	/* タイマライブラリの終了処理 */
	/* Finalize timer library */
	UTTMR_Finish();
}

/* ビデオの初期化			*/
/* Initialize video system	*/
void UsrInitVideo(const void *in, void *out)
{
	PSGLinitOptions options;
	PSGLcontext *psgl_context;

	// Initialize Video
	initializeVideo();

	// Create a PSGL device
	PSGLbufferParameters params = {
		width:SCREEN_WIDTH,
		height:SCREEN_HEIGHT,
		colorBits:24,
		alphaBits:8,
		depthBits:24,
		stencilBits:8,
		deviceType:PSGL_DEVICE_TYPE_TV,
		TVStandard:PSGL_TV_STANDARD_HD720P,
		TVFormat:PSGL_TV_FORMAT_YCRCB,
		bufferingMode:PSGL_BUFFERING_MODE_DOUBLE,
		antiAliasing:GL_TRUE
	};

	UNUSED(in);
	UNUSED(out);

	sys_spu_initialize(6, 2);
	options.enable = PSGL_INIT_MAX_SPUS | PSGL_INIT_INITIALIZE_SPUS;
	options.maxSPUs = 1;
	options.initializeSPUs = GL_FALSE;
	psglInit(&options);

	if ((g_psgl_device = psglCreateDevice(&params)) == NULL) {
		printf("Failed to create a PSGL device.");
		for (;;);
	}

	// Create a PSGL context
	if ((psgl_context = psglCreateContext()) == NULL) {
		printf("Failed to create a PSGL device.");
		for (;;);
	}
	psglMakeCurrent(psgl_context, g_psgl_device);

	// Reset the screen
	glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
	glScissor(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
	glClearColor(0.f, 0.f, 0.f, 1.f);
	glEnable(GL_DEPTH_TEST);
	glFlush();

	// set the background color
	glClearColor(0.f, 0.f, .120f, 1.f);

	/* デバッグ表示ライブラリの初期化 */
	/* Initialize debug print library */
	UTPRT_Init(NULL);
	UTPRT_SetColor(255, 255, 255);
}

/*	ビデオの終了										*/
/*	Finish Video	 									*/
void UsrFinishVideo(void)
{
	PSGLcontext *psgl_context;

	/* デバッグ表示ライブラリの終了処理 */
	/* Finalize debug print library */
	UTPRT_Finish();

	psgl_context = psglGetCurrentContext();
	if (psgl_context != NULL) {
		psglDestroyContext(psgl_context);
	}

	psglDestroyDevice(g_psgl_device);
	psglExit();

	// Finalize Video
	finalizeVideo();

	return;
}


/*	サウンドの初期化										*/
/*	Initialize Sound System									*/
void UsrInitSound(const void *in, void *out)
{
	Adxps3SoundConfig	config;
	CellAANHandle		sur_mixer_handle;
	Sint32				err;

	UNUSED(in);
	UNUSED(out);

	// Initialize Audio
	initializeAudio();

	/* サラウンドミキサのハンドルを取得する */
	/* Get SurMixer handle */
	err = cellSurMixerGetAANHandle(&sur_mixer_handle);
	if (err < 0){
		printf("cellSurMixerGetAANHandle() error : %#8x\n", err);
		return;
	}

	/* サウンドシステムの設定 */
	/* Sound system configuration */
	memset(&config, 0, sizeof(config));
	config.mixer_handle			= sur_mixer_handle;
	config.num_output_channels	= NUM_OUTPUT_CHANNELS;
	config.num_max_voices		= NUM_MAX_VOICES;
	config.work_size			= ADXPS3_CalcSoundWorkSize(&config);
	config.work					= malloc(config.work_size);
	if (config.work == NULL) {
		printf("Failed to allocate memory for work area of sound system\n");
		return;
	}
	/* サウンドシステムのセットアップ */
	/* Set up middleware sound system */
	ADXPS3_SetupSound(&config);

	/* サラウンドミキサの開始 */
	/* Start SurMixer */
	err = cellSurMixerStart();
	if (err < 0){
		printf("cellSurMixerStart() error : %#8x\n", err);
		return;
	}

	return;
}

/*	サウンドの終了											*/
/*	Finish Sound System										*/
void UsrFinishSound(void)
{
	/* ■補足															*/
	/* サラウンドミキサからのコールバックを止めるため、					*/
	/* ADXPS3_ShutdownSound関数を呼ぶ前にサラウンドミキサをポーズする必	*/
	/* 要があります。													*/
	/* ### NOTE ###														*/
	/* When you're going to shutdown the ADX sound system, pause the	*/
	/* SurMixer to stop the callback.									*/
	/* Pause SurMixer immediatelly */
	cellSurMixerPause(CELL_SURMIXER_CONT_PAUSE_ON_IMMEDIATE);

	/* サウンドシステムのシャットダウン */
	/* Shutdown middleware sound system */
	ADXPS3_ShutdownSound();

	// Finalize Audio
	finalizeAudio();

	return;
}

void UsrInitSync(Sint32 cnt)
{
	UNUSED(cnt);
}

void UsrSyncFrame(void)
{
	UsrWaitVBlank();
}

void UsrBeginDraw(void)
{
	// Enable swap synchronized to Vsync
	glEnable(GL_VSYNC_SCE);
	// Clear buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	glColor4f(1.f, 1.f, 1.f, 1.f);
	
	UTPRT_Clear();
}

void UsrEndDraw(void)
{
	UTPRT_Draw();

	psglSwap();
}

void UsrWaitVBlank(void)
{
	ADXM_WaitVsync();
	g_vblank_count++;
}

Sint32 UsrPrintf(Sint32 lx, Sint32 ly, const Char8 *fmt, ...)
{
	va_list	args;
	Sint32 	ret = 0;
	Char8	string[DISP_STR_LEN_MAX];

	va_start(args, fmt);
	vsprintf(string, fmt, args);
	va_end(args);

	UTPRT_Print(lx, ly, "%s", string);

#if 0
	// output to console
	va_list	args;
	Sint32 	ret = 0;
	static Sint32 ly_old = 0;

	UNUSED(lx);
//	UNUSED(ly);
//	UNUSED(fmt);

	/* 1秒に一回だけ表示する */
	if ((g_vblank_count % 60) == 0) {
		if (ly != ly_old) {
			printf("\n");
		}
		va_start(args, fmt);
		ret = vprintf(fmt, args);
		va_end(args);
		ly_old = ly;
	}
	if ((g_vblank_count % 60) == 1) {
		if (ly_old != 0) {
			printf("\n");
			ly_old = 0;
		}
	}
#endif

	return ret;
}

/*	Print string to serial output */
Sint32 UsrDbgPrintf(const Char8 *fmt, ...)
{
	va_list	args;
	Sint32 	ret;

	va_start(args, fmt);
	ret = vprintf(fmt, args);
	va_end(args);

	return ret;
}

void UsrSetColor(Uint8 r, Uint8 g, Uint8 b)
{
	UNUSED(r);
	UNUSED(g);
	UNUSED(b);
}

void UsrGetMetrix(Sint32 *w, Sint32 *h)
{
	UNUSED(w);
	UNUSED(h);
}

const USR_PERIPHERAL* UsrGetPeripheral(Uint32 port)
{
	static USR_PERIPHERAL	peri[USR_PD_PORT_MAX];
	const UTPAD_PERIPHERAL	*ut_peri;

	/* パッドの情報を取得する	*/
	/* Get peripheral states	*/
	ut_peri = UTPAD_GetPeripheral(port);

	peri[port].on    = ut_peri->on;
	peri[port].press = ut_peri->press;
	peri[port].x1    = (Sint16)(ut_peri->x * 128.0f);
	peri[port].y1    = (Sint16)(ut_peri->y * 128.0f);

	return &peri[port];
}

/*	Convert from sampling frequency and counter to time(hour,min,sec,frame)	*/
void UsrSfcnt2time(Sint32 sf, Sint32 ncnt, 
					Sint32 *hh, Sint32 *mm, Sint32 *ss, Sint32 *ff)
{
	Sint32 nf = (Sint32)((Float32)ncnt/(Float32)sf*100.0f);

	*hh = nf / (60 * 60 * 100);
	*mm = nf / (60 * 100) - (*hh) * 60;
	*ss = nf / 100 - (*hh) * 60 * 60 - (*mm) * 60;
	*ff = nf % 100;
}

Uint32 UsrGetTimeMicro(void)
{
	return UTTMR_CountToMicro(UTTMR_GetCount());
}

void *UsrMalloc(Uint32 size)
{
	return malloc(size);
}

void UsrFree(void *ptr)
{
	free(ptr);
}

/* 終了するかどうかの確認					*/
/* Check whether Usr module is terminated	*/
Bool UsrIsTerminated(void)
{
	return FALSE;
}


/* Get vsync count	*/
Uint32	UsrGetVcount(void)
{
	return g_vblank_count;
}

/* Initialize Video */
static Sint32 initializeVideo()
{
	Sint32 err;
	CellVideoOutConfiguration v_config;
	
	/* video configuration */
	memset(&v_config, 0, sizeof(v_config));
	v_config.resolutionId	= CELL_VIDEO_RESOLUTION_720;
	v_config.format			= CELL_VIDEO_BUFFER_COLOR_FORMAT_X8R8G8B8;
	v_config.pitch			= 1280 * 4;
	err = cellVideoOutConfigure(CELL_VIDEO_OUT_PRIMARY, &v_config, NULL, 0);
	if (err < 0) {
		printf("cellVideoOutConfigure() error : %#8x\n", err);
		return err;
	}

	return 0;
}

/* Finalize Video */
static Sint32 finalizeVideo()
{
	return 0;
}

/* Initialize Audio */
static Sint32 initializeAudio()
{
	Sint32 err;
	CellAudioOutConfiguration a_config;
	CellSurMixerConfig sur_mixer_config;

	/* audio configuration */
	memset(&a_config, 0, sizeof(a_config));
	a_config.channel	= 2;
	a_config.downMixer	= CELL_AUDIO_DOWNMIXER_NONE;
	err = cellAudioOutConfigure(CELL_AUDIO_OUT_PRIMARY, &a_config, NULL, 0);
	if (err < 0) {
		printf("cellAudioOutConfigure() error : %#8x\n", err);
		return err;
	}

	// Initialize AudioServer
	err = cellAudioInit();
	if (err < 0){
		printf("cellAudioInit() error : %#8x\n", err);
		return err;
	}

	/* ■補足																*/
	/* PS3用ADXのサウンドシステムはlibmixerを使用します。					*/
	/* libmixerを使用するための各種設定はアプリケーション側で行います。		*/
	/* ### NOTE ###															*/
	/* The ADX sound system uses the CellSurMixer(libmixer).				*/
	/* You have to configure and create the CellSurMixer.					*/

	/* SurMixer configuration */
	memset(&sur_mixer_config, 0, sizeof(sur_mixer_config));
	sur_mixer_config.priority	= SUR_MIXER_PRIORITY;
	sur_mixer_config.chStrips1	= SUR_MIXER_CHSTRIPS1;
	sur_mixer_config.chStrips2	= SUR_MIXER_CHSTRIPS2;
	sur_mixer_config.chStrips6	= SUR_MIXER_CHSTRIPS6;
	sur_mixer_config.chStrips8	= SUR_MIXER_CHSTRIPS8;

	/* Create SurMixer */
	err = cellSurMixerCreate(&sur_mixer_config);
	if (err < 0) {
		printf("cellSurMixerCreate() error : %#8x\n", err);
		return err;
	}

	/* ■補足																*/
	/* アプリケーション側でサラウンドミキサへのデータ入力をまったく行わない	*/
	/* 場合は、ADXPS3_SoundNotifyCallback関数を直接サラウンドミキサに登録し	*/
	/* ても問題ありません。													*/
	/* ### NOTE ###															*/
	/* If application does not input audio data to SurMixer, you can set	*/
	/* ADXPS3_SoundNotifyCallback() to SurMixer directly.					*/
	/* Set notify callback */
	err = cellSurMixerSetNotifyCallback(soundNotifyCallback, NULL);
	if (err < 0) {
		printf("cellSurMixerSetNotifyCallback() error : %#8x\n", err);
		return err;
	}

#if (USE_HEADPHONE == 1)
	printf("Set TotalLevel for Headphone\n");
	/* Set total level for headphone */
	err = cellSurMixerSetParameter(CELL_SURMIXER_PARAM_TOTALLEVEL, -24.0);
	if (err < 0) {
		printf("cellSurMixerSetParameter() error : %#8x\n", err);
		return err;
	}
#endif

	return 0;
}

/* Finalize Audio */
static Sint32 finalizeAudio()
{
	Sint32 err;

	err = cellSurMixerFinalize();
	if (err < 0){
		printf("cellSurMixerFinalize() error : %#8x\n", err);
		return err;
	}

	// Finalize AudioServer
	err = cellAudioQuit();
	if (err < 0){
		printf("cellAudioQuit() error : %#8x\n", err);
		return err;
	}

	return 0;
}

/* サラウンドミキサにオーディオデータを入力するための通知コールバック関数 */
/* Sound notification callback for SurMixer */
static Sint32 soundNotifyCallback(void *arg, Uint32 counter, Uint32 num_samples)
{
	Sint32 ret;
	
	/* ■補足																*/
	/* アプリケーション側で独自の（または他のサウンドライブラリによる）サウ	*/
	/* ンド出力を行う場合は、ここでサラウンドミキサにサウンドデータを入力し	*/
	/* ます。																*/
	/* ### NOTE ###															*/
	/* If application needs own(or by other sound library) sound output,	*/
	/* input sound data to SurMixer here.									*/
	/* Application sound output				*/
	/*					:					*/
	/*					:					*/

	/* Call Sound notification callback of ADX */
	ret = ADXPS3_SoundNotifyCallback(arg, counter, num_samples);

	return ret;
}


/* end of file */
