//--------------------------------------------------------------------------------------
//
// X-BASIC'
//	外部関数：iOS用 Sound
//
//--------------------------------------------------------------------------------------
// 外部関数の構造を持っている

#import "RunViewController.h"
#import "soundPlay.h"
#import "libs.h"
#import "FileControl2.h"
#include "fdef.h"
#include "support.h"
#include "XBiOSsound.h"
#import "AudioPlay.h"
#import "adpcm.h"
#import "wavMusic.h"

#define MAX_AUDIO	64	// a_setPlayData()で同時に設定しておけるデータ数

//---------------------------------------------------------------------------------------------
// 共通ワーク

// これらは内部で設定
static SoundPlay *beepPlay;
static SoundPlay *beepPlay2;
static int beep2No;
static AudioPlay *audioPlay[MAX_AUDIO]; // 空きはnilで判定する;これはポインタだけで、クラスの実体は_F_a_setPlayData()で確保される

//---------------------------------------------------------------------------------------------

static RET_FUNC _F_version(SWord numFAC,FAC fac[])
// この外部関数のバージョンを返す
{
	printFunc();
	fac[0].dl.cp[LL]=VERSION_XBIOSFUNCS;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

//---------------------------------------------------------------------------------------------

// beep()
RET_FUNC _F_beep(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	[beepPlay play];
	//
	fac[0].dl.l[LL]=0;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// beep2(no,floadOnly)
static RET_FUNC _F_beep2(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int i=0;
	int no=1;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		no=(int)fac[i].dl.l[LL];
		if (no>=1000) {
			nslog(@"システム内蔵音を鳴らす:no=%d",no);
			[SoundPlay play:no];
			goto beep_end;
		}
		if (no<0||no>13) no=1;
	}
	i++;
	BOOL floadOnly=NO;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		floadOnly=basic2cYESNO(fac[i].dl.l[LL]);
	}
	i++;
	nslog(@"beep2:no=%d,freadonly=%d",no,floadOnly);
	if (no!=beep2No) {
		// 前のデータを解放する
		@autoreleasepool {
			NSString *path;
			if (no==0) {
				path=@"click";
			} else {
				path=[NSString stringWithFormat:@"beep%d",no];
			}
			// SoundPlayは自前でリソースディレクトリを検索するので、ファイル名のみ与えれば良い
			nslog(@"beepPlay2 path=%@",path);
			beepPlay2=[[SoundPlay alloc]initWithFile:path resourceType:@"aiff"];
			nslog(@"beepPlay2=%@",beepPlay2);
			beep2No=no; // 読み込み済みの番号を保存
		}
	} // 前と同じ時は再読み込みしない；読込するとsoundIDが増えてしまうので、できるだ再読み込みを避けるようにする
	if (!floadOnly) {
		[beepPlay2 play];
	}
beep_end:;
	fac[0].dl.l[LL]=0;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

//---------------------------------------------------------------------------------------------

// int a_setPlayData(char *fname[,rateNo;int])
static RET_FUNC _F_a_setPlayData(SWord numFAC,FAC fac[])
{
	printFunc();
	
	UByte *bp=fac[0].dl.bp[LL];
	if (*bp==0) {
		nslog(@"ファイル名がNULLである");
		fac[0].dl.l[LL]=-2; // ファイルが見つかるはずがない
		fac[0].facType=FACTYPE_RETURN;
		return (RET_FUNC_OK);
	}
	
	@autoreleasepool {
		NSString *path=@(bstrWithUTF8(bp)); // UTF8化
		int multiple;	// 音声倍率
		int rate=15600; // ADPCMの省略値
		int rateNo=checkSoundType(path,&rate,&multiple);
		switch (rateNo) {
			case FILE_ADPCM:
				if (fac[1].facType!=VOID_PARAM) { // 引数が省略されていない時
					rate=adpcmRateNo2Rate((int)fac[1].dl.l[LL]);
				}
				break;
		}
		int i;
		for (i=0;i<MAX_AUDIO;i++) { // 空きを探して登録
			if (audioPlay[i]==nil) { // 空きがあった
				switch (rateNo) {
					case NO_ADPCM:
						// iOS直接サポート分
						audioPlay[i]=[[AudioPlay alloc]initWithFile:path]; // ここがメモリリークを起こしている
						break;
					case FILE_P16:
					case FILE_M30:
					case FILE_M44:
						audioPlay[i]=[[AudioPlay alloc]initWithPCMFile:path rate:rate multiple:multiple];
						break;
					default: // ADPCMのサンプリングレートはいろいろ
						audioPlay[i]=[[AudioPlay alloc]initWithADPCMFile:path rate:rate];
						break;
				}
				if (audioPlay[i]==nil) {
					nslog(@"ファイルが見つからない");
					fac[0].dl.l[LL]=-2; // ファイルが見つからない
				} else {
					fac[0].dl.l[LL]=i; // 登録番号
				}
				break;
			}
		}
		if (i==MAX_AUDIO) {
			fac[0].dl.l[LL]=-1; // 空きがない印
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

//---------------------------------------------------------------------------------------------
// 以下の関数群では、noが範囲外の時は何もしないだけ

// int a_play(no;int)
RET_FUNC _F_a_play(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			[audioPlay[no] operation:AUDIOPLAY_PLAY];
			fac[0].dl.l[LL]=no; // 再生した番号
		}
	} // 範囲外の時は何もしない
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int a_end(no;int)
static RET_FUNC _F_a_end(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			[audioPlay[no] operation:AUDIOPLAY_PAUSE];
			audioPlay[no]=nil; // 開放した印
			fac[0].dl.l[LL]=no; // 解放した番号
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// a_stop(no;int)
static RET_FUNC _F_a_stop(SWord numFAC,FAC fac[])
{
	printFunc();
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			[audioPlay[no] operation:AUDIOPLAY_PAUSE];
			fac[0].dl.l[LL]=no; // バッファー番号
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// a_cont(no;int)
static RET_FUNC _F_a_cont(SWord numFAC,FAC fac[])
{
	printFunc();
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			[audioPlay[no] operation:AUDIOPLAY_PLAY];
			fac[0].dl.l[LL]=no; // バッファー番号
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// a_volume(no;int,level;float)
// level=0.0~1.0
static RET_FUNC _F_a_volume(SWord numFAC,FAC fac[])
{
	printFunc();
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			[audioPlay[no] operation:AUDIOPLAY_VOLUME doubleArg:fac[1].dl.d];
			fac[0].dl.l[LL]=no; // バッファー番号
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// a_speed(no;int,speed;float)
// speed=0.5~2.0 : 0.5=半分速 , 1=等速 , 2=倍速
static RET_FUNC _F_a_speed(SWord numFAC,FAC fac[])
{
	printFunc();
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			[audioPlay[no] operation:AUDIOPLAY_SPEED doubleArg:fac[1].dl.d];
			fac[0].dl.l[LL]=no; // バッファー番号
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// a_pan(no;int,pan;float)
// pan=-1.0:左 , 0.0=中央 , 1.0=右
static RET_FUNC _F_a_pan(SWord numFAC,FAC fac[])
{
	printFunc();
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			[audioPlay[no] operation:AUDIOPLAY_PAN doubleArg:fac[1].dl.d];
			fac[0].dl.l[LL]=no; // バッファー番号
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// a_loop(no;int,loopcnt;int)
// loopcnt : 1~=n回再生 , 0=無限リピート until a_stop
static RET_FUNC _F_a_loop(SWord numFAC,FAC fac[])
{
	printFunc();
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=-1; // 指定音声登録がない印
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			int loopcnt=(int)fac[1].dl.l[LL]-1;
			[audioPlay[no] operation:AUDIOPLAY_LOOP intArg:loopcnt];
			fac[0].dl.l[LL]=no; // バッファー番号
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int a_stat(no;int)
RET_FUNC _F_a_stat(SWord numFAC,FAC fac[])
{
	printFunc();
	int no=(int)fac[0].dl.l[LL];
	fac[0].dl.l[LL]=0; // 未設定の時は0=停止しているを返す(V2.5)
	if (no>=0 && no<MAX_AUDIO) {
		if (audioPlay[no]!=nil) {
			fac[0].dl.l[LL]=([audioPlay[no] audioplayStat]? 2:0);
			// 再生中なら2 , 停止しているなら0 : X-BASIC/68の仕様
		}
	}
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

//---------------------------------------------------------------------------------------------
// インフォメーションテーブル (5.2.1章)
//---------------------------------------------------------------------------------------------

// 関数名テーブル(トークンテーブル)
static const char *iOSSTokens[]={
	"beep",
	"beep2",
	"a_setPlayData",
	"a_play",
	"a_end",
	"a_stop",
	"a_cont",
	"a_volume",
	"a_speed",
	"a_pan",
	"a_loop",
	"a_stat",
	// バージョン
	"versionXBiOSSound$",
	// end of table
	NULL,
};

// 実行アドレステーブル
static FUNC_EXEC iOSSFuncExec[]={
	// Cの標準関数名と重複させないよう_F_を付ける
	{_F_beep	},
	{_F_beep2	},
	{_F_a_setPlayData},
	{_F_a_play	},
	{_F_a_end	},
	{_F_a_stop	},
	{_F_a_cont	},
	{_F_a_volume},
	{_F_a_speed	},
	{_F_a_pan	},
	{_F_a_loop	},
	{_F_a_stat	},
	// バージョン
	{_F_version},
	// end of table
	{NULL},
};

//------------------------------
// パラメーターテーブル
// (1)種類別定義
// 1関数の引数は最大MAX_PARAM個

static const uwPARAMTYPE p_beep[]={
	VOID_RET,
};

static const uwPARAMTYPE p_beep2[]={
	INT_OMT,
	INT_OMT,
	VOID_RET,
};

static const uwPARAMTYPE p_a_setPlayData[]={
	STR_VAL,
	INT_OMT,
	INT_RET,
};

static const uwPARAMTYPE p_a_play[]={
	INT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_end[]={
	INT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_stop[]={
	INT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_cont[]={
	INT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_volume[]={
	INT_VAL,
	FLOAT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_speed[]={
	INT_VAL,
	FLOAT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_pan[]={
	INT_VAL,
	FLOAT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_loop[]={
	INT_VAL,
	INT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_a_stat[]={
	INT_VAL,
	INT_RET,
};

static const uwPARAMTYPE p_version[]={
	STR_RET,
};

//------------------------------
// パラメーターテーブル
// (2)関数別定義

static
const
uwPARAMTYPE *iOSSParams[]={
	p_beep		,
	p_beep2		,
	p_a_setPlayData	,
	p_a_play	,
	p_a_end		,
	p_a_stop	,
	p_a_cont	,
	p_a_volume	,
	p_a_speed	,
	p_a_pan		,
	p_a_loop	,
	p_a_stat	,
	// バージョン
	p_version	,
	// end of table
	NULL,
};

//-------------

// この中の関数の初期化処理
static void _Frun(void)
{
	printFunc();
	beepPlay =[[SoundPlay alloc]initWithFile:@"beep1" resourceType:@"aiff"];
	// とりあえず、一番使いそうなものを入れておく
	beep2No=0; // 0=click
	beepPlay2=[[SoundPlay alloc]initWithFile:@"click" resourceType:@"aiff"];
	int i;
	for (i=0;i<MAX_AUDIO;i++) {
		audioPlay[i]=nil;
	}
}

// この中の関数の終了処理
static void _Fend(void)
{
	printFunc();
	int i;
	for (i=0;i<MAX_AUDIO;i++) {
		if (audioPlay[i]!=nil) {
			nslog(@"audio %d",i);
			[audioPlay[i] operation:AUDIOPLAY_PAUSE];
			audioPlay[i]=nil;
		}
	}
	// 開放
	beepPlay =nil;
	beepPlay2=nil;
}

// インフォメーションテーブル
const
INFORMATION_TABLE	InfoIosSFuncs={
	#ifdef FUNC_DEBUG
	"iOS Sound functions",
	#endif
	returnOnly,	// Finit
	_Frun,		// Frun
	_Fend,		// Fend
	returnOnly,	// Fexit
	returnOnly,	// Fbreak
	NULL,		// FctrlD;未使用
	NULL,		// 予約1
	NULL,		// 予約2
	//
	iOSSTokens,
	iOSSParams,
	iOSSFuncExec
};

//---------------------------------------------------------------------------------------------
