#include "InterpreterUtil.h"

#include "vm/Object.h"

#include "../metadata/MetadataUtil.h"

namespace hybridclr
{
namespace interpreter
{
	TypeDesc GetValueTypeArgDescBySize(uint32_t size)
	{
		if (size <= 8)
		{
			return { LocationDataType::U8, 1 };
		}
		return { LocationDataType::S_N, (uint32_t)metadata::GetStackSizeByByteSize(size) };
	}

	TypeDesc GetTypeArgDesc(const Il2CppType* type)
	{
		if (type->byref)
		{
			return { LocationDataType::U8, 1 };
		}
		switch (type->type)
		{
		case IL2CPP_TYPE_BOOLEAN:
		case IL2CPP_TYPE_U1:
			return{ LocationDataType::U1, 1 };
		case IL2CPP_TYPE_I1:
			return{ LocationDataType::I1, 1 };
		case IL2CPP_TYPE_I2:
			return{ LocationDataType::I2, 1 };
		case IL2CPP_TYPE_CHAR:
		case IL2CPP_TYPE_U2:
			return{ LocationDataType::U2, 1 };
		case IL2CPP_TYPE_I4:
		case IL2CPP_TYPE_U4:
		case IL2CPP_TYPE_R4:
		case IL2CPP_TYPE_I8:
		case IL2CPP_TYPE_U8:
		case IL2CPP_TYPE_R8:
		case IL2CPP_TYPE_I:
		case IL2CPP_TYPE_U:
		case IL2CPP_TYPE_FNPTR:
		case IL2CPP_TYPE_PTR:
		case IL2CPP_TYPE_BYREF:
		case IL2CPP_TYPE_STRING:
		case IL2CPP_TYPE_ARRAY:
		case IL2CPP_TYPE_SZARRAY:
		case IL2CPP_TYPE_OBJECT:
		case IL2CPP_TYPE_CLASS:
			return{ LocationDataType::U8, 1 };
		case IL2CPP_TYPE_TYPEDBYREF:
			return GetValueTypeArgDescBySize(sizeof(Il2CppTypedRef));
		case IL2CPP_TYPE_VALUETYPE:
		{
			Il2CppClass* klass = il2cpp::vm::Class::FromIl2CppType(type);
			IL2CPP_ASSERT(IS_CLASS_VALUE_TYPE(klass));
			if (klass->enumtype)
			{
				return GetTypeArgDesc(&klass->castClass->byval_arg);
			}
			return GetValueTypeArgDescBySize(il2cpp::vm::Class::GetValueSize(klass, nullptr));
		}
		case IL2CPP_TYPE_GENERICINST:
		{
			Il2CppGenericClass* genericClass = type->data.generic_class;
			if (genericClass->type->type == IL2CPP_TYPE_CLASS)
			{
				IL2CPP_ASSERT(!IS_CLASS_VALUE_TYPE(il2cpp::vm::Class::FromIl2CppType(type)));
				return{ LocationDataType::U8, 1 };
			}
			else
			{
				Il2CppClass* klass = il2cpp::vm::Class::FromIl2CppType(type);
				IL2CPP_ASSERT(IS_CLASS_VALUE_TYPE(klass));
				if (klass->enumtype)
				{
					return GetTypeArgDesc(&klass->castClass->byval_arg);
				}
				return GetValueTypeArgDescBySize(il2cpp::vm::Class::GetValueSize(klass, nullptr));
			}
		}
		default:
		{
			RaiseExecutionEngineException("not support arg type");
			return{ LocationDataType::U8, 1 };
		}
		}
	}

	Il2CppObject* TranslateNativeValueToBoxValue(const Il2CppType* type, void* value)
	{
		if (type->byref)
		{
			RaiseExecutionEngineException("TranslateNativeValueToBoxValue can't box ref");
			return nullptr;
		}
		Il2CppClass* klass = il2cpp::vm::Class::FromIl2CppType(type);
		return il2cpp::vm::Object::Box(klass, value);
	}
}
}