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

#if !defined(_HARKLIB_NETWORK_H_INCLUDED_)
#define _HARKLIB_NETWORK_H_INCLUDED_ 1

#include "hark-lib/hark-lib.h"
#include "hark-lib/node.h"
#include "hark-lib/dispatch.h"
#include <typeinfo>

namespace hark
{
    class NetworkDef: public NodeDef {
    public:
        NetworkDef();
        NetworkDef(const std::string &name);
        virtual ~NetworkDef();

        NetworkDef(const NetworkDef &src) = delete;
        NetworkDef(NetworkDef &&src) = delete;

    public:
        virtual void build(Network &network, DataSourceMap &input, DataSinkMap &output) = 0;
        virtual void build_p(Network *network, DataSourceMap *input, DataSinkMap *output) {
            this->build(*network, *input, *output);
        }
        virtual void reset();
        virtual bool operator () (size_t count, InputDataBundle &input, OutputDataBundle &output);

    };

    class Network: public BaseNode
    {
    public:
        Network(NetworkDef *network_def, const std::string &name);
        Network(NetworkDef *network_def, dispatch::BaseDispatcher *dispatcher, const std::string &name);

        Network(const Network &src) = delete;
        Network(Network &&src) = delete;

        virtual ~Network() = default; //{ std::cout << "hark::Network::dtor: this=" << (void *)this << std::endl; }

    public:
        // Create anonymous network instance
        template<typename N, class... Args>
        static Network*create(const std::string &name, Args... args)
        {
            Network *v = new Network(new N(args...), name);
            return v;
        }

        // Create node instance with default name
        template<typename NODE, typename DEF, typename DISPATCH = dispatch::Default>
        NODE&create()
        {
            NODE *nv = new NODE(new DEF(), new DISPATCH());
            this->_nodes.push_back(std::unique_ptr<BaseNode>(nv));
            return *nv;
        }

        // Create node instance with designated name
        template<typename NODE, typename DEF, typename DISPATCH = dispatch::Default, class... Args>
        NODE&create(const std::string &name, Args... args)
        {
            NODE *nv = new NODE(new DEF(args...), new DISPATCH(), name);
            this->_nodes.push_back(std::unique_ptr<BaseNode>(nv));
            return *nv;
        }

    public:
        // virtual Network &add_input(Input &&input);

        void execute(); // DataBundle &input, DataBundle &output);
        void stop();

        virtual DataSource &operator [] (const std::string &name);
        Network &operator << (Input &&input);

        virtual void dump_graph(std::ostream &os, bool with_headers = false) const;
        virtual bool try_process(){
            return false;
        }

        BaseNode &create_node(NodeDef *nodeDef, dispatch::BaseDispatcher *baseDispatcher, const std::string &name);
        Network &create_network(NetworkDef *networkDef, dispatch::BaseDispatcher *baseDispatcher, const std::string &name);

        BaseNode *query_node(const std::string &name) const;

    private:
        void initialize();
        void schedule(dispatch::Scheduler &scheduler);
        void reset();
        void dispatch();

    private:
        NetworkDef *_networkdef; // lifeitme is managed by Node::_nodedef
        std::vector<std::unique_ptr<BaseNode>>_nodes;
        std::vector<std::unique_ptr<dispatch::BaseDispatcher>>_dispatchers;

        dispatch::Scheduler _scheduler;

        bool _built = false;
    };

}

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

/* end of file */
