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

#pragma once

#include "ConversionSettings.h"
#include "ContextInfo.h"
#include "IConversionContextManager.h"
#include "ISCW_UClass.h"
#include "ISCW_UnrealEngine.h"
#include "ISCW_UPropertyWrapper.h"
#include "ISCW_UObject.h"
#include "ISCW_UPackage.h"
#include "ISCW_UUserDefinedEnum.h"
#include "ISCW_UUserDefinedStruct.h"
#include <map>
#include <memory>
#include <string>
#include <set>
#include "SourcifyCoreDefines.h"

using std::dynamic_pointer_cast;
using std::enable_shared_from_this;
using std::map;
using std::set;
using std::shared_ptr;
using std::wstring;
using std::vector;

namespace Sourcify
{
    struct DependencyNode : enable_shared_from_this<DependencyNode>
    {
        enum class NodeType : uint8_t
        {
            Blueprint,
            Struct,
            Enum,
            Interface
        };

        struct ReferenceInfo
        {
            ReferenceInfo() {};
            SOURCIFY_CORE_DLL ReferenceInfo(ISCW_UnrealEnginePtr IUE, shared_ptr<DependencyNode> InNode, ISCW_UPropertyWrapperConstPtr InProperty);
			ReferenceInfo(shared_ptr<DependencyNode> InNode, ISCW_UPropertyWrapperConstPtr InProperty, const wstring& InDescription);

            shared_ptr<DependencyNode> Node;
            ISCW_UPropertyWrapperConstPtr Property;
            wstring Description;
        };

        // Objects that depend on this object
        vector<shared_ptr<DependencyNode>> Referencers;
        // Objects that this object depends on
        vector<ReferenceInfo> Referencees;
        NodeType Type;
        ISCW_UObjectPtr Object;
        wstring AssetIdentifier;
        FConversionSettings ConversionSettings;
        bool CanBeResourcified = false;
        FContextInfo ContextInfo;
        IConversionContextManager* CCM = nullptr;
        NodeErrorStatus ReadyStatus = NodeErrorStatus::Warning;
		ISCW_UnrealEnginePtr IUE;

    public:
        shared_ptr<DependencyNode> AddDependency(ISCW_UnrealEnginePtr InIUE, ISCW_UBlueprint& BP, ISCW_UPropertyWrapperConstPtr InProperty);
        shared_ptr<DependencyNode> AddDependency(ISCW_UnrealEnginePtr InIUE, ISCW_UUserDefinedStruct& Struct, ISCW_UPropertyWrapperConstPtr InProperty);
        shared_ptr<DependencyNode> AddDependency(ISCW_UnrealEnginePtr InIUE, ISCW_UUserDefinedEnum& Enum, ISCW_UPropertyWrapperConstPtr InProperty);
        shared_ptr<DependencyNode> AddDependency(ISCW_UnrealEnginePtr InIUE, ISCW_UClass& Class, ISCW_UPropertyWrapperConstPtr InProperty);
        bool CodeBuilderIsNeeded() const;
        bool ContextIsNeeded() const;
        SOURCIFY_CORE_DLL NodeErrorStatus IsReadyForOutput() const;
        SOURCIFY_CORE_DLL bool IsValid() const;
        SOURCIFY_CORE_DLL NodeErrorStatus UpdateReadyState();

    private:
        shared_ptr<DependencyNode> AddDependencyInternal(ISCW_UnrealEnginePtr InIUE, ISCW_UObject& InObject, ISCW_UPropertyWrapperConstPtr InProperty, NodeType InType);
        vector<shared_ptr<const DependencyNode>> CheckForNodesWithConflictingTypeName() const;
        const DependencyNode& GetRootNode() const;
        NodeErrorStatus SetReadyStatus(const NodeErrorStatus InReadyStatus);
    };
}