#pragma once
#include "type.h"
#include "field.h"
#include "object.h"
namespace refl {
	class UClass{
		using enum FieldFlag;
	public:
		Name name;
		uint32_t size;
		UClass* parent;
	public:
		constexpr UClass(Name name, uint32_t size, UClass* parent = nullptr)
			:name(name),size(size), parent(parent){}
		ObjectView New(void* ptr = nullptr) const{
			if (ptr == nullptr) {
				ptr = malloc(size);
				InitObject(ptr);
			}
			return { ptr , this };
		}
		template<typename T>
		T* New(T* ptr = nullptr) const{
			if (!IsChildOf<T>()) {
				return nullptr;
			}
			if (ptr == nullptr) {
				ptr = (T*)malloc(size);
			}
			InitObject(ptr);
			return ptr;
		}
		template<typename T>
		bool IsChildOf(bool bthis = false) const {
			constexpr UClass* cls = &TypeInfo<T>::StaticClass;
			if (bthis) {
				return cls == this;
			}
			const UClass* _parent = this;
			while (_parent != nullptr) {
				if (_parent == cls) {
					return true;
				}
				_parent = _parent->parent;
			}
			return false;
		}
		//unsafe if called by other, need check class && size
		virtual void InitObject(void* ptr)const {
			memset(ptr, 0, size);
		}
		virtual const FieldPtr* GetField(const Name& name)const {
			return nullptr; 
		}
	protected:
		template<uint32_t offset, typename T,typename U>    
		FieldPtr MakeMemberField(T U::* ptr, Name name) {
			FieldPtr::Data member = { offset };
			constexpr uint32_t flag = FIELD_MEMBER_FLAG | FIELD_ATTRIBUTE_FLAG;
			return { name, &TypeInfo<T>::StaticClass, {member}, flag };
		}
		template<typename R, typename ...Args>
		FieldPtr MakeMethodField(R (* ptr)(Args...), Name name) {
			FieldPtr::Data member = { *(void**)&ptr };
			constexpr uint32_t flag = FIELD_METHOD_FLAG;
			return { name, &TypeInfo<decltype(ptr)>::StaticClass, {member},flag };
		}
		template<typename T, typename R, typename ...Args>
		FieldPtr MakeMethodField(R(T::* ptr)(Args...), Name name) {
			FieldPtr::Data member = { *(void**)&ptr };
			constexpr uint32_t flag = FIELD_MEMBER_FLAG | FIELD_METHOD_FLAG;
			return { name, &TypeInfo<R(*)(Args...)>::StaticClass, {member},flag };
		}
	};
	template<_ReflCheck_Ctor T>
	class UClass_Auto : public UClass{
	public:
		using UClass::UClass;
	protected:
		void InitObject(void* ptr)const override {
			constexpr bool is_shallow_copyable = std::is_trivially_copyable<T>::value;
			if constexpr (!is_shallow_copyable){
				T obj{};
				std::construct_at((T*)ptr, obj);
			}
			else {
				memset(ptr, 0, size);
			}
		}
	public:
		static UClass_Auto<T> BuildClass() {
			auto cls = UClass_Auto<T>(type_name<T>().View(), sizeof(T));
			return cls;
		}
	};
	template<_ReflCheck_Ctor_NoUClass T>
	struct TypeInfo<T> {
		using UClass = UClass_Auto<T>;
		inline static UClass StaticClass = UClass::BuildClass();
	};
	template<typename R, typename... Args>
	class UMethod_Auto : public UClass {
		using UClass::UClass;
		using MethodType = R(*)(Args...);
	public:
		std::array<UClass*, sizeof...(Args) + 1> UList;
	public:
		static UMethod_Auto<R,Args...> BuildClass() {
			auto cls = UMethod_Auto<R, Args...>(type_name<MethodType>().View(), sizeof(MethodType));
			if constexpr(!std::is_same<R, void>::value) {
				cls.UList[0] = &TypeInfo<R>::StaticClass;
			}
			if (sizeof...(Args) > 0) {
				auto ptr = &cls.UList[1];
				(...,(*ptr = &TypeInfo<Args>::StaticClass, ptr++));
			}
			return cls;
		}
	};
	// 函数指针类型的偏特化
	template<typename R, typename... Args>
	struct TypeInfo<R(*)(Args...)> {
		using UClass = UMethod_Auto<R,Args...>;
		inline static UClass StaticClass = UClass::BuildClass();
	};
	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 + std::get<uint32_t>(cache->data));
			}
			return isChild;
		}
		auto field = cls->GetField(name);
		if (field) {
			cache = field;
			goto _cache_get;
		}
		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 + std::get<uint32_t>(cache->data)) = t;
			}
			return isChild;
		}
		auto field = cls->GetField(name);
		if (field) {
			cache = field;
			goto _cache_set;
		}
		return false;
	}
	template<typename ...Args>
	bool ObjectView::Invoke(const Name& name, Args... args)
	{
		auto field = cls->GetField(name);
		if (!field) {
			return false;
		}
		if (field->flag & FIELD_MEMBER_FLAG) {
			using MemberMethodType = void(*)(const void*, Args...);
			MemberMethodType fptr = (MemberMethodType)std::get<void*>(field->data);
			fptr(ptr, args...);
		}
		else {
			using MethodType = void(*)(Args...);
			MethodType fptr = (MethodType)std::get<void*>(field->data);
			fptr(args...);
		}
		return true;
	}
	template<typename R, typename ...Args>
	inline bool ObjectView::InvokeRet(const Name& name, R& ret, Args ...args)
	{
		auto field = cls->GetField(name);
		if (!field) {
			return false;
		}
		if (field->flag & FIELD_MEMBER_FLAG) {
			using MemberMethodType = R(*)(const void*, Args...);
			MemberMethodType fptr = (MemberMethodType)std::get<void*>(field->data);
			ret = fptr(ptr, args...);
		}
		else {
			using MethodType = R(*)(Args...);
			MethodType fptr = (MethodType)std::get<void*>(field->data);
			ret = fptr(args...);
		}
		return true;
	}
}