//--------------------------------------------------------------------------------------
//
// X-BASIC'
//	インタープリター(配列・変数初期化)
//
//--------------------------------------------------------------------------------------

#include "fdef.h"
#include "XB.h"
#include "libs.h"
#include <ctype.h>

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

// 配列の構造から、配列の実内容の先頭アドレスを返す
// tyousoNo=指定された要素番号を全次元での通し番号に直したもの
// 指定要素内容は((型*)リターン値)[tyousoNo]から読み出せばよい
UByte *dimSetSub(slAryNO *tyousoNo,swVarNO no,VAR_CTRL_EXEC *var_ctrl_exec)
{	// 1757 dim3:
	fnslog((char *)__FUNCTION__);
	// 配列番号noはローカル情報を取り去った物を渡すこと
	nslog(@"配列番号=%d:var_ctrl_exec=%p",no,var_ctrl_exec);

	DIM1_EXEC **dim1_exec_adrs=(DIM1_EXEC **)var_ctrl_exec->varArea;
	DIM1_EXEC  *dim1_exec	  =dim1_exec_adrs[no];  // 番号noの配列のDIM1_EXECの実体アドレスを得る
	nslog(@"dim1_exec=%p",dim1_exec);
	swJigen jigen1=dim1_exec->jigen1;	// 次元数-1(X-BASICの定義)
	uwYouso *youso=dim1_exec->youso;	// 要素数群
	nslog(@"次元-1=%d/.yousoアドレス=%p",jigen1,youso);
	if (jigen1>MAX_DIM_JIGEN-1) {
		nslog(@"次元数が大きすぎる(次元-1=%d)",jigen1);
		IError(ERR_SYSTEM);
	}
	
#ifdef INTERNAL_TRACE
	if (getinfp()!=NULL) {
		fprintf(getinfp(),"次元-1=%d/.yousoアドレス=%p\n",jigen1,youso);
	}
#endif

	// 1次元目
	SLong hno=intGetValue();	// 要素番号
	nslog(@"1次元目:指定要素番号=%d/全要素数=%d",(int)hno,*youso);

#ifdef INTERNAL_TRACE
	if (getinfp()!=NULL) {
		fprintf(getinfp(),"1次元目:要素番号=%d/要素数=%d\n",(int)hno,*youso);
	}
#endif
	
	if (hno<0) {
		IError(ERR_ARRAY_COUNT_MINUS);
	}
	// 配列はdim a(n)で(0～n)までが使える
	// n=最終要素番号
	// *yousoにはn+1=要素数が格納されている
	if (hno>=*youso) { // 固定配列はコンパイル時にチェックされているが、可変配列では実行時にチェックが必要
		// 要素番号>=要素数だとエラー
		IError(ERR_ARRAY_COUNT_TOO_LARGE);
	}
	youso++; // 次の要素数、または1次元の場合は配列本体へ

	if (jigen1==0) { // 1次元の時はオフセットテーブルがないので別処理
		fnslog("1次元の時はオフセットテーブルがないので別処理");
		// one_dim	一次元の時 1807
		*tyousoNo=hno; // 要素番号=通し要素番号である
		//
		// 要素数テーブル直後が配列実体（１次元ではオフセットテーブルがないから）
		// adrs[tyousoNo*型別サイズ]が指定要素番号の内容がある位置
		nslog(@"要素アドレス:%p",youso);
		return ((UByte *)youso);
	}
	
	// 2次元以上の時；オフセットテーブルを使う	1768
	// dim (m,n,...)
	/* 例
		dim(4,2,1)の時
		youso	5,3,2		宣言+1個
		offset	5,5*3=15	1次元目分は存在しない(2次元目以降分のみ)	basic(1次元,2次元,3次元)
		(0,0,0)				(4,0,0)		番号指定順がCとは逆なので注意	c[3次元][2次元][1次元]
	(0,0,0)	[ 0][ 1][ 2][ 3][ 4]
		(0,1,0)				(4,1,0)	
	(0,1,0)	[ 5][ 6][ 7][ 8][ 9]		(0,2,0)			(4,2,0)
	(0,2,0)	[10][11][12][13][14]
		(0,0,1)				(4,0,1)	
	(0,0,1)	[15][16][17][18][19]		(0,1,1)			(4,1,1)
	(0,1,1)	[20][21][22][23][24]		(0,2,1)			(4,2,1)
	(0,2,1)	[25][26][27][28][29]
	
	2次元目用	offset[0]=5			1*1次元目要素数
	3次元目用		  [1]=3*5=15	offset[0]*2次元目要素数
	
	dim (n1,n2,n3)のとき、その通し番号位置は、
		(n3+1)*offset[1]+(n2+1)*offset[0]+n1
	
	*/
	swJigen jigen=dim1_exec->jigen1+1;
	ULong *ofstTable=(ULong *)&dim1_exec->youso[jigen]; // 次元別オフセットテーブルは要素数テーブルの直後(これは型変換)
	ULong tno=hno; // 1次元目の番号
	#ifdef CORE_DEBUG
	int i=2; // ここは2次元目からの処理
	#endif
	// 「例」dim(l,m,n)
	// jigen1=2
	// hno=l→m→nで、lは1次元目の処理ですでに読まれている
	while (jigen1-->0) {
		// 各次元の指定番号を読んでいく
		hno=intGetValue();
		if (hno<0) {
			IError(ERR_ARRAY_COUNT_MINUS);
		}
		if (hno>=*youso) { // コンパイル時にチェックされているので、*youso<MAX_DIMである
			IError(ERR_ARRAY_COUNT_TOO_LARGE);
		}
#ifdef CORE_DEBUG
		nslog(@"%d次元目:要素番号=%d/要素数=%d/offset=%d",i++,(int)hno,*youso,(int)*ofstTable);
#endif

		// tno+=(*ofstTable++)*hno;	// 元の式
		SLong ofst=readLong(ofstTable++);
		tno+=(ofst*hno); // 全次元での通し要素番号；上記式参照
		youso++; // 次の要素数へ
		nslog(@"/通し番号=%d\n",(int)tno);
	}
	*tyousoNo=tno; // 通し要素番号
	// オフセットテーブル直後が配列実体
	return ((UByte *)ofstTable);
}

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

// 要素数とオフセットテーブルを格納する
// ここでいうオフセットとは、多次元配列を通しで見たときの、2次元目以降の先頭位置を示す要素番号である
// 可変長配列の要素数未確定状態では呼び出さないこと
// dim_clr_sub2	3622
// sz1		1要素サイズ
// jigen	次元数
// varArea	DIM1_EXECを確保するアドレス
UByte *dimSetYousoAndOffset(swJigen jigen,SWord sz1,uwYouso *youso,UByte *varArea)
{
	DBG_Func();
	nslog(@"varArea=%p←次元数=%d/1要素サイズ=%d\n",varArea,jigen,sz1);

	if (jigen>MAX_DIM_JIGEN) {
		nslog(@"次元数(%d)が多すぎる",jigen);
		IError(ERR_ARRAY_DIMENSION_TOO_LARGE);
	}

	DIM1_EXEC *dim1_exec=(DIM1_EXEC *)varArea;	// 読み替えのため
	dim1_exec->offset=0;		// offset（使ってない）
	dim1_exec->jigen1=jigen-1;	// 次元数-1(X-BASICの定義)
	dim1_exec->size1 =sz1;		// 1要素サイズ

	// 3499
	int i;
	for (i=0;i<jigen;i++) {
		dim1_exec->youso[i]=youso[i];	// 要素数の大きさ(拡張配列の場合は0)
		nslog(@"次元%d=%d個\n",i+1,youso[i]);
	}
	
#ifdef CORE_DEBUG
	UWord *yousoTable=&dim1_exec->youso[i]; // 要素数テーブルの直後
	nslog(@"要素数テーブルの直後:%p%s\n",yousoTable,jigen==1? "":"=オフセットテーブル");
#endif
	// 以下の処理は、拡張配列時には実質無効になる(dim文処理時に実確保される)
	// オフセットテーブルを作成する＆要素部分総サイズを得る	3504
	ULong *ofstTable=(ULong *)&dim1_exec->youso[i]; // これは型変換
		// i=jigen
		// オフセットテーブルは2次元以上の時生成される(1次元では存在しない)
		// オフセットは.lで格納する
	// オフセットテーブル直後が配列実内容領域
	varArea=(UByte *)&ofstTable[jigen-1]; // これは型変換
	nslog(@"オフセットテーブル作成：次元数=%d\n",jigen);
	ULong sizeL=1;
#if 1
	for (i=0;i<jigen-1;i++) {
		sizeL *= youso[i];	// 1次元目の要素数
		ofstTable[i]=sizeL;	// 2次元目のオフセット(=通し要素番号)に入れる
		nslog(@"オフセットテーブル[%d](%d+1次元用)=%d\n",i+1,i+1,(int)ofstTable[i]);
	}
	// 最後の次元分も加える
	sizeL *= youso[i];
#else // オリジナル
	while (jigen-->0) {
		UWord youso1=*(--yousoTable);	// 要素数リストを逆読み
		sizeL *= youso1;
		nslog(@"1要素数=%d/総要素数=%ld\n",youso1,sizeL);
		if (jigen>0) {			// 最後の1つ=1次元目は入れない
			*--ofstTable=sizeL;	// 配列計算用のオフセット(通しの要素番号)
		}
	}
#endif
	// 初期化
	nslog(@"varArea=%p,sizeL=%d,sz1=%d",varArea,(int)sizeL,sz1);
	sizeL*=sz1; // 総バイト数
	memclr(varArea,sizeL);
	nslog(@"初期化:varArea=%p～%dバイト\n",varArea,(int)sizeL);
	
	// 次の領域を返す
	varArea+=sizeL;
	nslog(@"→次varArea=%p\n",varArea);
	return (varArea);
}

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

// 変数の領域確保と初期化
// ここではmalloc()によるメモリの確保は一切ない
// internalCode.wp	このブロックで使う引数リストの先頭アドレス
UByte *
variableInit(UByte *varArea							// このブロックで使う変数エリア先頭アドレス
			,VARLIST_CHAIN	*varListInfo			// このブロックで使う変数リスト(variableBuffCtrl内)
			,INTERNAL_CODE_INFO	*internalCodesInfo	// 中間コードエリア情報アドレス
			,VAR_CTRL_EXEC	*varCtrl
			,UWord numFAC,FAC fac[])
{	// 3399
	nslog(@"%s:varCtrl=%p",__FUNCTION__,varCtrl);
	
	// 普通変数
	nslog(@"普通変数確保＆初期化");
	VAR_TYPES typeNo;
	for (typeNo=VAR_INT;typeNo<=VAR_FLOAT;typeNo++) {
		SWord size=varListInfo->cnt[typeNo];	// 変数個数
		nslog(@"%s変数:%d個",name_type[typeNo].name,size);
		varCtrl[typeNo].cnt		=size;			// コピーしておく(オリジナルでは未使用)
		varCtrl[typeNo].varArea	=varArea;		// 実体エリア先頭アドレス
		nslog(@"varCtrl[%d]=%p/",typeNo,&varCtrl[typeNo]);
		nslog(@"varArea=%p(%d個)",varArea,size);
//		nslog(@"varCtrl[%d].cnt=%d\n",typeNo,varCtrl[typeNo].cnt);
		//
		switch ((int)typeNo) { // (int)を付けないとenumのメンバーが足りない警告が出る
			case VAR_STR:
				DBG_prints("(VAR_STR)");
				if (size>(0x8000/operationStatus.sz_str)) {
					// sz_str=256なら128 個までしか出来ない;256*128=32768;short範囲でないといけないから
					// グローバルも同様
					// 足りないという話が出てきたら考える
					IError(ERR_TOO_MUCH_STRING_VARIABLE);
				}
				break;
			case VAR_CHAR:
				DBG_prints("(VAR_CHAR)");
				size=quadBoundary(size);	// 4バイト(.l)境界化
				break;
		}
		size*=Var1Size[typeNo]; // 変数個数分に必要なワークエリアサイズ
		// 初期化
		nslog(@";初期化:size=%dバイト\n",size); // 下で改行するのでここでは不要
		memclr(varArea,size);
	//	DBG_dump(name_type[typeNo].name,varArea,size);	// 初期化した直後なので何も入ってない
		varArea+=size; // 次へ
		DBG2_printf1("→次varArea=%p\n",varArea);
	}
	//
	// 配列初期:	3476
	nslog(@"\n配列確保＆初期化");
	swVarCNT dimCnt=varListInfo->cnt[VAR_DIM];// このブロックで使う配列の個数(変数チェインリストより)
	nslog(@"このブロックの配列数=%d個\n",dimCnt);
	varCtrl[VAR_DIM].cnt=dimCnt;		// コピーしておく(オリジナルでは未使用)
	varCtrl[VAR_DIM].varArea=varArea;	// 実体(DIM1_EXEC)エリア先頭アドレス設定;varArea=variableAreaから確保
	nslog(@"varCtrl[VAR_DIM]=%p",&varCtrl[VAR_DIM]);

	nslog(@"varArea=%p",varArea);		// まずここからに配列ポインタ群(DIM1_EXEC *dim1_exec_adrs[dimCnt])を作成する
	DIM1_EXEC **dim1_exec_adrs=(DIM1_EXEC **)varArea;	// 読み替えのため
	varArea=(UByte *)(&dim1_exec_adrs[dimCnt]);			// DIM1_EXEC実体を記録する位置は、配列ポインタ群の後
	nslog(@"配列実体(DIM1_EXEC)アドレス=%p\n",varArea);
	
	DIMLIST_CHAIN *dimList=varListInfo->dimList;
	//
	int i=0;
	while (dimCnt-->0) { // 配列がある時
		// 3483
		UWord jigenType=dimList->jigenType;	// 次元&型
		swJigen jigen=_getJignFromJigenAndType(jigenType);
		uwTYPE   type=_getTypeFromJigenAndType(jigenType);
		nslog(@"%d個目:次元=%d($%x),型=$%04x\n",i,jigen,jigen,type);
		// 拡張配列かどうかではなく、容量固定か可変かで処理を分ける
		if (dimList->youso[0]==0) { // 容量可変配列
			nslog(@"容量可変配列時");
			dim1_exec_adrs[i]=NULL; // 念のため；あとでdimで確定する
			// ここでは情報も構築しないので、varAreaも変更されない
		} else { // 固定サイズ通常配列
			nslog(@"固定サイズ配列時");
			// DIM1_EXECの位置テーブル記録	3491
			dim1_exec_adrs[i]=(DIM1_EXEC *)varArea;
			nslog(@"DIM1_EXECアドレス=%p\n",varArea);
			//
			// DIM1_EXEC情報を構築する	3493
			SWord sz1=Var1Size[_getSerialType(type)];	// 1要素サイズ	3496	bsr dim_clr_sub1相当
			varArea=dimSetYousoAndOffset(jigen,sz1,dimList->youso,varArea); // 連続確保の時はリターン値のvarAreaが次の確保位置になる
		}
		// 次の配列分へ
		dimList=(DIMLIST_CHAIN *)pointerAdd(dimList,SZ_WORD+SZ_WORD*jigen); // valiable.hのVARLIST_CHAINの定義参照
		// ↑
		i++;
	}
	
	// 引数のセット 3528
	nslog(@"\n引数セット");
#ifdef CORE_DEBUG
	nslog(@"internalCodesInfo->paramChainListNo=%d\n",internalCodesInfo->paramChainListNo); // ここにparamの個数情報がある
#endif
	PARAM_CHAIN *paramChain=(PARAM_CHAIN *)getValueFromBuffer(&paramBuffCtrl, internalCodesInfo->paramChainListNo);

	swVarCNT cnt=paramChain->msType; // 引数個数
#ifdef CORE_DEBUG
	nslog(@"引数個数=%d",cnt);
#endif
	swVarNO no;
	i=0;
	while (cnt-->0) {	// 引き数setloop: 3537
		nslog(@"引数%d:",i);
		++paramChain; // 次からが実引数情報
		uwMATHTYPE msType=paramChain->msType;	// 型
			  no=getVarNo(paramChain->no);		// 変数/配列番号
		if (!(msType&_PARAM)) {
			// 3540
			nslog(@"配列:番号%d(%d)\n",no,paramChain->no);
			// 渡し側の配列情報のアドレスをそのまま渡す
			DIM1_EXEC **dim1_exec_adrs=(DIM1_EXEC **)varCtrl[VAR_DIM].varArea;
			dim1_exec_adrs[no]=(DIM1_EXEC *)fac[i].dl.bp[LL];
			nslog(@"dim1_exec_adrs=%p<-%p\n",dim1_exec_adrs,dim1_exec_adrs[no]);
#ifdef CORE_DEBUG
			DIM1_EXEC *dim1_exec=dim1_exec_adrs[no];
			nslog(@"配列%d DIM1_EXEC=%p\n",no,dim1_exec); _f();
			nslog(@"次元-1=%d,1要素サイズ=%d\n",dim1_exec->jigen1,dim1_exec->size1); _f();
			int j;
			for (j=0;j<=dim1_exec->jigen1;j++) {
				nslog(@"次元%d=要素%d個\n",j+1,dim1_exec->youso[j]); _f();
			}
			nslog(@"\n"); _f();
#endif
		} else {
			nslog(@"変数:番号%d(%d)=値",no,paramChain->no);
			switch (_getType(msType)) {
				case T_INT:		// int引数		3581
				{
					SLong *ip=(SLong *)varCtrl[VAR_INT].varArea;
					ip[no]=fac[i].dl.l[LL];
					nslog(@"%d(int)\n",(int)ip[no]);
				}
					break;
				case T_FLOAT:	// float引数		3573
				{
					DOUBLE_LONG *dp=(DOUBLE_LONG *)varCtrl[VAR_FLOAT].varArea;
					readDOUBLE_LONG(&dp[no], &fac[i].dl); // dp[no]=fac[i].dl;
					nslog(@"%f(float)\n",dp[no].d);
				}
					break;
				case T_CHAR:	// char引数		3553
				{
					UByte *bp=(UByte *)varCtrl[VAR_CHAR].varArea;
					bp[no]=(UByte)fac[i].dl.l[LL];
					nslog(@"$%02x(char)\n",bp[no]);
				}
					break;
				case T_STR:		// str引数:		3560
				{
					SByte *bp=(SByte *)varCtrl[VAR_STR].varArea;
					bp+=(no*operationStatus.sz_str); // no*=operationStatus.sz_strとするとオーバーフロー起こす可能性があるので
					//					strcpy((char *)bp,(char *)fac[i].dl.l[LL]);	// 32bit
					strcpy(bp,fac[i].dl.cp[LL]); // 64bit
					nslog(@"%s(str)\n",bp);
				}
					break;
			}
		}
		i++;
	}
	nslog(@"%s(end)",__FUNCTION__);

	return (varArea);
}

//--------------------------------------------------------------------------------------
// 外部関数用補助関数
//--------------------------------------------------------------------------------------

// no = fac[no]から取得
void *getDim1FromFAC(FAC fac[],int no,SWord *size1,uwYouso *youso)
{
	DIM1_EXEC *dim1_exec=(DIM1_EXEC *)fac[no].dl.bp[LL];
	//
	swJigen jigen =dim1_exec->jigen1+1;	// 次元数
	if (jigen!=1) {
		// エラー処理
		setExternalFuncErrmes("配列は1次元に限ります","Array is limited to one-dimension.");
		return (NULL); // エラーの印
	}
	*size1=dim1_exec->size1;	// 1要素サイズ
	*youso=dim1_exec->youso[0];	// 要素数(1次元限定だから[0])
	// 1次元のみなので、オフセットテーブルはない
	return (&dim1_exec->youso[1]);
}

// dim1_exec=fac[n].dl.bp[LL]から1要素サイズと総要素数を返す
ULong sizeofArray(DIM1_EXEC *dim1_exec,int *isize1)
{
	swJigen jigen =dim1_exec->jigen1+1;	// 次元数
	SWord size1   =dim1_exec->size1;	// 1要素サイズ
	uwYouso *youso=dim1_exec->youso;	// 要素数テーブル
	ULong sizeAll=1;
	int i;
	for (i=0;i<jigen;i++) {
		sizeAll*=(*youso++);
	}
	DBG_printf2("1要素サイズ=%d,総要素数=%d\n",size1,(int)sizeAll);
	*isize1=size1;	// 1要素サイズを返す
	return sizeAll; // 総要素数を返す
}

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