#pragma once
#include "uclass.h"
namespace refl {
	template<typename T>
	inline bool ObjectView::Get(const Name& name, T& t)
	{
		if (cache && cache->name == name) {
		_cache_get: bool isChild = cache->type->IsChildOf<T>(true);
			if (isChild) {
				t = *(T*)(ptr + cache->data.offset);
			}
			return isChild;
		}
		auto field = cls->GetField(name, false);
		if (field) {
			cache = field;
			goto _cache_get;
		}
		if (cls->parent) {
			return Parent().Get(name, t);
		}
		return false;
	}
	template<typename T>
	inline bool ObjectView::Set(const Name& name, const T& t)
	{
		if (cache && cache->name == name) {
		_cache_set: bool isChild = cache->type->IsChildOf<T>(true);
			if (isChild) {
				*(T*)(ptr + cache->data.offset) = t;
			}
			return isChild;
		}
		auto field = cls->GetField(name, false);
		if (field) {
			cache = field;
			goto _cache_set;
		}
		if (cls->parent) {
			return Parent().Set(name, t);
		}
		return false;
	}
	template<typename ...Args>
	bool ObjectView::Invoke(const Name& name, Args&&... args)
	{
		auto field = cls->GetField(name, true);
		if (!field) {
			if (cls->parent) {
				return Parent().Invoke(name, args...);
			}
			return false;
		}
		constexpr int inputSize = sizeof...(Args);
		auto params = field->type->GetParams();
		int paramsSize = params.size();

		std::vector<ArgsView> ArgsList;
		ArgsList.reserve(paramsSize);
		ArgsList.emplace_back();

		bool member = field->flag & FIELD_MEMBER_FLAG;
		if (member) {
			ArgsList.emplace_back(ptr, &TypeInfo<const void*>::StaticClass);
		}
		(..., (ArgsList.emplace_back(args)));


		int argsSize = ArgsList.size();
		if (argsSize < paramsSize && field->flag & FIELD_METHOD_VALUE_FLAG) {
			auto [argsPtr, valueSize] = field->value.method();
			if (argsSize + valueSize >= paramsSize) {
				for (int i = valueSize + argsSize - paramsSize; i < valueSize; i++) {
					ArgsList.push_back(*(argsPtr + i));
				}
				argsSize = paramsSize;
			}
		}

		if (argsSize < paramsSize) {
			return false;
		}

		for (int i = member + 1; i < paramsSize; i++) {
			if (ArgsList[i].cls != params[i] && !ArgsList[i].ConvertTo(params[i])) {
				return false;
			}
		}
		field->type->Call(*field, ArgsList);
		return true;
	}
	template<_ReflCheck_Ctor R, typename ...Args>
	R ObjectView::Invoke(const Name& name, Args&&... args)
	{
		auto field = cls->GetField(name, true);
		if (!field) {
			if (cls->parent) {
				return Parent().Invoke<R>(name, args...);
			}
			return {};
		}
		R ret{};
		constexpr int inputSize = sizeof...(Args);
		auto params = field->type->GetParams();
		int paramsSize = params.size();
		bool member = field->flag & FIELD_MEMBER_FLAG;
		if (inputSize + member >= paramsSize) {
			return false;
		}
		std::vector<ArgsView> ArgsList;
		ArgsList.reserve(paramsSize);
		ArgsList.emplace_back(ret);
		if (member) {
			ArgsList.emplace_back(ptr, &TypeInfo<const void*>::StaticClass);
		}
		(..., (ArgsList.emplace_back(args)));
		for (int i = 0; i < paramsSize; i++) {
			if (ArgsList[i].cls != params[i] && !ArgsList[i].ConvertTo(params[i])) {
				return ret;
			}
		}
		field->type->Call(*field, ArgsList);
		return ret;
	}
	ObjectView ObjectView::Parent() {
		return { ptr, cls ? cls->parent : nullptr };
	}
	/*平凡对象的转化要支持吗
	* 比如同大小的安全转化
	* 对象从小到大
	* 对象从大到小
	*/
	bool ArgsView::ConvertTo(const UClass* toClass) {
		//子类转父类
		if (cls->IsChildOf(toClass)) {
			cls = toClass;
			return true;
		}
		return false;
	}
	ArgsValueList::ArgsValueList(const std::vector<ArgsValue>& args) {
		num = args.size();
		int size = num * sizeof(ArgsView);
		for (auto& arg : args) {
			size += arg.cls->size;
		}
		data = (void*)malloc(size);
		ptr = (ArgsView*)data;
		char* pData = ((char*)data) + num * sizeof(ArgsView);
		for (auto& arg : args) {
			arg.type->InitObject(pData);
			arg.type->CopyObject(pData, arg.val);
			ptr->cls = arg.cls;
			ptr->val = pData;
			ptr++;
			pData += arg.cls->size;
		}
		ptr = (ArgsView*)data;
	}
	inline ArgsValueList::~ArgsValueList()
	{
		if (num == 0 || !data) {
			return;
		}
		for (int i = 0; i < num; i++) {
			ptr->cls->DestObject((void*)ptr->val);
		}
		free(data);
		num = 0;
		data = nullptr;
	}
}