//
// Created by fotyev on 2018-07-13.
//

#ifndef ZENGINE_SCENEGRAPH_HPP
#define ZENGINE_SCENEGRAPH_HPP

#include "math.hpp"

#include <memory>
#include <vector>

#include "util/assert.hpp"

namespace scene {

    // Dim is matrix dimension (Dim x Dim)
    template <unsigned Dim>
    class node : public std::enable_shared_from_this<node<Dim>>
    {
    protected:
        using node_ptr = std::shared_ptr<node<Dim>>;
        using node_weak_ptr = std::weak_ptr<node<Dim>>;

        using node_list = std::vector<node_ptr>;
        using transform_t = qvm::mat<float, Dim, Dim>;

        node_list children;
        node_weak_ptr parent;
        transform_t transform, // transformation from parent subspace to this node subspace
                world_transform; // transformation from root node space to this node subspace
        //bool tr_invalidated = true;

        void update(const transform_t& parent_world_tr)
        {
            world_transform = parent_world_tr * transform;
            for(auto& child : children)
                child->update(world_transform);
        }

    public:
        node()
        {
            world_transform = transform = qvm::identity_mat<float, Dim>();
        }

        node(transform_t transform) : transform(std::move(transform)) {}

        virtual ~node()
        {}

        void add_child(node_ptr n)
        {
            // precond: n has no parent
            assume(n->parent.expired());

            n->parent = shared_from_this();
            n->update(world_transform);
            children.push_back(std::move(n));
        }
        // remove_child

        void set_transform(transform_t tr)
        {
            transform = std::move(tr);

            if(auto p = parent.lock())
                update(p->get_world_transform());
            else
                update(qvm::identity_mat<float, Dim>());
        }

        const transform_t& get_world_transform() const
        {
            return world_transform;
        }

    };

}

#endif //ZENGINE_SCENEGRAPH_HPP
