#ifndef HV_EVENT_H_
#define HV_EVENT_H_

#include "hloop.h"
#include "iowatcher.h"
#include "rudp.h"

#include "hbuf.h"
#include "hmutex.h"

#include "array.h"
#include "list.h"
#include "heap.h"
#include "queue.h"

#define HLOOP_READ_BUFSIZE          8192        // 8K
#define READ_BUFSIZE_HIGH_WATER     65536       // 64K
#define WRITE_BUFSIZE_HIGH_WATER    (1U << 23)  // 8M
#define MAX_READ_BUFSIZE            (1U << 24)  // 16M
#define MAX_WRITE_BUFSIZE           (1U << 24)  // 16M

// hio_read_flags
#define HIO_READ_ONCE           0x1
#define HIO_READ_UNTIL_LENGTH   0x2
#define HIO_READ_UNTIL_DELIM    0x4

ARRAY_DECL(hio_t*, io_array);
ARRAY_DECL(hsignal_t*, signal_array);
QUEUE_DECL(hevent_t, event_queue);

struct hloop_s {
    uint32_t    flags;
    hloop_status_e status;
    uint64_t    start_ms;       // ms
    uint64_t    start_hrtime;   // us
    uint64_t    end_hrtime;
    uint64_t    cur_hrtime;
    uint64_t    loop_cnt;
    long        pid;
    long        tid;
    void*       userdata;
//private:
    // events
    uint32_t                    intern_nevents;
    uint32_t                    nactives;
    uint32_t                    npendings;
    // pendings: with priority as array.index
    hevent_t*                   pendings[HEVENT_PRIORITY_SIZE];
    // signals
    struct signal_array         signals;
    uint32_t                    nsignals;
    // idles
    struct list_head            idles;
    uint32_t                    nidles;
    // timers
    struct heap                 timers;     // monotonic time
    struct heap                 realtimers; // realtime
    uint32_t                    ntimers;
    // ios: with fd as array.index
    struct io_array             ios;
    uint32_t                    nios;
    // one loop per thread, so one readbuf per loop is OK.
    hbuf_t                      readbuf;
    void*                       iowatcher;
    // custom_events
    int                         eventfds[2];
    event_queue                 custom_events;
    hmutex_t                    custom_events_mutex;
};

uint64_t hloop_next_event_id();

struct hidle_s {
    HEVENT_FIELDS
    uint32_t    repeat;
//private:
    struct list_node node;
};

#define HTIMER_FIELDS                   \
    HEVENT_FIELDS                       \
    uint32_t    repeat;                 \
    uint64_t    next_timeout;           \
    struct heap_node node;

struct htimer_s {
    HTIMER_FIELDS
};

struct htimeout_s {
    HTIMER_FIELDS
    uint32_t    timeout;                \
};

struct hperiod_s {
    HTIMER_FIELDS
    int8_t      minute;
    int8_t      hour;
    int8_t      day;
    int8_t      week;
    int8_t      month;
};

QUEUE_DECL(offset_buf_t, write_queue);
// sizeof(struct hio_s)=416 on linux-x64
struct hio_s {
    HEVENT_FIELDS
    // flags
    unsigned    ready       :1;
    unsigned    connected   :1;
    unsigned    closed      :1;
    unsigned    accept      :1;
    unsigned    connect     :1;
    unsigned    connectex   :1; // for ConnectEx/DisconnectEx
    unsigned    recv        :1;
    unsigned    send        :1;
    unsigned    recvfrom    :1;
    unsigned    sendto      :1;
    unsigned    close       :1;
    unsigned    alloced_readbuf :1; // for hio_alloc_readbuf
    unsigned    alloced_ssl_ctx :1; // for hio_new_ssl_ctx
// public:
    hio_type_e  io_type;
    uint32_t    id; // fd cannot be used as unique identifier, so we provide an id
    int         fd;
    int         error;
    int         events;
    int         revents;
    struct sockaddr*    localaddr;
    struct sockaddr*    peeraddr;
    uint64_t            last_read_hrtime;
    uint64_t            last_write_hrtime;
    // read
    fifo_buf_t          readbuf;
    unsigned int        read_flags;
    // for hio_read_until
    union {
        unsigned int    read_until_length;
        unsigned char   read_until_delim;
    };
    uint32_t            max_read_bufsize;
    uint32_t            small_readbytes_cnt; // for readbuf autosize
    // write
    struct write_queue  write_queue;
    hrecursive_mutex_t  write_mutex; // lock write and write_queue
    uint32_t            write_bufsize;
    uint32_t            max_write_bufsize;
    // callbacks
    hread_cb    read_cb;
    hwrite_cb   write_cb;
    hclose_cb   close_cb;
    haccept_cb  accept_cb;
    hconnect_cb connect_cb;
    // timers
    int         connect_timeout;    // ms
    int         close_timeout;      // ms
    int         read_timeout;       // ms
    int         write_timeout;      // ms
    int         keepalive_timeout;  // ms
    int         heartbeat_interval; // ms
    hio_send_heartbeat_fn heartbeat_fn;
    htimer_t*   connect_timer;
    htimer_t*   close_timer;
    htimer_t*   read_timer;
    htimer_t*   write_timer;
    htimer_t*   keepalive_timer;
    htimer_t*   heartbeat_timer;
    // upstream
    struct hio_s*       upstream_io;    // for hio_setup_upstream
    // unpack
    unpack_setting_t*   unpack_setting; // for hio_set_unpack
    // ssl
    void*       ssl;        // for hio_set_ssl
    void*       ssl_ctx;    // for hio_set_ssl_ctx
    char*       hostname;   // for hssl_set_sni_hostname
    // context
    void*       ctx; // for hio_context / hio_set_context
// private:
#if defined(EVENT_POLL) || defined(EVENT_KQUEUE)
    int         event_index[2]; // for poll,kqueue
#endif

#ifdef EVENT_IOCP
    void*       hovlp;          // for iocp/overlapio
#endif

#if WITH_RUDP
    rudp_t          rudp;
#if WITH_KCP
    kcp_setting_t*  kcp_setting;
#endif
#endif
};
/*
 * hio lifeline:
 *
 * fd =>
 * hio_get => HV_ALLOC_SIZEOF(io) => hio_init => hio_ready
 *
 * hio_read  => hio_add(HV_READ) => hio_read_cb
 * hio_write => hio_add(HV_WRITE) => hio_write_cb
 * hio_close => hio_done => hio_del(HV_RDWR) => hio_close_cb
 *
 * hloop_stop => hloop_free => hio_free => HV_FREE(io)
 */
void hio_init(hio_t* io);
void hio_ready(hio_t* io);
void hio_done(hio_t* io);
void hio_free(hio_t* io);
uint32_t hio_next_id();

void hio_accept_cb(hio_t* io);
void hio_connect_cb(hio_t* io);
void hio_handle_read(hio_t* io, void* buf, int readbytes);
void hio_read_cb(hio_t* io, void* buf, int len);
void hio_write_cb(hio_t* io, const void* buf, int len);
void hio_close_cb(hio_t* io);

void hio_del_connect_timer(hio_t* io);
void hio_del_close_timer(hio_t* io);
void hio_del_read_timer(hio_t* io);
void hio_del_write_timer(hio_t* io);
void hio_del_keepalive_timer(hio_t* io);
void hio_del_heartbeat_timer(hio_t* io);

static inline void hio_use_loop_readbuf(hio_t* io) {
    hloop_t* loop = io->loop;
    if (loop->readbuf.len == 0) {
        loop->readbuf.len = HLOOP_READ_BUFSIZE;
        HV_ALLOC(loop->readbuf.base, loop->readbuf.len);
    }
    io->readbuf.base = loop->readbuf.base;
    io->readbuf.len  = loop->readbuf.len;
}
static inline bool hio_is_loop_readbuf(hio_t* io) {
    return io->readbuf.base == io->loop->readbuf.base;
}
static inline bool hio_is_alloced_readbuf(hio_t* io) {
    return io->alloced_readbuf;
}
void hio_alloc_readbuf(hio_t* io, int len);
void hio_free_readbuf(hio_t* io);
void hio_memmove_readbuf(hio_t* io);

#define EVENT_ENTRY(p)          container_of(p, hevent_t, pending_node)
#define IDLE_ENTRY(p)           container_of(p, hidle_t,  node)
#define TIMER_ENTRY(p)          container_of(p, htimer_t, node)

#define EVENT_ACTIVE(ev) \
    if (!ev->active) {\
        ev->active = 1;\
        ev->loop->nactives++;\
    }\

#define EVENT_INACTIVE(ev) \
    if (ev->active) {\
        ev->active = 0;\
        ev->loop->nactives--;\
    }\

#define EVENT_PENDING(ev) \
    do {\
        if (!ev->pending) {\
            ev->pending = 1;\
            ev->loop->npendings++;\
            hevent_t** phead = &ev->loop->pendings[HEVENT_PRIORITY_INDEX(ev->priority)];\
            ev->pending_next = *phead;\
            *phead = (hevent_t*)ev;\
        }\
    } while(0)

#define EVENT_ADD(loop, ev, cb) \
    do {\
        ev->loop = loop;\
        ev->event_id = hloop_next_event_id();\
        ev->cb = (hevent_cb)cb;\
        EVENT_ACTIVE(ev);\
    } while(0)

#define EVENT_DEL(ev) \
    do {\
        EVENT_INACTIVE(ev);\
        if (!ev->pending) {\
            HV_FREE(ev);\
        }\
    } while(0)

#define EVENT_RESET(ev) \
    do {\
        ev->destroy = 0;\
        EVENT_ACTIVE(ev);\
        ev->pending = 0;\
    } while(0)

#endif // HV_EVENT_H_
