/*
 * Copyright (C) 1999-2020 Hartmut Seichter
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#ifndef PW_SCENE_ENTITY_HPP
#define PW_SCENE_ENTITY_HPP

#include <pw/core/globals.hpp>
#include <pw/scene/scene.hpp>

#include <string>
#include <vector>
#include <memory>

namespace pw {


class entity {
public:
    entity() = default;
    entity(const entity&) = default;
    entity(scene& s, const std::string &name = "");
    ~entity();

    /**
     * adds a component
     */
    template <typename T,typename... Args>
    T& add_component(Args&&... args) {
        return _registry->emplace<T>(this->_entity,std::forward<Args>(args)...);
    }

    /**
     * removes a component
     */
    template <typename T>
    void remove_component() {
        _registry->remove<T>(_entity);
    }


    /**
     * returns a component
     */
    template<typename T>
    T& get_component() const {
        return _registry->get<T>(this->_entity);
    }

    /**
     * gets or creates a component
     */
    template <typename T,typename... Args>
    T& get_or_create_component(Args&&... args) {
        return _registry->get_or_emplace<T>(this->_entity,std::forward<Args>(args)...);
    }

    /**
     * checks if this entity contains a component
     */
    template <typename T>
    bool has_component() const { return _registry->any_of(_entity); }
    // bool has_component() const { return _registry->has<T>(_entity);}

    /**
     * @brief destroy
     */
    void destroy();


    /**
     * @brief conversion to an entt entity
     */
    operator std::uint32_t() const { return static_cast<std::uint32_t>(_entity); }

    /**
     * @brief checks if this entity is valid
     * @return true of it belongs to a scene already
     */
    bool valid() const { return _registry && _registry->valid(_entity); }


    /**
     * @brief conversion operator
     */
    operator bool() const { return _registry && _entity != entt::null; }


    /**
     * @brief adds another child entity to this entity -> establishing a
     * @param child
     * @return true if the given child could be added to the hierarchy
     */
    bool add_child(entity &child);

    /**
     * @brief remove child entity from this parent
     * @param child
     * @return true if the given child could be remove from the hierarchy
     */
    bool remove_child(entity &child);

    /**
     * @brief returns the number of children on this entity
     * @return number of children
     */
    size_t child_count() const;

    /**
     * @brief set the name
     * @param n name to be set
     */
    void set_name(const std::string &n);

    /**
     * @brief name
     * @return name of the entity
     */
    std::string name();
private:

    using registry = entt::registry;

    std::shared_ptr<registry> _registry;
    entt::entity _entity { entt::null };

};

}

#include <pw/scene/components/relationship.hpp>
#include <pw/scene/components/transform.hpp>
#include <pw/scene/components/camera.hpp>

#endif
