#ifndef BSK_VIRTUAL_SCENE_H
#define BSK_VIRTUAL_SCENE_H

#include <memory>
#include <basilisk/render/shader.h>
#include <basilisk/resource/resourceServer.h>
#include <basilisk/engine/engine.h>

namespace bsk::internal {

template<typename NodeType, typename position_type, typename rotation_type, typename scale_type>
class VirtualScene {
protected:
    Engine* engine = nullptr;
    NodeType* root = nullptr;

    // store references to children as shared pointers so they are not destroyed when dereferenced from Python
    std::unordered_map<NodeType*, std::shared_ptr<NodeType>> childrenPythonMap;
    
public:
    VirtualScene(Engine* engine) : engine(engine) {
        root = new NodeType(this); // parent = nullptr
    }

    virtual ~VirtualScene() {
        clear();
    }

    inline Engine* getEngine() { return engine; }
    inline NodeType* getRoot() const { return root; }

    virtual void add(NodeType* node) = 0;
    virtual void add(std::shared_ptr<NodeType> node) = 0;
    virtual void remove(NodeType* node) = 0;
    virtual void remove(std::shared_ptr<NodeType> node) = 0;
    
    virtual Shader* getShader() = 0;

    /** Returns shared_ptr if the node was added via add(shared_ptr); otherwise nullptr. */
    std::shared_ptr<NodeType> findSharedNode(NodeType* node) const {
        auto it = childrenPythonMap.find(node);
        return it != childrenPythonMap.end() ? it->second : nullptr;
    }

protected:
    void clear() {
        if (root != nullptr) {
            // Remove Python-owned nodes from the tree so root's destructor won't delete them.
            // Important: nodes may be nested/reparented, so remove from their actual parent.
            for (auto& [node_ptr, node_sp] : childrenPythonMap) {
                if (node_ptr == nullptr) {
                    continue;
                }

                NodeType* parent = node_ptr->getParent();
                if (parent != nullptr) {
                    parent->remove(node_ptr);
                } else {
                    // Fallback for direct-root nodes or stale bookkeeping.
                    root->remove(node_ptr);
                }
            }
            delete root;
            root = nullptr;
        }
    }
};

}

#endif