/*************************************************************************
	> File Name: http2_common.cpp
	> Author:
	> Mail:
	> Created Time: 2018年09月21日 星期五 13时59分03秒
 ************************************************************************/

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include <tfe_utils.h>
#include <tfe_stream.h>
#include <event2/buffer.h>

#include <http2_common.h>

RTLogInit2Data logging_sc_lid = {

};

RTLogInit2Data *logger()
{
    return &logging_sc_lid;
};

Http2Plugin g_http2_plugin = {

};

Http2Plugin *http2_plugin()
{
	return &g_http2_plugin;
}

int
http2_header_str_to_val(const char *str, size_t slen, const char * map[], unsigned int map_size)
{
	size_t normlen = 0;
	for (unsigned int i = 2; i < map_size; i++)
	{
		normlen = strlen(map[i]);
		if ( (slen == normlen) && !strncasecmp(str, map[i], normlen))
		{
			return i;
		}
	}

	// in http  [TFE_HTTP_UNKNOWN_FIELD] = NULL;      [TFE_HTTP_HOST] = "Host",
	// in http2 [TFE_HTTP_UNKNOWN_FIELD] = "unknown"; [TFE_HTTP_HOST] = ":authority"
	normlen = strlen(":authority");
	if ( (slen == normlen) && !strncasecmp(str, ":authority", normlen))
	{
		return TFE_HTTP_HOST;
	} else {
		return TFE_HTTP_UNKNOWN_FIELD;
	}
}

const char*
http2_header_val_to_str(unsigned int val, const char * map[], unsigned int map_size)
{
	const static char * host = ":authority";
	const static char * unknown = "unknown";

	if (val < map_size && val != TFE_HTTP_HOST && val != TFE_HTTP_UNKNOWN_FIELD)
	{
		return map[val];
	}

	// in http  [TFE_HTTP_UNKNOWN_FIELD] = NULL;      [TFE_HTTP_HOST] = "Host",
	// in http2 [TFE_HTTP_UNKNOWN_FIELD] = "unknown"; [TFE_HTTP_HOST] = ":authority"
	if (val == TFE_HTTP_HOST)
	{
		return host;
	}
	else
	{
		return unknown;
	}
}

const char *
try_val_to_str_idx(const unsigned int val, const struct value_string *vs, int *idx)
{
    int  i = 0;
    if (idx == NULL){
        goto finish;
    }

    if(vs) {
        while (vs[i].strptr) {
            if (vs[i].value == val) {
                *idx = i;
                return(vs[i].strptr);
            }
            i++;
        }
    }

finish:
    *idx = -1;
    return NULL;
}

/* Find the index of a string in a value_string, or -1 when not present */
int
str_to_val_idx(const char *val, const struct value_string *vs)
{
    int i = 0;

    if(vs) {
        while (vs[i].strptr) {
            if (strcmp(vs[i].strptr, val) == 0) {
                return i;
            }
            i++;
        }
    }
    return 0;
}

const char*
val_to_str(unsigned int val, const struct value_string *vs)
{
    int ignore_me;

    return try_val_to_str_idx(val, vs, &ignore_me);
}

int
str_to_val(const char *val, const struct value_string *vs)
{
	return str_to_val_idx(val, vs);
}

int inflate_init(struct z_stream_st **strm, int gzip)
{
	if (*strm != NULL)
		return Z_OK;

	*strm = ALLOC(struct z_stream_st, 1);
	assert(*strm);

	/* ZSTREAM */
	(*strm)->zst.zalloc = NULL;
	(*strm)->zst.zfree = NULL;
	(*strm)->zst.opaque = NULL;
	(*strm)->zst.avail_in = 0;
	(*strm)->zst.next_in = Z_NULL;

	// Z_OK stand for 0; Z_ERRNO stand for -1.
	int ret = Z_ERRNO;
	if (gzip == HTTP2_CONTENT_ENCODING_GZIP)
		ret = inflateInit2(&((*strm)->zst), MAX_WBITS + 16);
	else if (gzip == HTTP2_CONTENT_ENCODING_DEFLATE)
		ret = inflateInit2(&((*strm)->zst), -MAX_WBITS);
	else if (gzip == HTTP2_CONTENT_ENCODING_BR){
		(*strm)->brdec_state = BrotliDecoderCreateInstance(NULL, NULL, NULL);
		if ((*strm)->brdec_state != NULL)
			ret = Z_OK;
	}

	if (ret != Z_OK)
		FREE(strm);

	return ret;
}

static int inflate_br_read(struct z_stream_st **strm, const uint8_t *source, int len,
				    char **dest, int *outlen)
{
#define CHUNK	 (1024 * 1024 * 4)
    unsigned  char  out[CHUNK];
    int  totalsize  =  0 ,ret = -1;
    size_t available_out;
    unsigned char * next_out;

	size_t available_in = len;
    const unsigned char * next_in = source;

    for ( ; ; ){
        available_out = CHUNK;
        next_out = out;

        ret = BrotliDecoderDecompressStream((*strm)->brdec_state, &available_in, &next_in,
            								&available_out, &next_out, 0);

        size_t have = CHUNK - available_out;
        if (have > 0){
			totalsize  +=  have;
			*dest  =  (char *)realloc(*dest,totalsize);
			memcpy(*dest  +  totalsize  -  have, out, have);
			*outlen = have;
        }

        if (ret == BROTLI_DECODER_RESULT_SUCCESS || ret == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT){
            ret = 1;
			goto finish;
        }

        if (ret == BROTLI_DECODER_RESULT_ERROR){
			ret = -1;
			goto finish;
        }
        assert(ret == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
    }
finish:
	return ret;
}

static int inflate_gzip_read(struct z_stream_st **strm, char **dest, int *outlen)
{
	#define CHUNK    (1024 * 1024 * 4)
    int  ret = -1;
    unsigned  have;
    unsigned  char  out[CHUNK];
    int  totalsize  =  0;

	 /*  run  inflate()  on  input  until  output  buffer  not  full  */
    do  {
        (*strm)->zst.avail_out  =  CHUNK;
        (*strm)->zst.next_out  =  out;
        ret  =  inflate(&((*strm)->zst),  Z_NO_FLUSH);
        switch  (ret)  {
			case Z_STREAM_ERROR:
			ret = Z_STREAM_ERROR;
            case  Z_NEED_DICT:
            ret  =  Z_DATA_ERROR;          /*  and  fall  through  */
            case  Z_DATA_ERROR:
            case  Z_MEM_ERROR:
            inflateEnd(&((*strm)->zst));
            return  ret;
        }
        have  =  CHUNK  -  (*strm)->zst.avail_out;
        totalsize  +=  have;
        *dest  =  (char *)realloc(*dest,totalsize);
        memcpy(*dest  +  totalsize  -  have,out,have);
		*outlen = have;
    }  while  ((*strm)->zst.avail_out  ==  0);

    return  ret;
}

int inflate_read(const uint8_t *source,int len,char **dest, int *outlen,
				 struct z_stream_st **strm, int encode)
{
    int  ret = -1;

	ret = inflate_init(strm, encode);
	if (ret != Z_OK){
		return ret;
	}

	if (encode == HTTP2_CONTENT_ENCODING_GZIP ||
		encode == HTTP2_CONTENT_ENCODING_DEFLATE){
		(*strm)->zst.avail_in  =  len;
		(*strm)->zst.next_in   =  (Bytef *)source;
		ret = inflate_gzip_read(strm, dest, outlen);
	}
	if (encode == HTTP2_CONTENT_ENCODING_BR){
		ret = inflate_br_read(strm, source, len, dest, outlen);
	}
    return  ret;
}

int deflate_init(struct z_stream_st **strm, int gzip)
{
	if (*strm != NULL)
		return Z_OK;

	int ret = 0; // 0 stand for Z_OK

	*strm = ALLOC(struct z_stream_st, 1);
	assert(*strm);

	if (gzip == HTTP2_CONTENT_ENCODING_GZIP ||
		gzip == HTTP2_CONTENT_ENCODING_DEFLATE){
		(*strm)->zst.zalloc = (alloc_func)0;
		(*strm)->zst.zfree = (free_func)0;
		(*strm)->zst.opaque = (voidpf)0;
		(*strm)->zst.avail_in = 0;
		(*strm)->zst.next_in = Z_NULL;

		int wbits = 0;
		if (gzip == HTTP2_CONTENT_ENCODING_GZIP){
			wbits = MAX_WBITS + 16;
		}
		if (gzip == HTTP2_CONTENT_ENCODING_DEFLATE){
			wbits = -MAX_WBITS;
		}
		ret = deflateInit2(&((*strm)->zst), Z_DEFAULT_COMPRESSION,
						   Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY);

	}
	if (gzip == HTTP2_CONTENT_ENCODING_BR){
		(*strm)->brenc_state = BrotliEncoderCreateInstance(NULL, NULL, NULL);
        if ( (*strm)->brenc_state == NULL)
			ret = -1;

		BrotliEncoderSetParameter((*strm)->brenc_state, BROTLI_PARAM_QUALITY, 3);
	}
	if (ret != Z_OK)
		FREE(strm);

	return ret;
}

static int deflate_br_write(struct z_stream_st **strm,
    				 const unsigned char * source, size_t slen,
    				 struct evbuffer * evbuf, int end)
{
	struct evbuffer_iovec v[1];

	size_t __sz_reserve_chunk = slen > 8192 ? slen : 8192;
	int iov_count = evbuffer_reserve_space(evbuf, __sz_reserve_chunk, v, 1);
	if (iov_count != 1) return -1;

	const unsigned char * next_in = source;
	size_t avail_in = slen;

    unsigned char * next_out = (unsigned char *)v[0].iov_base;
    size_t avail_out = v[0].iov_len;

    enum BrotliEncoderOperation op = end ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
    int ret = 0;
    do
    {
        ret = BrotliEncoderCompressStream((*strm)->brenc_state, op,
            &avail_in, &next_in, &avail_out, &next_out, NULL);

        if(unlikely(ret == BROTLI_FALSE)){
            return ret;
        }

        if (avail_out == 0 || avail_in == 0)
        {
            v[0].iov_len = v[0].iov_len - avail_out;
            ret = evbuffer_commit_space(evbuf, v, iov_count);
            if(ret < 0)
				return -2;

            if(avail_out == 0){
                iov_count = evbuffer_reserve_space(evbuf, __sz_reserve_chunk, v, 1);
                if(unlikely(iov_count != 1))
					return -3;

                next_out = (unsigned char *) v[0].iov_base;
                avail_out = (unsigned int) v[0].iov_len;
            }
        }
    }
    while (avail_in > 0);
    return 0;

}

static int defalta_gzip_write(struct z_stream_st **strm, const uint8_t *source, int slen,
					   struct evbuffer * evbuf, int end)
{
#define SZ_IOVEC    2
	int ret = 0;
	unsigned int i = 0;
	struct evbuffer_iovec io[SZ_IOVEC];

	size_t max = slen > 8192 ? slen : 8192;
	int iov_count = evbuffer_reserve_space(evbuf, max, io, SZ_IOVEC);
	if (iov_count < 1 || iov_count > SZ_IOVEC)
		return -1;

	(*strm)->zst.next_in = (unsigned char *) source;
	(*strm)->zst.avail_in = (unsigned int) slen;
	(*strm)->zst.next_out = (unsigned char *) io[i].iov_base;
	(*strm)->zst.avail_out = (unsigned int) io[i].iov_len;

	int flush = end ? Z_FINISH : Z_NO_FLUSH;
	do
	{
		ret = deflate(&((*strm)->zst), flush);
		assert(ret != Z_STREAM_ERROR);
		assert(i < SZ_IOVEC);

		if ((*strm)->zst.avail_out == 0 || (*strm)->zst.avail_in == 0)
		{
			unsigned int len = (unsigned int) io[i].iov_len - (*strm)->zst.avail_out;
			io[i].iov_len = (size_t) len;

			i++;
			(*strm)->zst.next_out = (unsigned char *) io[i].iov_base;
			(*strm)->zst.avail_out = (unsigned int) io[i].iov_len;
		}
	} while ((*strm)->zst.avail_in > 0);

	assert(end == 0 || ret == Z_STREAM_END);

	(void)ret;
	return evbuffer_commit_space(evbuf, io, iov_count);
}

int deflate_write(struct z_stream_st **strm, const uint8_t *source,
				  int slen, struct evbuffer * evbuf, int gzip, int end)
{
	int ret = 0;

	ret = deflate_init(strm, gzip);
	if (ret != Z_OK){
		return ret;
	}

	if (gzip == HTTP2_CONTENT_ENCODING_GZIP ||
		gzip == HTTP2_CONTENT_ENCODING_DEFLATE){
		ret = defalta_gzip_write(strm, source, slen, evbuf, end);
	}
	if (gzip == HTTP2_CONTENT_ENCODING_BR){
		ret = deflate_br_write(strm, source, slen, evbuf, end);
	}

	return ret;
}

void inflate_finished(struct z_stream_st **strm)
{
	if (*strm != NULL){
		if ((*strm)->brdec_state)
		{
			BrotliDecoderDestroyInstance((*strm)->brdec_state);
			(*strm)->brdec_state = NULL;			
			goto finish;
    	}
		(void)inflateEnd(&((*strm)->zst));
finish:
		free(*strm);
		*strm = NULL;
	}
}
void deflate_finished(struct z_stream_st **strm)
{
	if (*strm != NULL)
	{
		if ((*strm)->brenc_state)
		{
			BrotliEncoderDestroyInstance((*strm)->brenc_state);
			(*strm)->brenc_state = NULL;
			goto finish;

		}
		(void) deflateEnd(&((*strm)->zst));
finish:
		free(*strm);
		*strm = NULL;
	}
}

