#include "tango_cache_pending.h"
#include <tfe_utils.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>


time_t get_time_value(const char* field_value, const char* field_type)
{
    time_t time;
    char* time_value = NULL;
    field_value += strlen(field_type);
    field_value++;
    int len = strlen(field_value);
    time_value = ALLOC(char, len+1);
    int index = 0;
    while (field_value[index] != ',' && field_value[index] != '\r' && index < len)
    {
        time_value[index] = field_value[index];
        index++;
    }
    time_value[index] = '\0';
    time = (time_t)atol(time_value);
    free(time_value);
    return time;
}


void get_request_freshness(const char *value, struct request_freshness* restrict)
{
    const char* field_value = NULL;
    field_value = strstr(value, "min-fresh");
    if (field_value != NULL)
    {
        restrict->min_fresh = get_time_value(field_value, "min-fresh");;
    }

    field_value = strstr(value, "max-age");
    if (field_value != NULL)
    {
        restrict->max_age = get_time_value(field_value, "max-age");;
    }
}


enum cache_pending_action request_cache_control(const char* value, struct request_freshness* restrict)
{
    if (strstr(value, "no-cache") != NULL)
    {
        return REVALIDATE;
    }
    if (strstr(value, "no-store") != NULL)
    {
        return FORBIDDEN;
    }
    get_request_freshness(value, restrict);
    return  ALLOWED;
}


bool cache_verify(const struct tfe_http_half *request)
{
	if( !tfe_http_std_field_read(request,TFE_HTTP_IF_MATCH) ||
		!tfe_http_std_field_read(request,TFE_HTTP_IF_NONE_MATCH) ||
		!tfe_http_std_field_read(request,TFE_HTTP_IF_MODIFIED_SINCE) ||
		!tfe_http_std_field_read(request,TFE_HTTP_IF_UNMODIFIED_SINCE)
	)
	{
		return true;
	}
    return false;
}


const char* get_head_value(const struct tfe_http_field *http_fields, size_t n_fields, enum tfe_http_std_field head_key)
{
    size_t i = 0;
    for (i = 0; i < n_fields; i++)
    {
        if (http_fields[i].http_field == head_key)
        {
            return http_fields[i].value;
        }
    }
    return NULL;
}


enum cache_pending_action get_pragma_action(const char * value)
{
    const char *pragma_value = "no-cache";
    if (strcasecmp(value, pragma_value) == 0)
    {
        return REVALIDATE;
    }
    return UNDEFINED;
}


enum cache_pending_action tfe_cache_get_pending(const struct tfe_http_half *request, struct request_freshness* restrict)
{
	enum cache_pending_action res = UNDEFINED;
	const char *value = NULL;
	memset(restrict,0,sizeof(struct request_freshness));
	if(request->req_spec.method!=TFE_HTTP_METHOD_GET)
	{
		return FORBIDDEN;
	}
	if(NULL!=tfe_http_std_field_read(request, TFE_HTTP_CONT_RANGE) ||
		NULL!=tfe_http_std_field_read(request, TFE_HTTP_AUTHORIZATION)||
		NULL!=tfe_http_nonstd_field_read(request, "WWW-Authenticate"))
	{
		return FORBIDDEN;
	}
	value = tfe_http_std_field_read(request, TFE_HTTP_PRAGMA);
	if (value != NULL)
	{
		res = get_pragma_action(value);
	}
	else
	{
		value = tfe_http_std_field_read(request, TFE_HTTP_CACHE_CONTROL);
		if (value != NULL)
		{
			res = request_cache_control(value, restrict);
		}
		else
		{
			if (cache_verify(request))
			{
				res = REVALIDATE;
			}
		}
	}
	return res;
}



time_t read_GMT_time(const char* gmt_string)
{
    time_t expire_rel_time;
    struct tm expire_gmt_time;
	memset(&expire_gmt_time, 0, sizeof(expire_gmt_time));
    strptime(gmt_string, "%a, %d %b %Y %H:%M:%S GMT", &expire_gmt_time);
    expire_rel_time = mktime(&expire_gmt_time);
    return expire_rel_time;
}


bool  is_standard_gmt_format(const char* value)
{
    int str_len = strlen(value);
	if(0==strcasecmp(value+str_len-3,"GMT"))
    {
        return true;
    }
    else
    {
        return false;
    }
}

time_t get_response_s_maxage(const char* cache_ctl)
{
    const char* s_maxage = NULL;
    s_maxage = strstr(cache_ctl, "s-maxage");
    if (s_maxage != NULL)
    {
        return get_time_value(s_maxage, "s-maxage");
    }
	else
	{
		return 0;
	}
}


time_t get_response_maxage(const char* cache_ctl)
{
    const char* max_age = NULL;
    max_age = strstr(cache_ctl, "max-age");
    if (max_age != NULL)
    {
        return get_time_value(max_age, "max-age");
    }
	else
	{
		return 0;
	}
}


void get_response_freshness(const struct tfe_http_half *response, struct response_freshness* freshness)
{
    time_t expire_rel_time = 0;
    time_t cur_rel_time = 0;
    struct tm cur_gmt_time;
    const char* field_value = NULL;
    field_value = tfe_http_std_field_read(response, TFE_HTTP_CACHE_CONTROL);
    if (field_value != NULL)
    {
        freshness->timeout = get_response_s_maxage(field_value);
        if (freshness->timeout == 0)
        {
            freshness->timeout = get_response_maxage(field_value);
        }
    }
    else
    {
        field_value = tfe_http_std_field_read(response, TFE_HTTP_EXPIRES);
        if (field_value != NULL && is_standard_gmt_format(field_value))
        {
            expire_rel_time = read_GMT_time(field_value);
            const time_t cur_ct_time = time(NULL);
            if (gmtime_r(&cur_ct_time, &cur_gmt_time) == NULL)
            {
                assert(0);
            }
            cur_rel_time = mktime(&cur_gmt_time);
            freshness->timeout = expire_rel_time - cur_rel_time;
        }
    }
    field_value = tfe_http_std_field_read(response, TFE_HTTP_DATE);
    if (field_value != NULL)
    {
        if(is_standard_gmt_format(field_value))
		{
        	freshness->date = read_GMT_time(field_value);;
        }
    }
    field_value = tfe_http_std_field_read(response, TFE_HTTP_LAST_MODIFIED);
    if (field_value != NULL && is_standard_gmt_format(field_value))
    {
        freshness->last_modified = read_GMT_time(field_value);;
    }
}


enum cache_pending_action response_cache_control(const char* value)
{
    const char *forbidden_vaule[] = {"no-store", "private"};
    const char *verify_vaule[] = { "no-cache", "must-revalidate","proxy-revalidate" };
    int i = 0;
    for (i = 0; i < 2; i++)
    {
        if (strstr(value, forbidden_vaule[i]) != NULL)
        {
            return FORBIDDEN;
        }
    }
    for (i = 0; i < 3; i++)
    {
        if (strstr(value, verify_vaule[i]) != NULL)
        {
            return REVALIDATE;
        }
    }
    return  ALLOWED;
}


enum cache_pending_action tfe_cache_put_pending(const struct tfe_http_half *response, struct response_freshness* freshness)
{
    enum cache_pending_action res = UNDEFINED;
    const char *value = NULL;
    memset(freshness,0,sizeof(struct response_freshness));
	if(response->resp_spec.resp_code!=TFE_HTTP_STATUS_OK 
		|| NULL!=tfe_http_std_field_read(response, TFE_HTTP_CONT_RANGE) //NOT upload response with content-range
		|| NULL==response->resp_spec.content_length
		|| NULL!=tfe_http_std_field_read(response, TFE_HTTP_AUTHORIZATION)
		|| NULL!=tfe_http_nonstd_field_read(response, "WWW-Authenticate")
		|| NULL!=tfe_http_std_field_read(response, TFE_HTTP_SET_COOKIE)) 
	{
		return FORBIDDEN;
	}

    value = tfe_http_std_field_read(response, TFE_HTTP_PRAGMA);
    if (value != NULL)
    {
        res = get_pragma_action(value);
    }
    else
    {
        value = tfe_http_std_field_read(response, TFE_HTTP_CACHE_CONTROL);
        if (value != NULL)
        {
            res = response_cache_control(value);
        }
        else
        {
            value = tfe_http_std_field_read(response, TFE_HTTP_EXPIRES);
            if (value != NULL && 0!= read_GMT_time(value))
            {
                res = ALLOWED;
            }
        }
    }
    if (res == ALLOWED)
    {
        get_response_freshness(response, freshness);
    }
    return res;
}
