#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>

#include "stellar/packet.h"

enum session_state
{
    SESSION_STATE_INIT = 0,
    SESSION_STATE_OPENING = 1,
    SESSION_STATE_ACTIVE = 2,
    SESSION_STATE_CLOSING = 3,
    SESSION_STATE_DISCARD = 4,
    SESSION_STATE_CLOSED = 5,
    MAX_STATE = 6,
};

enum session_type
{
    SESSION_TYPE_TCP = 0x1,
    SESSION_TYPE_UDP = 0x2,
};

enum session_direction
{
    SESSION_DIRECTION_INBOUND = 0,
    SESSION_DIRECTION_OUTBOUND = 1,
};

enum flow_direction
{
    FLOW_DIRECTION_NONE = -1,
    FLOW_DIRECTION_C2S = 0,
    FLOW_DIRECTION_S2C = 1,
    MAX_FLOW_DIRECTION = 2,
};

enum closing_reason
{
    CLOSING_BY_TIMEOUT = 0x1,
    CLOSING_BY_LRU_EVICTED = 0x2,
    CLOSING_BY_PORT_REUSE_EVICTED = 0x3,
    CLOSING_BY_CLIENT_FIN = 0x4,
    CLOSING_BY_CLIENT_RST = 0x5,
    CLOSING_BY_SERVER_FIN = 0x6,
    CLOSING_BY_SERVER_RST = 0x7,
};

enum session_stat
{
    // raw packet
    STAT_RAW_PACKETS_RECEIVED,
    STAT_RAW_BYTES_RECEIVED,

    STAT_RAW_PACKETS_TRANSMITTED,
    STAT_RAW_BYTES_TRANSMITTED,

    STAT_RAW_PACKETS_DROPPED,
    STAT_RAW_BYTES_DROPPED,

    // duplicate packets
    STAT_DUPLICATE_PACKETS_BYPASS,
    STAT_DUPLICATE_BYTES_BYPASS,

    // injected packet
    STAT_INJECTED_PACKETS_FAILED,
    STAT_INJECTED_PACKETS_SUCCESS,
    STAT_INJECTED_BYTES_SUCCESS,

    // control packet
    STAT_CONTROL_PACKETS_RECEIVED, // TODO
    STAT_CONTROL_BYTES_RECEIVED,   // TODO

    STAT_CONTROL_PACKETS_TRANSMITTED,
    STAT_CONTROL_BYTES_TRANSMITTED,

    STAT_CONTROL_PACKETS_DROPPED,
    STAT_CONTROL_BYTES_DROPPED,

    // TCP segment
    STAT_TCP_SEGMENTS_RECEIVED,
    STAT_TCP_PAYLOADS_RECEIVED,

    STAT_TCP_SEGMENTS_EXPIRED,
    STAT_TCP_PAYLOADS_EXPIRED,

    STAT_TCP_SEGMENTS_RETRANSMIT,
    STAT_TCP_PAYLOADS_RETRANSMIT,

    STAT_TCP_SEGMENTS_OVERLAP,
    STAT_TCP_PAYLOADS_OVERLAP,

    STAT_TCP_SEGMENTS_NOSPACE,
    STAT_TCP_PAYLOADS_NOSPACE,

    STAT_TCP_SEGMENTS_INORDER,
    STAT_TCP_PAYLOADS_INORDER,

    STAT_TCP_SEGMENTS_REORDERED,
    STAT_TCP_PAYLOADS_REORDERED,

    STAT_TCP_SEGMENTS_BUFFERED,
    STAT_TCP_PAYLOADS_BUFFERED,

    STAT_TCP_SEGMENTS_RELEASED,
    STAT_TCP_PAYLOADS_RELEASED,

    MAX_STAT,
};

// realtime in milliseconds
enum session_timestamp
{
    SESSION_TIMESTAMP_START,
    SESSION_TIMESTAMP_LAST,
    MAX_TIMESTAMP,
};

struct session;

#define SESSION_SEEN_C2S_FLOW (1 << 0)
#define SESSION_SEEN_S2C_FLOW (1 << 1)
int session_is_symmetric(const struct session *sess, unsigned char *flag);

int session_has_duplicate_traffic(const struct session *sess);
enum session_type session_get_type(const struct session *sess);

enum session_state session_get_current_state(const struct session *sess);
const struct packet *session_get0_current_packet(const struct session *sess);

enum closing_reason session_get_closing_reason(const struct session *sess);
enum session_direction session_get_direction(const struct session *sess);
enum flow_direction session_get_current_flow_direction(const struct session *sess);
const struct packet *session_get_first_packet(const struct session *sess, enum flow_direction dir);
uint64_t session_get_id(const struct session *sess);
uint64_t session_get_timestamp(const struct session *sess, enum session_timestamp type);
uint64_t session_get_stat(const struct session *sess, enum flow_direction dir, enum session_stat stat);

const char *session_get0_readable_addr(const struct session *sess);

void session_set_discard(struct session *sess);

#ifdef __cplusplus
}
#endif
