#pragma once
#include <iosfwd>
#include <panda/excepted.h>
#include <panda/unievent/http/Server.h>

namespace panda { namespace unievent { namespace http { namespace manager {

struct Mpm;

extern log::Module panda_log_module;

struct Manager : Refcnt {
    enum class WorkerModel { PreFork, Thread };

    #ifdef _WIN32
        static const WorkerModel def_wm = WorkerModel::Thread;
    #else
        static const WorkerModel def_wm = WorkerModel::PreFork;
    #endif

    struct Config {
        Server::Config server;
        uint32_t       min_servers = 1;          // The minimum number of servers to keep running
        uint32_t       max_servers = 0;          // The maximum number of child servers to start. [number of cpu threads]
        uint32_t       min_spare_servers = 0;    // The minimum number of servers to have waiting for requests.
        uint32_t       max_spare_servers = 0;    // The maximum number of servers to have waiting for requests. [min_spare_server + min_servers, if min_spare_servers]
        float          min_load = 0;             // minimum average loop load on workers {0-1} [max_load/2 if max_load]
        float          max_load = 0;             // maximum average loop load on workers {0-1} [0.7 if !min_spare_servers]
        uint32_t       load_average_period = 3;  // number of seconds to collect load average for, on workers
        uint32_t       max_requests = 0;         // max number of the requests to process per one worker process [0=unlimited]
        uint32_t       min_worker_ttl = 60;      // Minimum number of seconds between starting and killing a worker
        float          check_interval = 1;       // interval between checking to see if we can kill off some waiting servers or if we need to spawn more workers
        uint32_t       activity_timeout = 0;     // kill worker if it's not responding for this number of seconds [0=disable]
        uint32_t       termination_timeout = 10; // kill worker if it's not terminated after this number of seconds [0=disable]
        WorkerModel    worker_model = def_wm;    // Multi-processing module type
        bool           force_worker_stop = true; // if true, stop worker's loop immediately after http server is gracefully stopped
                                                   // if false, do not stop loop and let it stop when no more active handle remains
    };

    using start_fptr        = void();
    using start_fn          = function<start_fptr>;
    using start_cd          = CallbackDispatcher<start_fptr>;
    using server_factory_fn = function<ServerSP(const Server::Config&, const LoopSP&)>;
    using spawn_fptr        = void(const ServerSP&);
    using spawn_fn          = function<spawn_fptr>;
    using spawn_cd          = CallbackDispatcher<spawn_fptr>;
    using request_cd        = decltype(std::declval<Server>().request_event);

    start_cd          start_event;
    server_factory_fn server_factory;
    spawn_cd          spawn_event;
    request_cd        request_event;

    Manager (const Config&, LoopSP = {}, LoopSP = {});
    Manager (Mpm*);

    const LoopSP& loop   () const;
    const Config& config () const;

    void run  ();
    void stop ();

    void restart_workers ();

    excepted<void, string> reconfigure (const Config&);

    virtual ~Manager ();

private:
    Mpm* mpm;
};

using ManagerSP = iptr<Manager>;

std::ostream& operator<< (std::ostream&, const Manager::Config&);

}}}}
