//--------------------------------------------------------------------------------------
//
// X-BASIC for iOS
//	外部関数：音楽関数
//	V3.50
//--------------------------------------------------------------------------------------
// 外部関数の構造を持っている

#import "libs.h"
#include "fdef.h"
#import "support.h"
#import "Music8ch.h"
#import "FileControl2.h"
#import "NSString+substringWithRange2.h"

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

Music8ch	*music8ch;

//---------------------------------------------------------------------------------------------
// 共通関数

// 1〜のトラック番号をチェックし、0〜に変換する
int checkTrack(int trk)
{
	if (trk<MML_MIN_TRACK) trk=MML_MIN_TRACK;
	if (trk>MML_MAX_TRACK) trk=MML_MIN_TRACK;
	return (trk-MML_MIN_TRACK);
}

// 1〜のCH番号をチェックし、0〜に変換する
int checkChannel(int ch)
{
	if (ch<MML_MIN_CHANNEL) ch=MML_MIN_CHANNEL;
	if (ch>MML_MAX_CHANNEL) ch=MML_MAX_CHANNEL;
	return (ch-MML_MIN_CHANNEL);
}

// 1〜の音色番号をチェックし、0〜に変換する
int checkSound(int sno)
{
	if (sno<MML_MIN_TONE) sno=MML_MIN_TONE;
	if (sno>MML_MAX_TONE) sno=MML_MAX_TONE;
	return sno-MML_MIN_TONE;
}

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

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

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

// m_init()				初期化
static RET_FUNC _F_m_init(SWord numFAC,FAC fac[])
{
	printFunc();

	[music8ch m_init];
	
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_trk(trk,mml)			MMLをトラックに追加する
static RET_FUNC _F_m_trk(SWord numFAC,FAC fac[])
{
	printFunc2();
	
	int i=0;
	int trk	 =(int)fac[i++].dl.l[LL];
	char *mml=     fac[i++].dl.cp[LL];
	trk=checkTrack(trk);
	nslog(@"trk %d+1<-%s",trk,mml);

	int ret;
	if (*mml==0) {
		ret=-1; // mml内容がない
	} else {
		ret=0;
		[music8ch track:trk mmlSource:@(mml)];
	}
	
	fac[0].dl.l[LL]=ret;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_ftrk(trk,fname)			ファイル中のMMLをトラックに追加する
static RET_FUNC _F_m_ftrk(SWord numFAC,FAC fac[])
{
	printFunc2();
	
	int ret=0;
	int i=0;
	int trk	=(int)fac[i++].dl.l[LL];
	char *fname=bstrWithUTF8(fac[i++].dl.bp[LL]); // UTF8化
	trk=checkTrack(trk);

	nslog2(@"fname=%@",@(fname));
	if (*fname==0) {
		nslog2(@"ファイル名がNULLである");
		ret=-1;
	} else {
		NSString *path=getResourceOrSavedataPath(@(fname));
		nslog(@"trk %d+1<-%@",trk,path);
		if (path==nil) {
			nslog2(@"ファイルが見つからない");
			ret=-2;
		} else {
			FILE *fp=fopen(nsStringTocString(path),"r");
			if (fp==NULL) { // エラー
				nslog2(@"ファイルが開けられない(fopen)");
				ret=-3;
			} else {
				NSError *error;
				NSString *mml=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
				if (error!=nil) {
					nslog2(@"error=%@",error);
					ret=-4; // 何かしらのエラー
				}
				nslog2(@"mml=%@",mml);
				[music8ch track:trk mmlSource:mml];
			}
			fclose(fp);
		}
	}
	fac[0].dl.l[LL]=ret;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}


// int m_compile(trk)			MMLをコンパイルする
static RET_FUNC _F_m_compile(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int i=0;
	int trk=(int)fac[i++].dl.l[LL];
	trk=checkTrack(trk);

	fac[0].dl.l[LL]=(int)[music8ch compile:trk];
	
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// m_free(trk)				トラックワークを解放
static RET_FUNC _F_m_free(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int i=0;
	int trk=(int)fac[i++].dl.l[LL];
	trk=checkTrack(trk);
	
	[music8ch free:trk];
	
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_assign(ch,trk)		チャンネルにトラックを割り当て
static RET_FUNC _F_m_assign(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int i=0;
	int ch	 =(int)fac[i++].dl.l[LL];
	int trk	 =(int)fac[i++].dl.l[LL];

	ch =checkChannel(ch);
	trk=checkTrack(trk);

	[music8ch assign:ch trk:trk];
	
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_tone(音色番号,"音色ファイル",rate,octave,scale,floop)
static RET_FUNC _F_m_tone(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int i=0;
	int no=(int)fac[i++].dl.l[LL];
	no=checkSound(no);
	
	char *fname=fac[i++].dl.cp[LL];
	nslog2(@"音色 %d<-%s",no,fname);
	int ret;
	if (*fname==0) {
		ret=-1; // ファイル名がない
	} else {
		int rate  =(int)fac[i++].dl.l[LL];
		int octave=(int)fac[i++].dl.l[LL];
		int scale =(int)fac[i++].dl.l[LL];
		int floop =(int)fac[i++].dl.l[LL];
		ret=[music8ch tone:no path:@(fname) rate:rate octave:octave scale:scale floop:basic2cYESNO(floop)]; // 0=ok,1=ファイルが見つからない
	}
	fac[0].dl.l[LL]=ret;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}


// [c1][,c2]...[,c8]をbchに変換する
static int makeCh2Bch(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int bch=0;
	for (int j=0;j<MML_CHANNELS;j++) {
		if (fac[j].facType!=VOID_PARAM) { // 引数が省略されていない時
			int ch=(int)fac[j].dl.l[LL];
			if (ch<MML_MIN_CHANNEL) ch=MML_MIN_CHANNEL;
			if (ch>MML_MAX_CHANNEL) ch=MML_MAX_CHANNEL;
			bch|=(MUSIC_bCH0<<(ch-MML_MIN_CHANNEL));
		}
	}
	if (bch==0) bch=MUSIC_all_CH; // 全CH指定なしの時は全CHとみなす
	nslog2(@"bch=$%02x",bch);
	return bch;
}

// int m_play([c1][,c2]...[,c8])	指定チャンネルを演奏	省略時は全チャンネル
static RET_FUNC _F_m_play(SWord numFAC,FAC fac[])
{
	printFunc();
	int bch=makeCh2Bch(numFAC,fac);
	fac[0].dl.l[LL]=(int)[music8ch play:bch];
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// m_stop([c1][,c2]...[,c8])	指定チャンネルを演奏一時停止
static RET_FUNC _F_m_stop(SWord numFAC,FAC fac[])
{
	printFunc();
	int bch=makeCh2Bch(numFAC,fac);
	[music8ch pause:bch]; // BASICではstopだけど、内部的にはpause
	fac[0].dl.l[LL]=0;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// m_cont([c1][,c2]...[,c8])	チャンネルの演奏再開
static RET_FUNC _F_m_cont(SWord numFAC,FAC fac[])
{
	printFunc();
	int bch=makeCh2Bch(numFAC,fac);
	[music8ch cont:bch];
	fac[0].dl.l[LL]=0;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// m_mute([c1][,c2]...[,c8])	指定チャンネルをミュートする
static RET_FUNC _F_m_mute(SWord numFAC,FAC fac[])
{
	printFunc();
	int bch=makeCh2Bch(numFAC,fac);
	[music8ch mute:bch onoff:YES];
	fac[0].dl.l[LL]=0;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// m_muteoff([c1][,c2]...[,c8])	指定チャンネルのミュートを解除する
static RET_FUNC _F_m_muteoff(SWord numFAC,FAC fac[])
{
	printFunc();
	int bch=makeCh2Bch(numFAC,fac);
	[music8ch mute:bch onoff:NO];
	fac[0].dl.l[LL]=0;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// m_solo(ch)			指定チャンネル以外をミュートする
static RET_FUNC _F_m_solo(SWord numFAC,FAC fac[])
{
	printFunc();
	int i=0;
	int ch=(int)fac[i++].dl.l[LL];
	ch =checkChannel(ch);
	nslog2(@"ch=%d+1",ch);
	[music8ch solo:ch];
	fac[0].dl.l[LL]=0;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_stat(ch)		指定チャンネルの演奏状態
static RET_FUNC _F_m_stat(SWord numFAC,FAC fac[])
{
	printFunc();
	int i=0;
	int ch=(int)fac[i++].dl.l[LL];
	if (ch>=0) {
		// 単一ch
		ch =checkChannel(ch);
		nslog(@"ch=%d+1",ch);
	} // <0は全CH
	fac[0].dl.l[LL]=[music8ch stat:ch];
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// str m_comment(ch)		指定チャンネルの直前のコメントを得る
//	返すコメントの文字コードは、MMLを記述したそれと同じ
static RET_FUNC _F_m_comment(SWord numFAC,FAC fac[])
{
	printFunc();
	int i=0;
	int ch=(int)fac[i++].dl.l[LL];
	ch =checkChannel(ch);
	nslog(@"ch=%d+1:%@",ch,[music8ch comment:ch]);
	NSString *np=[music8ch comment:ch];
	char *p=nsStringTocString(np);
	if (strlen(p)>=operationStatus.sz_str-1) {
		// コメント長が最大文字列長より大きい時
		int j=1;
		for (int i=1;i<=[np length];i++) {
			NSString *subStr=[np substringToIndex2:i];
			p=nsStringTocString(subStr);
			if (strlen(p)>=operationStatus.sz_str-1) {
				subStr=[np substringToIndex2:j]; // 1つ前が入りきる限界
				p=nsStringTocString(subStr);
				break;
			}
			// まだ最大文字列長に収まる
			j=i; // その位置を記録
		}
	}
	fac[0].dl.cp[LL]=p;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_count(ch)		指定チャンネルの発声数を得る
static RET_FUNC _F_m_count(SWord numFAC,FAC fac[])
{
	printFunc();
	int i=0;
	int ch=(int)fac[i++].dl.l[LL];
	ch =checkChannel(ch);
	nslog(@"ch=%d+1",ch);
	fac[0].dl.l[LL]=[music8ch count:ch];
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_pan(ch,pan)		指定チャンネルのパンポットを指定する/取得する
static RET_FUNC _F_m_pan(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int i=0;
	int ch	 =(int)fac[i++].dl.l[LL];
	int pan  =(int)fac[i++].dl.l[LL];
	ch =checkChannel(ch);
	nslog(@"ch=%d+1/pan=%d",ch,pan);

	int ret=0;
	if (pan<0) { // 取得
		ret=[music8ch panpot:ch];
	} else { // 設定
		[music8ch panpot:ch panpot:pan];
	}
	fac[0].dl.l[LL]=ret;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int m_vol(ch,vol)		指定チャンネルの音量を指定する/取得する
static RET_FUNC _F_m_vol(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int i=0;
	int ch	 =(int)fac[i++].dl.l[LL];
	int vol  =(int)fac[i++].dl.l[LL];
	ch =checkChannel(ch);
	nslog(@"ch+1=%d/vol=%d",ch,vol);
	
	int ret=0;
	if (vol<0) { // 取得
		ret=[music8ch volume:ch];
	} else { // 設定
		[music8ch volume:ch volume:vol];
	}
	fac[0].dl.l[LL]=ret;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// m_tempo(te)			全体のテンポ設定
static RET_FUNC _F_m_tempo(SWord numFAC,FAC fac[])
{
	printFunc();
	
	int i=0;
	int te	 =(int)fac[i++].dl.l[LL];
	nslog2(@"テンポ=%d",te);
	
	music8ch.tempo=te;
	
	int ret=0;
	fac[0].dl.l[LL]=ret;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

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

// 関数名テーブル(トークンテーブル)
static const char *iOSmusicTokens[]={
	"m_init",
	"m_trk",
	"m_ftrk",
	"m_compile",
	"m_free",
	"m_tone",
	"m_assign",
	"m_play",
	"m_stop",
	"m_cont",
	"m_mute",
	"m_muteoff",
	"m_solo",
	"m_stat",
	"m_comment",
	"m_count",
	"m_pan",
	"m_vol",
	"m_tempo"
	// バージョン
	"versionXBiOSMusic$",
	// end of table
	NULL,
};
				
// 実行アドレステーブル
static FUNC_EXEC iOSmusicFuncExec[]={
	// Cの標準関数名と重複させないよう_F_を付ける
	{_F_m_init},
	{_F_m_trk},
	{_F_m_ftrk},
	{_F_m_compile},
	{_F_m_free},
	{_F_m_tone},
	{_F_m_assign},
	{_F_m_play},
	{_F_m_stop},
	{_F_m_cont},
	{_F_m_mute},
	{_F_m_muteoff},
	{_F_m_solo},
	{_F_m_stat},
	{_F_m_comment},
	{_F_m_count},
	{_F_m_pan},
	{_F_m_vol},
	{_F_m_tempo},
	// バージョン
	{_F_version},
	// end of table
	{NULL},
};

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

static const uwPARAMTYPE p_m_init[]={
	INT_RET,
};

static const uwPARAMTYPE p_m_trk[]={
	INT_VAL,	// trk;int
	STR_VAL,	// mml;str
	INT_RET,
};
#define p_m_ftrk	p_m_trk

static const uwPARAMTYPE p_m_compile[]={
	INT_VAL,	// trk;int
	INT_RET,
};

static const uwPARAMTYPE p_m_free[]={
	INT_VAL,	// trk;int
	INT_RET,
};

static const uwPARAMTYPE p_m_tone[]={
	INT_VAL,	// tno
	STR_VAL,	// fname
	INT_VAL,	// rate
	INT_VAL,	// octave
	INT_VAL,	// scale
	INT_VAL,	// floop
	INT_RET,
};

static const uwPARAMTYPE p_m_assign[]={
	INT_VAL,	// ch;int
	INT_VAL,	// trk;no
	INT_RET,
};

static const uwPARAMTYPE p_m_play[]={
	INT_OMT,	// ch1;int
	INT_OMT,	// ch2;int
	INT_OMT,	// ch3;int
	INT_OMT,	// ch4;int
	INT_OMT,	// ch5;int
	INT_OMT,	// ch6;int
	INT_OMT,	// ch7;int
	INT_OMT,	// ch8;int
	INT_RET,
};
#define	p_m_stop	p_m_play
#define	p_m_cont	p_m_play
#define	p_m_mute	p_m_play
#define	p_m_muteoff	p_m_play

static const uwPARAMTYPE p_m_solo[]={
	INT_VAL,	// ch;int
	INT_RET,
};
#define	p_m_stat	p_m_solo
#define p_m_count	p_m_solo

static const uwPARAMTYPE p_m_comment[]={
	INT_VAL,	// ch;int
	STR_RET,
};

static const uwPARAMTYPE p_m_pan[]={
	INT_VAL,	// ch;int
	INT_VAL,	// pan;int
	INT_RET,
};
#define p_m_vol	p_m_pan

static const uwPARAMTYPE p_m_tempo[]={
	INT_VAL,	// te;int
	INT_RET,
};

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

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

static
const
uwPARAMTYPE *iOSmusicParams[]={
	p_m_init,
	p_m_trk,
	p_m_ftrk,
	p_m_compile,
	p_m_free,
	p_m_tone,
	p_m_assign,
	p_m_play,
	p_m_stop,
	p_m_cont,
	p_m_mute,
	p_m_muteoff,
	p_m_solo,
	p_m_stat,
	p_m_comment,
	p_m_count,
	p_m_pan,
	p_m_vol,
	p_m_tempo,
	// バージョン
	p_version	,
	// end of table
	NULL,
};

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

// この中の関数の初期化処理
static void _Frun(void)
{
	printFunc();
	music8ch=[[Music8ch alloc]init];
}

// この中の関数の終了処理
static void _Fend(void)
{
	printFunc();
	[music8ch terminate]; // 何故か=nilでdeallocが呼び出されないので強制的に呼び出す
	music8ch =nil;	// deallocが走る
}

// インフォメーションテーブル
const
INFORMATION_TABLE	InfoMusicFuncs={
	#ifdef FUNC_DEBUG
	"iOS X68 Music functions",
	#endif
	returnOnly,	// Finit
	_Frun,		// Frun
	_Fend,		// Fend
	_Fend,		// Fexit
	_Fend,		// Fbreak
	NULL,		// FctrlD;未使用
	NULL,		// 予約1
	NULL,		// 予約2
	//
	iOSmusicTokens,
	iOSmusicParams,
	iOSmusicFuncExec
};

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