/************************************************************************************************************************
System module.

Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.system;

import bubel.ecs.entity;
import bubel.ecs.manager;

/************************************************************************************************************************
System contain data required to proper glue EntityManager with Systems.
System callbacks:
$(LIST
    * void onUpdate(EntitesData);
    * void onEnable() - called inside system.enable() function
    * void onDisable() - called inside system.disable() function
    * bool onBegin() - called inside manager.begin()
    * void onEnd() - called inside manager.end()
    * void onCreate() - called after registration inside registerSystem function
    * void onDestroy() - called during re-registration and inside manager destructor
    * void onAddEntity(EntitesData) - called for every entity which are assigned to system (by adding new entity or changing its components)
    * void onRemoveEntity(EntitiesData) - called for every entity removed from system update process
    * void onChangeEntity(EntitiesData) - called for every entity which components are changed but it was previously assigned to system
    * void handleEvent(Entity*, Event) - called for every event supported by system
)
*/
struct System
{

    /************************************************************************************************************************
    Check if system is enabled.
    */
    export bool enabled() nothrow @nogc
    {
        return m_enabled;
    }

    /************************************************************************************************************************
    Enable system. If actually it is enabled function do nothing.
    */
    export void enable() nothrow @nogc
    {
        if (!m_enabled && m_enable)
            (cast(void function(void*) nothrow @nogc) m_enable)(m_system_pointer);
        m_enabled = true;
    }

    /************************************************************************************************************************
    Disable system. If actually it is disabled function do nothing.
    */
    export void disable() nothrow @nogc
    {
        if (m_enabled && m_disable)
            (cast(void function(void*) nothrow @nogc) m_disable)(m_system_pointer);
        m_enabled = false;
    }

    /************************************************************************************************************************
    Get system priority.
    */
    export int priority() nothrow @nogc
    {
        return m_priority;
    }

    /************************************************************************************************************************
    Get if system will be executed during current frame. Should be checked after manager.begin(). Its value is setted as result of manager.onBegin() callback.
    */
    export bool willExecute() nothrow @nogc
    {
        return m_execute;
    }

    /************************************************************************************************************************
    Get system id.
    */
    export ushort id() nothrow @nogc
    {
        return m_id;
    }

    /************************************************************************************************************************
    Get system name.
    */
    export const(char)[] name() nothrow @nogc
    {
        return cast(const(char)[]) m_name;
    }

package:

    struct EventCaller
    {
        ushort id;
        void* callback;
    }

    ///should system be executed in current update?
    bool m_execute = true;
    ///system id
    ushort m_id;
    ///is system empty? Empty systems don't update entities, and is called once per update
    bool m_empty = false;

    ///should system update and catch events?
    bool m_enabled = false;
    ///system priority
    int m_priority;
    ///pointer to system implementation
    void* m_system_pointer;
    ///system pass index
    int m_pass;

    ///system name
    char[] m_name;

    ///required components
    ushort[] m_components;
    ///excluded components
    ushort[] m_excluded_components;
    ///optional components
    ushort[] m_optional_components;

    EntityManager.Job[] jobs;

    //System*[] m_dependencies;
    ushort[] m_read_only_components;
    ushort[] m_writable_components;

    ushort[] m_readonly_dependencies;
    ushort[] m_writable_dependencies;

    EntityManager.SystemCaller* m_any_system_caller;

    EventCaller[] m_event_callers;

    //void function(ref EntityManager.CallData data) m_update;
    void* m_update; ///workaroud for DMD bug with upper line
    void delegate() m_update_delegate;

    //void function(void* system_pointer) m_enable;
    //void function(void* system_pointer) m_disable;

    //void function(void* system_pointer) m_create;
    //void function(void* system_pointer) m_destroy;

    //void function(void* system_pointer) m_begin;
    //void function(void* system_pointer) m_end;

    void* m_enable;
    void* m_disable;

    void* m_create;
    void* m_destroy;

    void* m_begin;
    void* m_end;

    void* m_add_entity;
    void* m_remove_entity;
    void* m_change_entity;

    //void function(ref EntityManager.CallData data) m_initialize;
    //void function(ref EntityManager.CallData data) m_deinitilize;
    void* m_initialize;
    void* m_deinitilize;
}
