#pragma once
#include "Child.h"
#include "Manager.h"
#include <time.h>
#include <memory>
#include <panda/unievent/http/Server.h>

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

struct Worker {
    enum class State { starting = 1, running = 2, restarting = 4, terminating = 8 };
    uint64_t id;
    State    state = State::starting;
    time_t   creation_time;
    time_t   activity_time;
    size_t   active_requests  = 0;
    size_t   total_requests   = 0;
    size_t   recent_requests  = 0;
    float    load_average     = 0;
    uint64_t replaced_by      = 0;
    time_t   termination_time = 0;

    virtual void fetch_state () = 0;
    virtual void terminate   () = 0;
    virtual void kill        () = 0;

    virtual ~Worker () {}
};

using WorkerPtr = std::unique_ptr<Worker>;
using Workers   = std::map<uint64_t, WorkerPtr>;

struct Mpm {
    using Config = Manager::Config;

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

    Mpm (const Config&, const LoopSP&, const LoopSP&);

    const LoopSP& get_loop   () const { return loop; }
    const Config& get_config () const { return config; }

    virtual void run  ();
    virtual void stop ();

    virtual void restart_workers ();

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

    virtual ~Mpm ();

protected:
    enum class State { initial, running, stopping, stopped };
    LoopSP   loop;
    LoopSP   worker_loop;
    Config   config;
    State    state = State::initial;
    TimerSP  check_timer;
    TimerSP  check_termination_timer;
    Workers  workers;
    uint64_t last_check_time = 0;
    uint64_t check_count = 0;

    virtual WorkerPtr create_worker     () = 0;
    void              worker_terminated (Worker*);
    virtual void      stopped           ();

private:
    std::vector<Worker*> get_workers (int states = 0);
    std::vector<Worker*> get_workers (Worker::State state) { return get_workers((int)state); }

    void check_workers              ();
    void fetch_state                ();
    void terminate_restared_workers ();
    void autorestart_workers        ();
    void kill_not_responding        ();
    void kill_not_terminated        ();

    Worker* spawn               ();
    void    terminate_workers   (uint32_t cnt);
    void    terminate_worker    (Worker*);
    void    kill_worker         (Worker*);
    Worker* restart_worker      (Worker*);
    void    restart_all_workers ();

    excepted<void, string> create_and_bind_sockets (Config&);
    void close_socket (sock_t);
};

}}}}