/*************************************************************************
	> File Name: http_stream.h
	> Author:
	> Mail:
	> Created Time: 2018年09月12日 星期三 15时56分05秒
 ************************************************************************/

#ifndef _HTTP_STREAM_H
#define _HTTP_STREAM_H

#include <tfe_http.h>
#include <tfe_stream.h>

#include <nghttp2/nghttp2.h>
#include <sys/queue.h>

typedef int (*event_cb)(struct http2_half_private * hf_private,
                        tfe_http_event ev, const unsigned char * data,
                        size_t len, void * user);

enum nghttp2_manage_stage
{
    MANAGE_STAGE_INIT,
    MANAGE_STAGE_READING,
    MANAGE_STAGE_COMPLETE,
};

struct data_t
{
    int gzip;
    uint8_t flags;
    ssize_t padlen;
    struct z_stream_st *inflater;
    struct z_stream_st *deflate;
	struct evbuffer * evbuf_body;
};

struct header_data{
    nghttp2_nv nv;
    struct http_field_name field;
    struct header_data *prev, *next;
};

struct http2_headers{
    int nvlen;
    int  complete;
    uint8_t flag;
    struct header_data *head, *tail;
};

struct http2_half_private
{
	/* PUBLIC STRUCTURE */
	struct tfe_http_half half_public;

	struct http_frame_session_ctx *frame_ctx;

	/* UNDERLAY BUFFER */
	int method_or_status;
    int by_stream;

	char * url_storage;
    struct data_t body;
    struct http2_headers headers;
    struct http2_headers promised;

    /*Read Cache**/
    int32_t stream_id;
    nghttp2_session *session;

	enum nghttp2_manage_stage body_state;
	enum nghttp2_manage_stage message_state;

    /* Callback Function */
    int (*event_cb)(struct http2_half_private * half_private, tfe_http_event ev,
                    const unsigned char * data, size_t len, void * user);
	/* Callback Function User Pointer */
	void * event_cb_user;
	/* Callback Function User Pointer Deleter */
	void (* event_cb_user_deleter)(void *);
};

struct cache_trapper_t{
    int spd_set;
	int spd_valid;
    int rse_set;
    //int set_cnt;
    //int flag_end;
    int spd_set_cnt;
    int spd_cnt;
	tfe_http_event spd_event;
};

struct h2_stream_data_t{
    struct tfe_http_session tfe_session;
    TAILQ_ENTRY(h2_stream_data_t) next;
    const struct tfe_stream *tf_stream;

    int32_t stream_id;
    nghttp2_session *session;

    struct cache_trapper_t cache;
	struct http_frame_session_ctx *frame_ctx;
    struct http2_half_private *req, *pangu_req;
    struct http2_half_private *resp, *pangu_resp;
};

struct tfe_session_info_t
{
	TAILQ_HEAD(list_head, h2_stream_data_t) list;

    int goaway;

    int32_t stream_id;

    enum tfe_stream_action stream_action;

    unsigned int thread_id;

    /** for down stream as server */
    nghttp2_session *as_server;
    /** for up stream as client*/
    nghttp2_session *as_client;

    const struct tfe_stream *tf_stream;
};

struct stream_tap_info_t
{
    /** IS PREEMPTED */
    unsigned int preempted;
    /** sess manage */
    struct tfe_session_info_t *session_info;
};

/*STREAM STATE **/
#define  TFE_NGHTTP2_DATA               0x00000001
#define  TFE_NGHTTP2_HEADERS            0x00000002
#define  TFE_NGHTTP2_PRIORITY           0x00000020
#define  TFE_NGHTTP2_RST_STREAM         0x00000040
#define  TFE_NGHTTP2_SETTINGS           0x00000080
#define  TFE_NGHTTP2_PUSH_PROMISE       0x00000100
#define  TFE_NGHTTP2_PING               0x00000200
#define  TFE_NGHTTP2_GOAWAY             0x00000400
#define  TFE_NGHTTP2_WINDOW_UPDATE      0x00000800
#define  TFE_NGHTTP2_CONTINUATION       0x00001000
#define  TFE_NGHTTP2_ALTSVC             0x00002000
#define  TFE_NGHTTP2_RESPONSE           0x00004000

#define ACTION_USER_DATA 3
#define NGHTTP2_NO_USER_ERROR  0x0e

static inline const struct http2_half_private *
nghttp2_to_half_private(const struct tfe_http_half * half_public)
{
	if(!half_public)
        return NULL;
	return container_of(half_public, struct http2_half_private, half_public);
}

static inline struct http2_half_private *
nghttp2_to_half_private(struct tfe_http_half * half_public)
{
	if(!half_public)
        return NULL;
	return container_of(half_public, struct http2_half_private, half_public);
}

static inline struct h2_stream_data_t *
nghttp2_to_stream_data(struct tfe_http_session * hs_public)
{
	if (hs_public == NULL)
        return NULL;
	return container_of(hs_public, struct h2_stream_data_t, tfe_session);
}

extern struct tfe_session_info_t* tfe_session_info_init();
extern void sess_data_ctx_fini(struct tfe_session_info_t *session_info,	const struct tfe_stream * stream,
                               unsigned int thread_id);

extern enum tfe_stream_action
detect_down_stream_protocol(struct tfe_session_info_t *session_info, const struct tfe_stream *tfe_stream,
					      	unsigned int thread_id, const unsigned char *data, size_t len);

extern enum tfe_stream_action
detect_up_stream_protocol(struct tfe_session_info_t *session_info, const struct tfe_stream *tfe_stream,
					      unsigned int thread_id, const unsigned char *data, size_t len);

enum tfe_stream_action
nghttp2_client_mem_send(struct tfe_session_info_t *session_info);

enum tfe_stream_action
nghttp2_server_mem_send(struct tfe_session_info_t *session_info);

/*for test **/

#endif
