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

#import	"RunViewController.h"
#import "libs.h"
#import "ScreenCapture.h"
#include "XB.h"
#include "fdef.h"
#include "support.h"
#import "AirPrint.h"
#import "GeneratePdf.h"

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

static kConsole *kconsole;	// これは内部で設定
NSTimeInterval	repeat1st;	// リピート開始までの時間	TM_REPEAT1ST(tm)	0=リピートしない
NSTimeInterval	repeat;		// リピート間隔			TM_REPEAT1(tm)
UByte tempBuff[10]; 		// 本当はunicode１文字の最大バイト数分で良い

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

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

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

RET_FUNC _F_kBackgroundAlpha(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	[kconsole alpha:fac[0].dl.d];
	//
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

//---------------------------------------------------------------------------------------------
// キー入力
//---------------------------------------------------------------------------------------------

// 文字コードのまま取得
// inkey([mode;int])	modeは/68互換のため(未使用)
RET_FUNC _F_inkey(SWord numFAC,FAC fac[])
{
	printFunc();
	fac[0].dl.l[LL]=[kconsole inkey];
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// 文字列化して取得
// inkey$([mode;int])	modeは/68互換のため(未使用)
static RET_FUNC _F_inkeyS(SWord numFAC,FAC fac[])
{
	printFunc();
	unichar key=[kconsole inkey];
	sprintf((char *)tempBuff,"%c",key);
	fac[0].dl.bp[LL]=tempBuff;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

static RET_FUNC _F_kbhit(SWord numFAC,FAC fac[])
{
	printFunc();
	fac[0].dl.l[LL]=c2basicYESNO([kconsole kbhit]);
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

RET_FUNC _F_kbclr(SWord numFAC,FAC fac[])
{
	printFunc();
	[kconsole clear];
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

//---------------------------------------------------------------------------------------------
// タッチ
//---------------------------------------------------------------------------------------------

RET_FUNC _F_keyRepeatTime(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int i=0;
	double r1=fac[i++].dl.d; // 正確にはNSTimeInterval
	double r2=fac[i++].dl.d; // NSTimeInterval=doubleである
	// デフォルトはr1=3,r2=2
	if (r1<0.0) {
		// リピート無し
		repeat1st=0.0;
	} else {
		repeat1st=TM_REPEAT1ST(r1);	// 200+tm*100 ms
	}
	if (r2<0.0) {
		// 待ちなしで即リピート
		repeat=0.0;
	} else {
		repeat=TM_REPEAT(r2);	// 30+5*tm    ms
	}
	nslog(@"repeat1st=%f(%f)/repeat=%f(%f)",repeat1st,r1,repeat,r2);
	//
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

RET_FUNC _F_setTouchArea(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int i=0;
	CGFloat sx=fac[i++].dl.d;
	CGFloat sy=fac[i++].dl.d;
	CGFloat wx=fac[i++].dl.d;
	CGFloat wy=fac[i++].dl.d;
	nslog(@"sx=%f,sy=%f,wx=%f,wy=%f",sx,sy,wx,wy);
	int	keyCode   =(int)fac[i++].dl.l[LL];
	int	fBeganOnly=basic2cYESNO(fac[i++].dl.l[LL]);
	//
	UIColor *backColor=[UIColor clearColor];
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		int r,g,b,alpha;
		getRGBColorFromFac(fac,i,&r,&g,&b,&alpha); // v2.2
		backColor=[UIColor colorWithRed:_cf(r) green:_cf(g) blue:_cf(b) alpha:_cf(alpha)];
	}
	CGRect area={{sx,sy},{wx,wy}};
	fac[0].dl.l[LL]=[kconsole setTouchArea:area key:keyCode fBeganOnly:fBeganOnly repeat1st:repeat1st repeat:repeat backColor:backColor];
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

static RET_FUNC _F_setTouchAreaWithText(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int i=0;
	CGFloat sx=(CGFloat)fac[i++].dl.l[LL];
	CGFloat sy=(CGFloat)fac[i++].dl.l[LL];
	CGFloat wx=(CGFloat)fac[i++].dl.l[LL];
	CGFloat wy=(CGFloat)fac[i++].dl.l[LL];
	nslog(@"sx=%f,sy=%f,wx=%f,wy=%f",sx,sy,wx,wy);
	int	keyCode   =(int)fac[i++].dl.l[LL];
	int	fBeganOnly=basic2cYESNO(fac[i++].dl.l[LL]);
	//
	UIColor *backColor=[UIColor clearColor];
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		int r,g,b,alpha;
		getRGBColorFromFac(fac,i,&r,&g,&b,&alpha); // v2.2
		backColor=[UIColor colorWithRed:_cf(r) green:_cf(g) blue:_cf(b) alpha:_cf(alpha)];
	}
	CGRect area={{sx,sy},{wx,wy}};
	fac[0].dl.l[LL]=[kconsole setTouchAreaWithText:area key:keyCode fBeganOnly:fBeganOnly repeat1st:repeat1st repeat:repeat backColor:backColor];
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

RET_FUNC _F_removeTouchArea(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int	no=(int)fac[0].dl.l[LL];
	nslog(@"no=%d",no);
	fac[0].dl.l[LL]=[kconsole removeTouchArea:no];
	//
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

// int getTouchPoint(xys();float[,mode;int])
static RET_FUNC _F_getTouchPoint(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int mode=0;
	if (fac[1].facType!=VOID_PARAM) { // 引数が省略されていない時
		mode=(int)fac[1].dl.l[LL];
	}
	
	SWord size1;
	uwYouso youso;
	
	DOUBLE_LONG *xys=(DOUBLE_LONG *)getDim1FromFAC(fac,0,&size1,&youso); // fac[0]から
	// float配列は直接float*で読みだしてはいけない
	if (xys==NULL) { // 1次元でない
		return (RET_FUNC_ERR);
	}
	CGPoint points[MAX_TPOINT];
	int cnt=[kconsole getTouchPoint:points mode:mode]; // cntはx,y座標での組数
	int i=0,k=0;
	while (cnt-->0) {
		nslog(@"xys=%p:(%f,%f)",xys,points[i].x,points[i].y);
#if AARM>=1
		storeDoubleToDOUBLE_LONGPointer(xys, points[i].x);
//		nslog(@"%f",xys->d);	// xysは境界整合していないので、このアクセスは出来ない
#else
		xys->d=points[i].x;
#endif
		if (++k>=youso) {
			// x座標しか入らないときはyも消してしまう
			break;
		}
		xys++;
		//
#if AARM>=1
		storeDoubleToDOUBLE_LONGPointer(xys, points[i].y);
//		nslog(@"%f",xys->d);	// xysは境界整合していないので、このアクセスは出来ない
#else
		xys->d=points[i].y;
#endif
		if (++k>=youso) {
			// 次の座標は入らないのでここで終了
			break;
		}
		xys++;
		//
		i++; // 次の座標へ
	}
	fac[0].dl.l[LL]=k/2; // 使った要素数;dim(0〜k/2-1)に入っている。2つ1組
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}
// 座標の順序は不定
// 配列のほうが小さい時は入るだけ
// 最大10(MAX_TOUCH)組

// int getTouchStatus()
static RET_FUNC _F_getTouchStatus(SWord numFAC,FAC fac[])
{
	printFunc();
	fac[0].dl.l[LL]=[kconsole getTouchStatus];
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

//---------------------------------------------------------------------------------------------
// ファンクションキー
//---------------------------------------------------------------------------------------------

RET_FUNC _F_setFunctionKey(SWord numFAC,FAC fac[])
{
	printFunc();
	@autoreleasepool {
		int i=0;
		int	no=(int)fac[i++].dl.l[LL];
		nslog(@"no=%d:%s",no,fac[i].dl.bp[LL]);
		UByte *title=strWithUTF8(fac[i++].dl.bp[LL]); // UTF8化
		nslog(@"title=%s",title);
		unichar	keyCode=fac[i++].dl.l[LL];
//		NSString *nsTitle=uCstringToNSString(title);
//		[kconsole setFunctionKey:no title:nsTitle keyCode:keyCode];
		[kconsole setFunctionKey:no title:uCstringToNSString(title) keyCode:keyCode];
	}
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

RET_FUNC _F_displayFunctionKey(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int i=0;
	int	onoff=basic2cYESNO(fac[i++].dl.l[LL]);

	int st=0;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		st=(int)fac[i].dl.l[LL];
	} // OFFの時は設定無意味(常に全部消すから)
	i++;
	
	int ed=-1; // 全部の印
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		ed=(int)fac[i].dl.l[LL];
	} // OFFの時は設定無意味(常に全部消すから)
	i++;
	
	BOOL f2danKey=NO;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		f2danKey=basic2cYESNO(fac[i].dl.l[LL]);
	}
	i++;
	
	// v3.20
	int clearance=0;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		clearance=fac[i].dl.l[LL];
		if (clearance<0) clearance=0;
		nslog(@"clearance=%d",clearance);
	}
	i++;
	
	[kconsole displayFunctionKey:onoff st:st ed:ed f2danKey:f2danKey clearance:(CGFloat)clearance];
	//
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

static RET_FUNC _F_displayFunctionKeyAll(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int i=0;
	int	onoff=basic2cYESNO(fac[i++].dl.l[LL]);

	BOOL f2danKey=NO;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		f2danKey=basic2cYESNO(fac[i].dl.l[LL]);
	}
	i++;
	
	BOOL fextend=NO;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		fextend=basic2cYESNO(fac[i].dl.l[LL]);
	}
	i++;
	
	// v3.20
	int clearance=0;
	if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
		clearance=fac[i].dl.l[LL];
		if (clearance<0) clearance=0;
		nslog(@"clearance=%d",clearance);
	}
	i++;
	
	[kconsole displayFunctionKey:onoff f2danKey:f2danKey fextend:fextend clearance:clearance];
	//
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

#if 0	// v3.5でも封印
static RET_FUNC _F_setFunctionKeyAtrb(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	int i=0;
	//
	fac[0].dl.l[LL]=0; // 念のため
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}
#endif

static RET_FUNC _F_setFunctionKeyBackgroundImage(SWord numFAC,FAC fac[])
{
	printFunc();
	@autoreleasepool {
		int i=0;
		char *fname="funcBack.png"; // 標準背景
		if (fac[i].facType!=VOID_PARAM) { // 引数が省略されていない時
			fname=bstrWithUTF8(fac[i].dl.bp[LL]); // UTF8化
		}
		i++;
		
		[kconsole setFunctionKeyBackgroundImage:uCstringToNSString(fname)];
		//
		fac[0].dl.l[LL]=0; // 念のため
		fac[0].facType=FACTYPE_RETURN;
		return (RET_FUNC_OK);
	} // OK
}

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

// hcopy([fname;str[,quality;float]])
// 拡張子で出力データ変更
// pdf/jpg/png/アルバム出力に対応
static RET_FUNC _F_hcopy(SWord numFAC,FAC fac[])
{
	printFunc();
	//
	RunViewController *runView=xb_getRunView();
	runView.capturedImageView.image=viewCapture(kconsole,NO); // キー画面キャプチャー
	runView.capturedImageView.hidden=NO;  // それを表示
	UIImage *image=viewCapture(runView.captureView,YES); // 印刷するためには背景を黒にしておく必要がある
	runView.capturedImageView.hidden=YES; // キー画面を消去
	
	if (fac[0].facType!=VOID_PARAM) { // ファイル名が設定されている時
		// ファイル名指定があるときはファイル出力
		UByte *fname=fac[0].dl.bp[LL];
		if (*fname==0) {
			nslog(@"ファイル名がNULLである");
			fac[0].dl.l[LL]=PIC_FILENOTFOUND; // ファイルが見つかるはずがない
			goto hcopy_end;
		}

		NSString *path=makeFullPathForSaveData(@(bstrWithUTF8(fname)));
		NSString *ext=[path pathExtension];
		nslog(@"出力ファイル名=%@(拡張子:%@)",path,ext);
		if (strcasecmp((char*)fname,".ALBUM")==0 // .ALBUMと記述したときはこちらに一致する
		 || [ext caseInsensitiveCompare:@"ALBUM"]==NSOrderedSame) {
			nslog(@"アルバムへ出力");
			// 面倒なのでエラーチェックは省く
			UIImageWriteToSavedPhotosAlbum(image, nil, (SEL)0, NULL);
		}
		else
		if ([ext caseInsensitiveCompare:@"JPG"]==NSOrderedSame) {
			CGFloat quality=1.0; // 圧縮率
			if (fac[1].facType!=VOID_PARAM) { // 引数が省略されていない時
				quality=fac[1].dl.d;
			}
			// ファイルへコピー
			nslog(@"JPEGファイルへ出力");
			NSData *imgdata=UIImageJPEGRepresentation(image,quality);
			[imgdata writeToFile:path atomically:YES];
		}
		else
		if ([ext caseInsensitiveCompare:@"PNG"]==NSOrderedSame) {
			// ファイルへコピー
			nslog(@"PNGファイルへ出力");
			NSData *imgdata=UIImagePNGRepresentation(image);
			[imgdata writeToFile:path atomically:YES];
		}
		else {
			// PDFまたはどれでもなかった時
			GeneratePdf *pdf=[[GeneratePdf alloc]init];
			if ([ext caseInsensitiveCompare:@"PDF"]!=NSOrderedSame) {
				// どれでもなかった時
				// 強制的に拡張子.pdfを付けて出力する
				[pdf savePDFFileWithImage:image fname:[NSString stringWithFormat:@"%@.pdf",path]];
			} else {
				// PDF
				[pdf savePDFFileWithImage:image fname:path];
			}
		}
	} else {
		// ファイル名指定がないときはAirPrint出力
		nslog(@"AirPrint出力");
		// 印刷はメインスレッドで行わなければならないので渡す
		[runView printImage:image];
	}
	//
	fac[0].dl.l[LL]=PIC_OK; // 正常終了
hcopy_end:;
	fac[0].facType=FACTYPE_RETURN;
	return (RET_FUNC_OK); // OK
}

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

// 関数名テーブル(トークンテーブル)
static const char *iOSKTokens[]={
	"inkey",
	"inkey$",
	"kbhit",
	"keysns",
	"kbclr",
	"setTouchArea",
	"setTouchAreaWithText",
	"removeTouchArea",
	"keyRepeatTime",
	"getTouchStatus",
	//
	"setFunctionKey",
	"displayFunctionKey",
	"displayFunctionKeyAll",
//	"setFunctionKeyAtrb",
	"setFunctionKeyBackgroundImage",
	"kBackgroundAlpha",
	"getTouchPoint",
	"hcopy",
	// バージョン
	"versionXBiOSKey$",
	// end of table
	NULL,
};

// 実行アドレステーブル
static FUNC_EXEC iOSKFuncExec[]={
	// Cの標準関数名と重複させないよう_F_を付ける
	{_F_inkey	},
	{_F_inkeyS	},
	{_F_kbhit	},
	{_F_kbhit	},
	{_F_kbclr	},
	{_F_setTouchArea		},
	{_F_setTouchAreaWithText},
	{_F_removeTouchArea		},
	{_F_keyRepeatTime		},
	{_F_getTouchStatus		},
	//
	{_F_setFunctionKey		},
	{_F_displayFunctionKey	},
	{_F_displayFunctionKeyAll},
//	{_F_setFunctionKeyAtrb	},
	{_F_setFunctionKeyBackgroundImage},
	{_F_kBackgroundAlpha},
	{_F_getTouchPoint},
	{_F_hcopy},
	// バージョン
	{_F_version},
	// end of table
	{NULL},
};

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

static const uwPARAMTYPE p_inkey[]={
	INT_OMT,	// /68互換のため1引数を許す。ただし未使用	v3.20
	INT_RET,
};

static const uwPARAMTYPE p_inkeyS[]={
	INT_OMT,	// /68互換のため1引数を許す。ただし未使用	v3.20
	STR_RET,
};

static const uwPARAMTYPE p_kbhit[]={
	INT_RET,
};
#define p_keysns			p_kbhit
#define p_getTouchStatus	p_kbhit

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

static const uwPARAMTYPE p_setTouchArea[]={
	FLOAT_VAL,	// sx
	FLOAT_VAL,	// sy
	FLOAT_VAL,	// wx
	FLOAT_VAL,	// wy
	INT_VAL,	// keyCode
	INT_VAL,	// fBeganOnly
	// 色
	INT_OMT,	// r
	INT_OMT,	// g
	INT_OMT,	// b
	INT_OMT,	// alpha
	//
	INT_RET,
};

static const uwPARAMTYPE p_setTouchAreaWithText[]={
	INT_VAL,	// sx
	INT_VAL,	// sy
	INT_VAL,	// wx
	INT_VAL,	// wy
	INT_VAL,	// keyCode
	INT_VAL,	// fBeganOnly
	// 色
	INT_OMT,	// r
	INT_OMT,	// g
	INT_OMT,	// b
	INT_OMT,	// alpha
	INT_RET,
};

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

static const uwPARAMTYPE p_setFunctionKey[]={
	INT_VAL,
	STR_VAL,
	INT_VAL,
	VOID_RET,
};

static const uwPARAMTYPE p_displayFunctionKey[]={
	INT_VAL,
	INT_OMT,	// v2.1
	INT_OMT,	// v2.1
	INT_OMT,
	INT_OMT,	// v3.20
	VOID_RET,
};

static const uwPARAMTYPE p_displayFunctionKeyAll[]={
	INT_VAL,
	INT_OMT,
	INT_OMT,	// v2.1
	INT_OMT,	// v3.20
	VOID_RET,
};

static const uwPARAMTYPE p_setFunctionKeyBackgroundImage[]={
	STR_OMT,	// v3.2で省略可能化
	VOID_RET,
};

static const uwPARAMTYPE p_keyRepeatTime[]={
	FLOAT_VAL,
	FLOAT_VAL,
	VOID_RET,
};

static const uwPARAMTYPE p_kBackgroundAlpha[]={
	FLOAT_VAL,
	VOID_RET,
};

static const uwPARAMTYPE p_getTouchPoint[]={
	ARRAY1_FLOAT,
	INT_OMT,	// v2.5
	INT_RET,
};

static const uwPARAMTYPE p_hcopy[]={
	STR_OMT,
	FLOAT_OMT,
	INT_RET,	// v2.2
};

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

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

static
const
uwPARAMTYPE *iOSKParams[]={
	p_inkey,
	p_inkeyS,
	p_kbhit,
	p_keysns,
	p_kbclr,
	p_setTouchArea,
	p_setTouchAreaWithText,
	p_removeTouchArea,
	p_keyRepeatTime,
	p_getTouchStatus,
	//
	p_setFunctionKey,
	p_displayFunctionKey,
	p_displayFunctionKeyAll,
	p_setFunctionKeyBackgroundImage,
	p_kBackgroundAlpha,
	p_getTouchPoint,
	p_hcopy,
	// バージョン
	p_version	,
	// end of table
	NULL,
};

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

// この中の関数の初期化処理
static void _Frun(void)
{
	printFunc();
	// 一番最初の初期化はinit2に任せる
	kconsole=[xb_getRunView() getConsole:KCONSOLE];
	[kconsole clear]; // kbclr()
	[kconsole alpha:1.0];
	// デフォルトキーリピート時間
	repeat1st=TM_REPEAT1ST(DEF_REPEAT1ST);
	repeat   =TM_REPEAT(DEF_REPEAT);
}

// この中の関数の終了処理
static void _Fend(void)
{
	printFunc();
	xb_getRunView().capturedImageView.image=viewCapture(kconsole,NO); // キー画面キャプチャー
	//
	[kconsole resetTouchArea];
	[kconsole resetAllFunctionKey]; // これでファンクションキーを消す。そうでないとボタンが有効なまま残ってしまうので
}

// インフォメーションテーブル
const
INFORMATION_TABLE	InfoIosKFuncs={
	#ifdef FUNC_DEBUG
	"iOS Key & touch functions",
	#endif
	returnOnly,	// Finit
	_Frun,		// Frun
	_Fend,		// Fend
	returnOnly,	// Fexit
	returnOnly,	// Fbreak
	NULL,		// FctrlD;未使用
	NULL,		// 予約1
	NULL,		// 予約2
	//
	iOSKTokens,
	iOSKParams,
	iOSKFuncExec
};

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