#ifndef __TANGO_CACHE_CLIENT_IN_H__
#define __TANGO_CACHE_CLIENT_IN_H__

#include <curl/curl.h>
#include <sys/queue.h>
#include <pthread.h>

#include <event2/event.h>  
#include <event.h>
#include <hiredis-vip/async.h>
#include <hiredis-vip/hircluster.h>
#include <cjson/cJSON.h>

#include <MESA/wiredLB.h>
#include <MESA/field_stat2.h>
#include "tango_cache_client.h"

#define RESPONSE_HDR_EXPIRES 1
#define RESPONSE_HDR_LAST_MOD 2
#define RESPONSE_HDR_ALL	3

#define CACHE_ALL_HOS   0 //元信息和对象都存在MINIO
#define CACHE_META_REDIS  1 //元信息在REDIS对象在MINIO
#define CACHE_SMALL_REDIS 2 //元信息和小文件在REDIS，大文件在MINIO

enum FIELD_STAT_FILEDS
{
	FS_FILED_GET_RECV=0,
	FS_FILED_GET_S_TOTAL,
	FS_FILED_GET_S_HTTP,
	FS_FILED_GET_S_REDIS,
	FS_FILED_GET_MISS,
	FS_FILED_GET_E_TOTAL,
	FS_FILED_GET_E_HTTP,
	FS_FILED_GET_E_REDIS,
	FS_FILED_PUT_RECV,
	FS_FILED_PUT_S_TOTAL,
	FS_FILED_PUT_S_HTTP,
	FS_FILED_PUT_S_REDIS,
	FS_FILED_PUT_E_TOTAL,
	FS_FILED_PUT_E_HTTP,
	FS_FILED_PUT_E_REDIS,
	FS_FILED_DEL_RECV,
	FS_FILED_DEL_SUCC,
	FS_FILED_DEL_ERROR,
	FS_FILED_TOTAL_DROP,

	//Next use Status
	FS_FILED_MEM_USED,
	FS_FILED_SESS_HTTP,	
	FS_FILED_SESS_REDIS,
	
	FS_FILED_NUM,
};

enum CACHE_REQUEST_METHOD
{
	CACHE_REQUEST_GET=0,
	CACHE_REQUEST_PUT,
	CACHE_REQUEST_DELETE,
	CACHE_REQUEST_DELETE_MUL,
	CACHE_REQUEST_HEAD,
};

enum GET_OBJECT_STATE
{
	GET_STATE_START=0,
	GET_STATE_DELETE,
	GET_STATE_REDIS_META,
	GET_STATE_REDIS_ALL,
	GET_STATE_REDIS_TRY,
	GET_STATE_END,
};

enum PUT_OBJECT_STATE
{
	PUT_STATE_START=0,
	PUT_STATE_WAIT_START,
	PUT_STATE_PART,
	PUT_STATE_CANCEL,
	PUT_STATE_REDIS_META,
	PUT_STATE_REDIS_EXPIRE,
	PUT_STATE_REDIS_SETEX, //该状态用于等待两次执行结果
	PUT_STATE_END,
};

struct easy_string
{
	char* buff;
	size_t len;
	size_t size;
};

struct wiredlb_parameter
{
	char wiredlb_topic[64];
	char wiredlb_datacenter[64];
	char wiredlb_group[64];
	char iplist[4096];//minio列表
	WLB_handle_t wiredlb;
	u_int32_t wiredlb_override;
	u_int32_t port;
	short wiredlb_ha_port;
};

struct tango_cache_parameter
{
	char bucketname[256];
    char cache_token[256];
	char redis_key[256];
	long maximum_host_cnns;
	long transfer_timeout;//传输总时间限制
	long maximum_pipelines;
	u_int64_t maximum_used_mem;
	u_int32_t maximum_sessions;
	u_int32_t upload_block_size; //minio分段上传块的最小长度
	time_t relative_ttl; //缓存的相对有效期
	u_int32_t hash_object_key;
	//wiredlb
	int object_store_way; //存取object信息的方式
	struct wiredlb_parameter cache;
	char redisaddrs[4096];
	u_int32_t redis_object_maxsize;//小文件存在redis时，对象的最大大小

	//FieldStatLog
	int32_t fsstat_dst_port;
	char fsstat_dst_ip[64];
	char fsstat_appname[16];
	char fsstat_filepath[256];
	u_int32_t fsstat_period;
	u_int32_t fsstatid_trig;
	char fsstat_histlen[256];
	screen_stat_handle_t fsstat_handle;
	int32_t fsstat_histlen_id;
	int32_t fsstat_field_ids[FS_FILED_NUM];
};

struct tango_cache_instance
{
	struct event_base* evbase;
	struct event timer_event;
	struct event timer_statistic;
	CURLM *multi_hd;
	enum CACHE_ERR_CODE error_code;

	int redis_connecting;
	redisClusterAsyncContext *redis_ac;
	char redisaddr[128];

	const struct tango_cache_parameter *param;
	void *runtime_log;
	struct cache_statistics statistic;
	struct cache_statistics statistic_last; //用于多个instance使用同一个fieldstat累加
};

struct multipart_etag_list
{
	char *etag;
	u_int32_t part_number;
	TAILQ_ENTRY(multipart_etag_list) node;
};

typedef void (redisRedirectMinioCallback)(struct tango_cache_ctx *ctx);

struct cache_ctx_data_get
{
	time_t max_age;
	time_t min_fresh;
	time_t expires;
	time_t last_modify;
	u_int32_t need_hdrs;
	enum GET_OBJECT_STATE state;
	struct easy_string response_tag;
	struct tango_cache_result result;
	redisRedirectMinioCallback *redis_redirect_minio_cb;
};

struct cache_ctx_data_put
{
	struct evbuffer *evbuf;
	size_t upload_length;
	size_t upload_offset;
	char *uploadID;
	char *combine_xml;
	TAILQ_HEAD(__etag_list_head, multipart_etag_list) etag_head;
	cJSON *object_meta;
	struct easy_string once_request; //一次性PUT时存储了数据，失败的时候便于清理，不能复用其他结构
	enum PUT_OBJECT_STATE state;
	u_int32_t part_index; //宏RESPONSE_HDR_
	u_int32_t object_ttl;
	bool close_state; //主动被调用关闭
	size_t object_size;
};

struct cache_ctx_multi_delete
{
	u_int32_t succ_num;
	u_int32_t fail_num;
};

struct tango_cache_ctx
{
	CURL *curl;
	struct curl_slist *headers;
	struct promise* promise;
	char error[CURL_ERROR_SIZE];	
	char object_key[256];
	char hostaddr[48];

	enum CACHE_REQUEST_METHOD method;
	enum CACHE_ERR_CODE error_code;
	struct easy_string response;

	bool fail_state;
	enum OBJECT_LOCATION locate; //由程序处理并断定所处位置
	long res_code;
	
	union{
		struct cache_ctx_data_put put;
		struct cache_ctx_data_get get;
		struct cache_ctx_multi_delete del;
	};
	struct tango_cache_instance *instance;
};

struct curl_socket_data
{
	struct event sock_event;
};

void caculate_sha256(const char *data, unsigned long len, char *result, u_int32_t size);

void easy_string_savedata(struct easy_string *estr, const char *data, size_t len);
void easy_string_destroy(struct easy_string *estr);

void tango_cache_ctx_destroy(struct tango_cache_ctx *ctx, bool callback=true);
void tango_cache_set_fail_state(struct tango_cache_ctx *ctx, enum CACHE_ERR_CODE error_code);
const char *tango_cache_get_errstring(const struct tango_cache_ctx *ctx);

bool sessions_exceeds_limit(struct tango_cache_instance *instance, enum OBJECT_LOCATION where_to_get);

struct tango_cache_ctx *tango_cache_update_prepare(struct tango_cache_instance *instance, 
	         struct future* f, struct tango_cache_meta_put *meta, enum OBJECT_LOCATION maybe_loc);
struct tango_cache_ctx *tango_cache_fetch_prepare(struct tango_cache_instance *instance, 
	         enum CACHE_REQUEST_METHOD method, struct future* f, struct tango_cache_meta_get *meta, enum OBJECT_LOCATION where_to_get);
struct tango_cache_ctx *tango_cache_delete_prepare(struct tango_cache_instance *instance, 
             struct future* f, const char *objkey, const char *minio_addr, const char *bucket);

enum OBJECT_LOCATION tango_cache_object_locate(struct tango_cache_instance *instance, size_t object_size);
void tango_cache_get_object_path(struct tango_cache_ctx *ctx, char *path/*OUT*/, size_t pathsize);
struct tango_cache_ctx *tango_cache_update_once_prepare(struct tango_cache_instance *instance, 
             struct future* f, struct tango_cache_meta_put *meta, size_t object_size, char *path, size_t pathsize);

#endif

