#include "stdafx.h"

#include <jenga/include/smoothie/Smoothie.h>

#include <Compiler.h>

#include "../BasicCompiler_Common/common.h"
#include "Opcode.h"

void ChangeTypeToDouble_ToFpuReg(int OldType){
	//現在のスタックの内容を実数レジスタに保存する
	//NumOpeの直後専用
	if(OldType==DEF_DOUBLE){
		//fld qword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_DOUBLE);

		//add esp,8
		compiler.codeGenerator.op_add_esp(8);
	}
	else if(OldType==DEF_SINGLE){
		//fld dword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_SINGLE);

		//add esp,4
		compiler.codeGenerator.op_add_esp(4);
	}
	else if(OldType==DEF_LONG){
		//fild dword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_LONG);

		//add esp,4
		compiler.codeGenerator.op_add_esp(4);
	}
	else if(OldType==DEF_DWORD){
		//pop eax
		compiler.codeGenerator.op_pop(REG_EAX);

		//push 0
		compiler.codeGenerator.op_push_V(0);

		//push eax
		compiler.codeGenerator.op_push(REG_EAX);

		//fild qword ptr[esp]
		compiler.codeGenerator.PutOld(
			(char)0xDF,
			(char)0x2C,
			(char)0x24
		);

		//add esp,8
		compiler.codeGenerator.op_add_esp(8);
	}
}
void ChangeTypeToDouble(int OldType){
	//現在のスタックの内容をdouble型に変換する
	//NumOpeの直後専用
	if(OldType==DEF_DOUBLE) return;
	else if(OldType==DEF_SINGLE){
		//fld dword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_SINGLE);

		//sub esp,4
		compiler.codeGenerator.op_sub_esp(4);

		//fstp qword ptr[esp]
		compiler.codeGenerator.op_fstp_basereg( DEF_DOUBLE, REG_ESP );
	}
	else if(OldType==DEF_INT64||OldType==DEF_QWORD){
		//64ビット整数型

		//fild qword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_INT64);

		//fstp qword ptr[esp]
		compiler.codeGenerator.op_fstp_basereg( DEF_DOUBLE, REG_ESP );
	}
	else if(IsWholeNumberType(OldType)){
		//その他整数型

		if(IsSignedType(OldType)){
			//符号あり

			if(OldType==DEF_INTEGER || (Smoothie::IsUnicode()&&OldType==DEF_CHAR)){
				//pop eax
				compiler.codeGenerator.op_pop(REG_EAX);

				//movsx eax,ax
				compiler.codeGenerator.op_movsx_R32R16( REG_EAX );

				//push eax
				compiler.codeGenerator.op_push(REG_EAX);
			}
			else if(OldType==DEF_SBYTE || (Smoothie::IsUnicode()==false&&OldType==DEF_CHAR)){
				//pop eax
				compiler.codeGenerator.op_pop(REG_EAX);

				//movsx eax,al
				compiler.codeGenerator.op_movsx_R32R8( REG_EAX );

				//push eax
				compiler.codeGenerator.op_push(REG_EAX);
			}

			//fild dword ptr[esp]
			compiler.codeGenerator.op_fld_ptr_esp(DEF_LONG);

			//sub esp,4
			compiler.codeGenerator.op_sub_esp(4);
		}
		else{
			//符号なし

			//pop eax
			compiler.codeGenerator.op_pop(REG_EAX);

			//push 0
			compiler.codeGenerator.op_push_V(0);

			//push eax
			compiler.codeGenerator.op_push(REG_EAX);

			//fild qword ptr[esp]
			compiler.codeGenerator.PutOld(
				(char)0xDF,
				(char)0x2C,
				(char)0x24
			);
		}

		//fstp qword ptr[esp]
		compiler.codeGenerator.op_fstp_basereg( DEF_DOUBLE, REG_ESP );
	}
	else SetError(9,NULL,cp);
}
void ChangeTypeToSingle(int OldType){
	//現在のスタックの内容をfloat型に変換する
	//NumOpeの直後専用
	if(OldType==DEF_SINGLE) return;
	else if(OldType==DEF_DOUBLE){
		//fld qword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_DOUBLE);

		//add esp,4
		compiler.codeGenerator.op_add_esp(4);

		//fstp dword ptr[esp]
		compiler.codeGenerator.op_fstp_basereg( DEF_SINGLE, REG_ESP );
	}
	else if(OldType==DEF_INT64||OldType==DEF_QWORD){
		//64ビット整数型

		//fild qword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_INT64);

		//add esp,4
		compiler.codeGenerator.op_add_esp(4);

		//fstp dword ptr[esp]
		compiler.codeGenerator.op_fstp_basereg( DEF_SINGLE, REG_ESP );
	}
	else if(IsWholeNumberType(OldType)){
		//その他整数型

		if(IsSignedType(OldType)){
			//符号あり

			if(OldType==DEF_INTEGER || (Smoothie::IsUnicode()&&OldType==DEF_CHAR)){
				//pop eax
				compiler.codeGenerator.op_pop(REG_EAX);

				//movsx eax,ax
				compiler.codeGenerator.op_movsx_R32R16( REG_EAX );

				//push eax
				compiler.codeGenerator.op_push(REG_EAX);
			}
			else if(OldType==DEF_SBYTE || (Smoothie::IsUnicode()==false&&OldType==DEF_CHAR)){
				//pop eax
				compiler.codeGenerator.op_pop(REG_EAX);

				//movsx eax,al
				compiler.codeGenerator.op_movsx_R32R8( REG_EAX );

				//push eax
				compiler.codeGenerator.op_push(REG_EAX);
			}

			//fild dword ptr[esp]
			compiler.codeGenerator.op_fld_ptr_esp(DEF_LONG);
		}
		else{
			//符号なし

			//fild dword ptr[esp]
			compiler.codeGenerator.op_fld_ptr_esp(DEF_LONG);
		}

		//fstp dword ptr[esp]
		compiler.codeGenerator.op_fstp_basereg( DEF_SINGLE, REG_ESP );
	}
	else SetError(9,NULL,cp);
}

void ChangeTypeToInt64(int OldType){
	//現在のスタックの内容をInt64型に変換する
	//NumOpeの直後専用
	if(Is64Type(OldType)) return;

	else if(OldType==DEF_DOUBLE){
		//fld qword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_DOUBLE);

		//fistp qword ptr[esp]
		compiler.codeGenerator.op_fistp_ptr_esp( sizeof(_int64) );
	}
	else if(OldType==DEF_SINGLE){
		//fld dword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_SINGLE);

		//sub esp,4
		compiler.codeGenerator.op_sub_esp(4);

		//fistp qword ptr[esp]
		compiler.codeGenerator.op_fistp_ptr_esp( sizeof(_int64) );
	}
	else if(IsWholeNumberType(OldType)){
		//その他整数

		if(IsSignedType(OldType)){
			//符号あり

			//pop eax
			compiler.codeGenerator.op_pop(REG_EAX);

			//cdq
			compiler.codeGenerator.op_cdq();

			//push edx
			compiler.codeGenerator.op_push(REG_EDX);

			//push eax
			compiler.codeGenerator.op_push(REG_EAX);
		}
		else{
			//符号なし

			//pop eax
			compiler.codeGenerator.op_pop(REG_EAX);

			//push 0
			compiler.codeGenerator.op_push_V(0);

			//push eax
			compiler.codeGenerator.op_push(REG_EAX);
		}
	}
	else SetError(9,NULL,cp);
}
void ChangeTypeToLong(int OldType){
	//現在のスタックの内容をLong型に変換する
	//NumOpeの直後専用
	if(OldType==DEF_DOUBLE){

		//fld qword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_DOUBLE);

		//add esp,4
		compiler.codeGenerator.op_add_esp(4);

		//fistp dword ptr[esp]
		compiler.codeGenerator.op_fistp_ptr_esp( sizeof(long) );
	}
	else if(OldType==DEF_SINGLE){
		//fld dword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_SINGLE);

		//fistp dword ptr[esp]
		compiler.codeGenerator.op_fistp_ptr_esp( sizeof(long) );
	}
	else if(OldType==DEF_INT64||OldType==DEF_QWORD){
		//pop eax
		compiler.codeGenerator.op_pop(REG_EAX);

		//add esp,4
		compiler.codeGenerator.op_add_esp(4);

		//push eax
		compiler.codeGenerator.op_push(REG_EAX);
	}
}
void ChangeTypeToInteger(int OldType){
	//現在のスタックの内容をInteger型に変換する
	if(OldType==DEF_BOOLEAN||
		OldType==DEF_BYTE||
		OldType==DEF_WORD||OldType==DEF_INTEGER || (Smoothie::IsUnicode()&&OldType==DEF_CHAR)) return;
	else if(OldType==DEF_SBYTE || (Smoothie::IsUnicode()==false&&OldType==DEF_CHAR)){
		//pop eax
		compiler.codeGenerator.op_pop(REG_EAX);

		//movsx eax,al
		compiler.codeGenerator.op_movsx_R32R8( REG_EAX );

		//push eax
		compiler.codeGenerator.op_push(REG_EAX);
	}
	else{
		ChangeTypeToLong(OldType);

		//pop eax
		compiler.codeGenerator.op_pop(REG_EAX);

		//and eax,0000FFFFh
		compiler.codeGenerator.op_and_RV( REG_EAX, 0x0000FFFF );

		//push eax
		compiler.codeGenerator.op_push(REG_EAX);
	}
}
void ChangeTypeToByte(int OldType){
	//現在のスタックの内容をbyte型に変換する
	if(OldType==DEF_BYTE||OldType==DEF_SBYTE || (Smoothie::IsUnicode()==false&&OldType==DEF_CHAR)) return;

	ChangeTypeToLong(OldType);

	//pop eax
	compiler.codeGenerator.op_pop(REG_EAX);

	//and eax,000000FFh
	compiler.codeGenerator.op_and_RV( REG_EAX, 0x000000FF );

	//push eax
	compiler.codeGenerator.op_push(REG_EAX);
}









void RestoreDefaultRegisterFromStackMemory( int type ){
	//現在のスタックの内容を実数レジスタに保存する
	//NumOpeの直後専用
	if(type==DEF_DOUBLE){
		//fld qword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_DOUBLE);

		//add esp,8
		compiler.codeGenerator.op_add_esp(8);
	}
	else if(type==DEF_SINGLE){
		//fld dword ptr[esp]
		compiler.codeGenerator.op_fld_ptr_esp(DEF_SINGLE);

		//add esp,4
		compiler.codeGenerator.op_add_esp(4);
	}
	else if( Is64Type( type ) ){
		//pop eax
		compiler.codeGenerator.op_pop(REG_EAX);

		//pop edx
		compiler.codeGenerator.op_pop(REG_EDX);
	}
	else{
		//pop eax
		compiler.codeGenerator.op_pop(REG_EAX);
	}
}

void SetVariableFromEax( const Type &varType,int CalcType,RELATIVE_VAR *pRelativeVar){
	/////////////////////////////////////////////////
	// eaxの内容を変数にコピーするコードを抽出
	/////////////////////////////////////////////////

	if( varType.IsBoolean() )
	{
		//bool
		SetBooleanVariable(CalcType,pRelativeVar);
	}
	else if( varType.IsReal() )
	{
		// Double/Single型変数へレジスタの値を代入
		SetRealVariable(varType.GetBasicType(), CalcType, pRelativeVar);
	}
	else if( varType.IsWhole() || varType.IsObject() )
	{
		int typeSize = varType.GetSize();

		//整数変数へraxの値を格納する
		SetWholeVariable( typeSize, CalcType, pRelativeVar );
	}
	else{
		SetError(300,NULL,cp);
	}
}

void OpcodeCalc( const char *Command ){
	int i,i2,i3;
	char variable[VN_SIZE];



	//////////////////////////////////////
	// インクリメント・デクリメント
	//////////////////////////////////////

	for(i=0;;i++){
		if(Command[i]=='\"'){
			//ダブルクォートは不正なのでエラー扱い
			variable[i]=0;
			SetError(3,variable,cp);
			return;
		}

		if(Command[i]=='('){
			i2=GetStringInPare(variable+i,Command+i);
			i+=i2-1;
			continue;
		}
		if(Command[i]=='['){
			i2=GetStringInBracket(variable+i,Command+i);
			i+=i2-1;
			continue;
		}
		if(Command[i]=='\0'){

			///////////////////////////////////
			// インクリメント・デクリメント
			///////////////////////////////////

			if(i>2){
				if(Command[i-2]=='+'&&Command[i-1]=='+'){
					//インクリメント
					variable[i-2]=0;
					IncDec(CALC_ADDITION,variable,"1");
					return;
				}
				else if(Command[i-2]=='-'&&Command[i-1]=='-'){
					//デクリメント
					variable[i-2]=0;
					IncDec(CALC_SUBTRACTION,variable,"1");
					return;
				}
			}


			//先端部分の識別子をエラーキーワードにする
			for(i=0;;i++){
				if(!IsVariableChar(Command[i])){
					variable[i]=0;
					break;
				}
				variable[i]=Command[i];
			}

			if(GetVarType(variable,Type(),0)){
				//変数リストに該当したとき
				SetError(1,NULL,cp);
			}
			else{
				if( compiler.GetObjectModule().meta.GetGlobalConsts().IsExist(variable)
					|| compiler.GetObjectModule().meta.GetGlobalConstMacros().IsExist(variable) )
				{
					//定数リストに該当したとき
					SetError(1,NULL,cp);
				}
				else{
					//変数リスト、定数リストに該当しないとき
					SetError(3,variable,cp);
				}
			}
			return;
		}

		i2=GetCalcId(Command+i,&i3);
		if(i2){
			variable[i]=0;

			if(Command[i]=='=') break;

			if(Command[i+1+i3]=='='){
				IncDec(i2,variable,Command+i+1+i3+1);
				return;
			}
		}

		variable[i]=Command[i];
	}

	if(Command[i+1]=='\0'){
		SetError(1,NULL,cp);
		return;
	}


	///////////////////////////////////////////////////////////////
	// インデクサのsetアクセサ（[]=演算子のオーバーロードに対応）
	///////////////////////////////////////////////////////////////

	char ObjName[VN_SIZE],array_element[VN_SIZE];
	GetArrayElement(variable,ObjName,array_element);
	if(array_element[0]){
		Type varType;
		if( GetVarType(ObjName,varType,0) && varType.IsObject() ){
			char temporary[VN_SIZE];
			sprintf(temporary,"%s.%c%c%c",ObjName,1,ESC_OPERATOR,CALC_ARRAY_SET);

			char temp2[VN_SIZE];
			sprintf(temp2,"%s,%s",array_element,Command+i+1);

			int idProc;
			void *pProc;
			idProc=GetProc(temporary,(void **)&pProc);
			if( idProc )
			{
				CallProc(
					idProc,
					pProc,
					temporary,
					temp2,
					Type(),			// ベースタイプはなし
					Type()
				);
				return;
			}
		}
	}


	if( lstrcmpi( variable, "This" ) == 0 ){
		SetError(133,NULL,cp);
		return;
	}


	////////////////////////////////////////
	// 変数のタイプ型を識別して、演算を行う
	////////////////////////////////////////

	Type varType;

	//型を識別
	if( !GetTermTypeOnlyVariable(variable,varType) ){

		// プロパティ用のメソッドを呼び出す
		if(!CallPropertyMethod( variable, Command+i+1, Type() )){
			//エラーを表示
			GetVarType(variable,varType,true);
		}

		return;
	}

	RELATIVE_VAR VarRelativeVar;
	if( varType.IsStruct() ){
		//代入コピーに備える

		//変数アドレスを取得
		if(!GetVarOffsetReadWrite(
			variable,
			&VarRelativeVar,
			varType)) return;

		SetVarPtrToEax(&VarRelativeVar);

		//push eax
		compiler.codeGenerator.op_push(REG_EAX);
	}

	//NumOpe...（スタックに答えが格納される）
	BOOL bCalcUseHeap;
	Type calcType;
	if( !NumOpe(Command+i+1,varType,calcType,&bCalcUseHeap) ){
		return;
	}

	if( calcType.IsObject() && !calcType.Equals( varType ) ){
		bool isUpCast = false;
		if( varType.IsObject() ){
			if( varType.GetClass().IsEqualsOrSubClass( &calcType.GetClass() ) ){
				isUpCast = true;
			}
		}
		if( !isUpCast ){
			//キャスト演算子のオーバーロードに対応する
			CallCastOperatorProc(calcType,bCalcUseHeap,varType);
		}
	}

	//変数アドレスを取得
	if( !TermOpeOnlyVariable( variable, varType, VarRelativeVar, true ) )
	{
		SetError();
		return;
	}

	if(varType.GetBasicType()&FLAG_PTR){
		SetError(14,variable,cp);
		return;
	}

	if( varType.IsStruct() ){
		//構造体インスタンスへの代入
		SetStructVariable(varType,calcType,bCalcUseHeap);
		return;
	}


	if( varType.IsObject() && compiler.GetObjectModule().meta.GetBlittableTypes().IsExist( calcType ) ){
		// Blittable型をオブジェクトとして扱う
		vector<const UserProc *> userProcs;
		compiler.GetObjectModule().meta.GetBlittableTypes().GetClass( calcType ).GetStaticMethods().Enum( "_Create", userProcs );
		if( userProcs.size() != 1 ){
			SetError();
			return;
		}
		const UserProc *pUserProc = userProcs[0];

		// call System.[TypeClass]._Create
		compiler.codeGenerator.op_call( pUserProc );

		// push eax
		compiler.codeGenerator.op_push( REG_EAX );

		calcType = pUserProc->ReturnType();
	}


	/////////////////////////////////
	// 右辺、左辺の型チェックを行う
	/////////////////////////////////

	CheckDifferentType(varType,calcType,0,0);


	/////////////////////////////////////////////////
	// スタックの内容を変数にコピーするコードを抽出
	/////////////////////////////////////////////////

	//eax、edx:eax、またはst(0)にスタック上のデータを取り出す
	RestoreDefaultRegisterFromStackMemory( calcType.GetBasicType() );

	SetVariableFromEax(varType,calcType.GetBasicType(),&VarRelativeVar);
}
