/**
 * @file	app_bus.hpp
 * @author	Jichan (development@jc-lab.net / http://ablog.jc-lab.net/ )
 * @date	2019/10/23
 * @copyright Copyright (C) 2019 jichan.\n
 *            This software may be modified and distributed under the terms
 *            of the Apache License 2.0.  See the LICENSE file for details.
 */

#ifndef __NODE_APP_MAIN_APP_BUS_HPP__
#define __NODE_APP_MAIN_APP_BUS_HPP__

#include <uv.h>
#include <node.h>

#include <memory>
#include <mutex>
#include <map>
#include <list>
#include <functional>

#include "rapidjson/document.h"

namespace node_app {

class AppBus {
 public:
  typedef std::function<void(const rapidjson::Document &)> EventHandler_t;
  typedef std::function<void(const rapidjson::Document &retval, bool is_throw)> ResponseHandler_t;
  typedef std::function<void(const rapidjson::Document &args, ResponseHandler_t &response)> RequestHandler_t;

  AppBus();
  void init(uv_loop_t *loop);
  void registerToContext(uv_loop_t *loop, v8::Local<v8::Context> context, const char *globalKey);

  void on(const char *event_key, EventHandler_t handler, uv_loop_t *loop = NULL);
  void emit(const char *event_key, rapidjson::Value &args, bool single_argument = false);
  void emit(const char *event_key);

  void onRequest(const char *key, RequestHandler_t handler, uv_loop_t *loop = NULL);

 private:
  uv_loop_t *loop_;

  struct EventMessage {
    rapidjson::Document args;

    EventMessage()
        : args(rapidjson::kArrayType) {
    }
  };

  struct RequestMessage {
    std::string reqid;
    rapidjson::Document args;

    RequestMessage(const std::string &_reqid)
        : reqid(_reqid), args(rapidjson::kArrayType) {
    }
  };

  struct EventHandlerHolder {
    uv_loop_t *loop_;
    EventHandlerHolder(uv_loop_t *loop) : loop_(loop) {}
    virtual ~EventHandlerHolder() {}
    uv_loop_t *loop() const { return loop_; }
    virtual void handle(std::shared_ptr<EventMessage> message) = 0;
  };

  struct RequestHandlerHolder {
    uv_loop_t *loop_;
    RequestHandlerHolder(uv_loop_t *loop) : loop_(loop) {}
    virtual ~RequestHandlerHolder() {}
    uv_loop_t *loop() const { return loop_; }
    virtual void handle(std::shared_ptr<RequestMessage> message) = 0;
  };

  struct EventHolder {
    std::recursive_timed_mutex mutex;
    std::list<std::shared_ptr<EventHandlerHolder>> list;
    std::map<std::string, std::shared_ptr<RequestHandlerHolder>> reqs;
  };

  class EventLock;

  struct EventEmitTask;

  struct HostEventHandlerHolder;
  struct V8EventHandlerHolder;
  struct HostRequestHandlerHolder;
  struct V8RequestHandlerHolder;

  std::recursive_timed_mutex mutex_;
  std::map<std::string, std::unique_ptr<EventHolder>> event_map_;

  static void v8ThrowError(const char *msg);
  static void v8CallbackOn(const v8::FunctionCallbackInfo<v8::Value> &info);
  static void v8CallbackEmit(const v8::FunctionCallbackInfo<v8::Value> &info);

  void requestFromNode(const v8::FunctionCallbackInfo<v8::Value> &info);

  void emitImpl(const char *event_key, std::shared_ptr<EventMessage> message);
};
}

#endif //__NODE_APP_MAIN_APP_BUS_HPP__