/**
 * Copyright (C) 2018 Andreas Granig (sipwise.com)
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * Kamailio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */


#ifndef _REDIS_CONNECTION_H_
#define _REDIS_CONNECTION_H_

#ifdef WITH_HIREDIS_CLUSTER
#include <hircluster.h>
#else
#ifdef WITH_HIREDIS_PATH
#include <hiredis/hiredis.h>
#ifdef WITH_SSL
#include <hiredis/hiredis_ssl.h>
#endif
#else
#include <hiredis.h>
#ifdef WITH_SSL
#include <hiredis_ssl.h>
#endif
#endif
#endif

#include "db_redis_mod.h"

#ifndef WITH_REDIS_CLUSTER
#define db_redis_check_reply(con, reply, err)                                \
	do {                                                                     \
		if(!(reply) && !(con)->con) {                                        \
			LM_ERR("Failed to fetch type entry: no connection to server\n"); \
			goto err;                                                        \
		}                                                                    \
		if(!(reply)) {                                                       \
			LM_ERR("Failed to fetch type entry: %s\n", (con)->con->errstr);  \
			redisFree((con)->con);                                           \
			(con)->con = NULL;                                               \
			goto err;                                                        \
		}                                                                    \
		if((reply)->type == REDIS_REPLY_ERROR) {                             \
			LM_ERR("Failed to fetch type entry: %s\n", (reply)->str);        \
			goto err;                                                        \
		}                                                                    \
	} while(0);
#else
#define db_redis_check_reply(con, reply, err)                                \
	do {                                                                     \
		if(!(reply) && !(con)->con) {                                        \
			LM_ERR("Failed to fetch type entry: no connection to server\n"); \
			goto err;                                                        \
		}                                                                    \
		if(!(reply)) {                                                       \
			LM_ERR("Failed to fetch type entry: %s\n", (con)->con->errstr);  \
			redisClusterFree((con)->con);                                    \
			(con)->con = NULL;                                               \
			goto err;                                                        \
		}                                                                    \
		if((reply)->type == REDIS_REPLY_ERROR) {                             \
			LM_ERR("Failed to fetch type entry: %s\n", (reply)->str);        \
			goto err;                                                        \
		}                                                                    \
	} while(0);
#endif

typedef struct redis_key redis_key_t;

typedef struct redis_command
{
	redis_key_t *query;
	struct redis_command *next;
} redis_command_t;

typedef struct km_redis_con
{
	struct db_id *id;
	unsigned int ref;
	struct pool_con *next;
#ifdef WITH_HIREDIS_CLUSTER
	redisClusterContext *con;
#else
	redisContext *con;
#endif
	redis_command_t *command_queue;
	unsigned int append_counter;
	struct str_hash_table tables;
	char srem_key_lua[41]; // sha-1 hex string
} km_redis_con_t;


struct redis_key;
typedef struct redis_key redis_key_t;

#define REDIS_CON(db_con) ((km_redis_con_t *)((db_con)->tail))

km_redis_con_t *db_redis_new_connection(const struct db_id *id);
void db_redis_free_connection(struct pool_con *con);

int db_redis_connect(km_redis_con_t *con);
void *db_redis_command_argv(km_redis_con_t *con, redis_key_t *query);
int db_redis_append_command_argv(
		km_redis_con_t *con, redis_key_t *query, int queue);
int db_redis_get_reply(km_redis_con_t *con, void **reply);
void db_redis_consume_replies(km_redis_con_t *con);
void db_redis_free_reply(redisReply **reply);
const char *db_redis_get_error(km_redis_con_t *con);

#ifdef WITH_HIREDIS_CLUSTER
void *db_redis_command_argv_to_node(
		km_redis_con_t *con, redis_key_t *query, cluster_node *node);
#endif

#endif /* _REDIS_CONNECTION_H_ */
