/*
   HARK Library
   Copyright (C) 2021 Honda Research Institute Japan Inc.
 */

#if !defined(_HARKLIB_DISPATCH_H_INCLUDED_)
#define _HARKLIB_DISPATCH_H_INCLUDED_ 1

#include "hark-lib/hark-lib.h"
#include "hark-lib/object.h"
#include "hark-lib/error.h"

#include <list>
#include <vector>
#include <sstream>

namespace hark
{
namespace dispatch
{

    class BaseDispatcher: public object
    {
    public:
        enum Status {
            Start = 0, Running, Wait, Finished,
        };

    public:
        BaseDispatcher();
        virtual ~BaseDispatcher();

    public:
        void add_node(BaseNode *node);
        virtual void initialize(Scheduler &scheduler);
        virtual void set_loop_condition(DataSource &source);

        virtual bool is_synchronous() const = 0;
        virtual void notify_data_arrival();
        virtual void notify_queue_vacancy();

        void trigger();
        void notify();
        virtual void operator () ();

        virtual bool is_idle() const;

        virtual void hook_initial();
        virtual void hook_start();
        virtual void hook_end(bool processed);
        virtual void hook_final();

        virtual void hook_data_arrival();
        virtual void hook_queue_vacancy();

    protected:
        mutable std::unique_ptr<Mutex> _mutex;
        mutable std::unique_ptr<ConditionVariable> _condvar;
        Scheduler *_scheduler;
        std::vector<BaseNode *>_nodes;
        Status _status;

        bool _loop_condition;
        std::unique_ptr<DataSink>_loop_condition_datasink;

        bool _triggered;
        size_t _loop_count;

        size_t _pred_count;
    };

    // 起動時に１回だけ実行される
    class OneShot: public BaseDispatcher
    {
        virtual void hook_initial();
        virtual void hook_data_arrival();
        virtual void hook_queue_vacancy();
        virtual bool is_synchronous() const;
    };

    // 他のノードの状態にかかわらず、繰り返し実行される
    // ただし、loop_condition が false になるか、実行メソッドが false を返すと終了する
    class Repeat: public BaseDispatcher
    {
        virtual void hook_initial();
        virtual void hook_end(bool processed);
        virtual bool is_synchronous() const;
    };

    // 他のノードの状態にかかわらず、繰り返し実行される
    // ただし、loop_condition が false になるか、実行メソッドが false を返すと終了する
    class TriggeredRepeat: public BaseDispatcher
    {
        virtual void hook_end(bool processed);
        virtual bool is_synchronous() const;
    };

    // 上流からのデータ到達をきっかけとして、繰り返し実行される
    // ただし、実行メソッドが false を返すと次のきっかけを待つ
    class TriggeredMultiShot: public BaseDispatcher
    {
        virtual void hook_end(bool processed);
        virtual bool is_synchronous() const;
    };

    class TriggeredOneShot: public BaseDispatcher
    {
        virtual bool is_synchronous() const;
    };

    class OfflineDispatcher: public BaseDispatcher
    {
        virtual bool is_synchronous() const;
    };

    class Scheduler: public object
    {
    public:
        Scheduler();
        virtual ~Scheduler();

    public:
        void request_quit(bool to_quit = true);
        bool to_quit() const;

        void initialize();
        void add_dispatcher(BaseDispatcher *dispatcher);
        void start_dispatchers();
        void wait_for_idle();

        void join();
        void notify();

        void handle_exception(std::exception &ex);

    private:
        std::unique_ptr<Mutex> _mutex;
        std::unique_ptr<ConditionVariable> _condvar;

        bool _to_quit;
        std::vector<BaseDispatcher *>_dispatchers;
        std::vector<Thread *>_dispatcher_threads;

        std::list<RuntimeError> _dispatcher_exceptions;
    };

    using Default = TriggeredOneShot;
}
}

#endif /* #if !defined(_HARKLIB_DISPATCH_H_INCLUDED_) */

/* end of file */
