// Copyright (c) 2024 Codemongous LLC. All Rights Reserved.

#include "SCW_UObject.h"

#include "SCW_FProperty.h"
#include "SourcifyCoreUEWrapperModule.h"
#include "UObject/Object.h"
#include "UObject/UnrealType.h"

using namespace Sourcify;

SCW_UObject::SCW_UObject(UObject* InObject) : Object(TWeakObjectPtr{ InObject })
{

}

ISCW_UObjectPtr SCW_UObject::ToUObjectPtr()
{
	return GetSharedThisAs<SCW_UObject>();
}

ISCW_UObjectConstPtr SCW_UObject::ToUObjectPtr() const
{
	return GetSharedThisAs<SCW_UObject>();
}

ISCW_UObjectPtr SCW_UObject::GetArchetype() const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*GetInternalObject()->GetArchetype());
}

void SCW_UObject::GetArchetypeInstances(vector<ISCW_UObjectPtr>& OutArchetypeInstances) const
{
	// Gathering pointers so safe to call UE native function then wrap those results
	TArray<UObject*> UEArchetypeInstances;
	GetInternalObject()->GetArchetypeInstances(UEArchetypeInstances);

	// Wrap results
	OutArchetypeInstances.clear();
	OutArchetypeInstances.reserve(UEArchetypeInstances.Num());
	for (UObject* UEArchetypeInstance : UEArchetypeInstances)
	{
		OutArchetypeInstances.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*UEArchetypeInstance));
	}
}

ISCW_UClassPtr SCW_UObject::GetClass() const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*GetInternalObject()->GetClass());
}

void SCW_UObject::GetDefaultSubobjects(vector<ISCW_UObjectPtr>& OutDefaultSubobjects) const
{
	// Gathering pointers so safe to call UE native function then wrap those results
	TArray<UObject*> UEDefaultSubobjects;
	GetInternalObject()->GetDefaultSubobjects(UEDefaultSubobjects);
	
	// Wrap results
	OutDefaultSubobjects.clear();
	OutDefaultSubobjects.reserve(UEDefaultSubobjects.Num());
	for (UObject* UEDefaultSubobject : UEDefaultSubobjects)
	{
		OutDefaultSubobjects.push_back(FSourcifyCoreUEWrapper::Get().Wrap(*UEDefaultSubobject));
	}
}

ISCW_UPackagePtr SCW_UObject::GetExternalPackage() const
{
	return GetInternalObject()->GetExternalPackage() ? FSourcifyCoreUEWrapper::Get().Wrap(*GetInternalObject()->GetExternalPackage()) : nullptr;
}

SCW_EObjectFlags SCW_UObject::GetFlags() const
{
	return static_cast<SCW_EObjectFlags>(GetInternalObject()->GetFlags());
}

wstring SCW_UObject::GetName() const
{
	return *GetInternalObject()->GetName();
}

ISCW_UPackagePtr SCW_UObject::GetPackage() const
{
	return GetInternalObject()->GetPackage() ? FSourcifyCoreUEWrapper::Get().Wrap(*GetInternalObject()->GetPackage()) : nullptr;
}

wstring SCW_UObject::GetPathName() const
{
	return *GetInternalObject()->GetPathName();
}

ISCW_UObjectPtr SCW_UObject::GetOuter() const
{
	return GetInternalObject()->GetOuter() ? FSourcifyCoreUEWrapper::Get().Wrap(*GetInternalObject()->GetOuter()) : nullptr;
}

ISCW_UObjectPtr SCW_UObject::GetOutermostObject() const
{
	return FSourcifyCoreUEWrapper::Get().Wrap(*GetInternalObject()->GetOutermost());
}

bool SCW_UObject::HasAllFlags(SCW_EObjectFlags FlagsToCheck) const
{
	return GetInternalObject()->HasAllFlags(static_cast<EObjectFlags>(FlagsToCheck));
}

bool SCW_UObject::HasAnyFlags(SCW_EObjectFlags FlagsToCheck) const
{
	return GetInternalObject()->HasAnyFlags(static_cast<EObjectFlags>(FlagsToCheck));
}

bool SCW_UObject::IsDefaultSubobject() const
{
	return GetInternalObject()->IsDefaultSubobject();
}

bool SCW_UObject::IsEditorOnly() const
{
	return GetInternalObject()->IsEditorOnly();
}

bool SCW_UObject::IsNative() const
{
	return GetInternalObject()->IsNative();
}

bool SCW_UObject::MarkPackageDirty() const
{
	return GetInternalObject()->MarkPackageDirty();
}

void SCW_UObject::Modify(bool bAlwaysMarkDirty)
{
	GetInternalObject()->Modify(bAlwaysMarkDirty);
}

void SCW_UObject::PostEditChangeProperty(ISCW_FPropertyPtr InProperty, SCW_EPropertyChangeType InChangeType/* = SCW_EPropertyChangeType::Unspecified*/)
{
	FPropertyChangedEvent ChangeEvent { dynamic_cast<SCW_FProperty*>(InProperty.get())->ToFProperty(), static_cast<EPropertyChangeType::Type>(InChangeType) };
	GetInternalObject()->PostEditChangeProperty(ChangeEvent);
}

void SCW_UObject::PreEditChange(ISCW_FPropertyPtr PropertyAboutToChange)
{
	GetInternalObject()->PreEditChange(dynamic_cast<SCW_FProperty*>(PropertyAboutToChange.get())->ToFProperty());
}

void SCW_UObject::PostReinitProperties()
{
	GetInternalObject()->PostReinitProperties();
}

bool SCW_UObject::Rename(const wchar_t* NewName/* = nullptr*/, shared_ptr<ISCW_UObject> NewOuter/* = nullptr*/, SCW_ERenameFlags Flags/* = SCW_ERenameFlags::EREN_None*/)
{
	return GetInternalObject()->Rename(NewName, NewOuter ? dynamic_cast<SCW_UObject*>(NewOuter.get())->ToUObjectRawPtr() : nullptr, static_cast<ERenameFlags>(Flags));
}

void SCW_UObject::SetFlags(SCW_EObjectFlags FlagsToSet)
{
	GetInternalObject()->SetFlags(static_cast<EObjectFlags>(FlagsToSet));
}

bool SCW_UObject::ISCW_Equals(const ISCW_UObject& Other) const
{
	return GetInternalObject() == dynamic_cast<const SCW_UObject*>(&Other)->ToUObjectRawPtr();
}

bool SCW_UObject::ISCW_Equals(const shared_ptr<const ISCW_UObject> Other) const
{
	return GetInternalObject() == (Other ? dynamic_cast<const SCW_UObject*>(Other.get())->ToUObjectRawPtr() : nullptr);
}

size_t SCW_UObject::ISCW_GetHash() const
{
	return std::hash<UObject*>{}(GetInternalObject());
}

bool SCW_UObject::Less(const ISCW_UObject& Other) const
{
	return GetInternalObject() < dynamic_cast<const SCW_UObject*>(&Other)->ToUObjectRawPtr();
}

void SCW_UObject::MakeInternalStrongObjectPointer()
{
	Object = TStrongObjectPtr<UObject>{ GetInternalObject() };
}

UObject* SCW_UObject::ToUObjectRawPtr() const
{
	return GetInternalObject();
}

UObject* SCW_UObject::GetInternalObject() const
{
	if (std::holds_alternative<TStrongObjectPtr<UObject>>(Object))
	{
		return std::get<TStrongObjectPtr<UObject>>(Object).Get();
	}

	auto WeakObjectPointer = std::get<TWeakObjectPtr<UObject>>(Object);
	//check(WeakObjectPointer.IsValid());
	return WeakObjectPointer.Get();
}
