#include "../include/pps_redis.h"
#include "../include/param.h"
#include <hiredis/hiredis.h>
#include <stdarg.h>
#include <stdlib.h>
//#include <syslog.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include<stdio.h>
#include<sys/types.h>
//#include<sys/stat.h>
//#include<sys/sysinfo.h>
#include<unistd.h>
#include<ctype.h>
#include<string.h>

//////////////////////////////////////////////////////////////////////////////////////////
extern void RedisLinkMon();
extern int RedisFlushDb();

extern int RedisCreateLogTask(pthread_t *idTask);
extern void RedisAddLogStr(char *time, const char *level, const char *filename, int line, char *text);

// Redis log writer
#define MAX_REDIS_LOG_LINES 81920
#define MAX_REDIS_LINE_LEN 512
typedef struct OCS_redis_log_t
{
    int header;
    int tailer;
    
    char log_str[MAX_REDIS_LOG_LINES][MAX_REDIS_LINE_LEN];
}OCS_redis_log_t;

// simple log
enum RedisLogLevel
{
    RS_ERROR = 1,
    RS_WARN  = 2,
    RS_INFO  = 3,
    RS_DEBUG = 4,
};

void mylog1(const char* filename, int line, enum RedisLogLevel level, const char* fmt, ...) __attribute__((format(printf,4,5)));

#define redislog(level, format, ...) mylog1(__FILE__, __LINE__, level, format, ## __VA_ARGS__)
//

// all Redis field of one KEY
#define RR_SERVICE_NBR 0
#define RR_PRD_INST_ID 1
#define RR_PRICING_SECTION_ID 2
#define RR_OFR_INST_ID 3
#define RR_OFR_ID 4
#define RR_OFR_NAME 5
#define RR_BEGIN_TIME 6
#define RR_END_TIME 7
#define RR_FREE_VALUE 8
#define RR_USED_VALUE 9
#define RR_MEASURE_DOMAIN 10
#define RR_TARIFF_ID 11
#define RR_STRATEGY_ID 12
#define RR_START_REF_TIME 13
#define RR_END_REF_TIME 14
#define RR_CALC_PRIORITY 15
#define RR_EVENT_PRIORITY 16
#define RR_RATE_UNIT 17
#define RR_UNIT_FEE 18
#define RR_REDIS_FIELD_MAX_NUM 19

#define TARIFF_TARIFF_ID 0
#define TARIFF_MEASURE_DOMAIN 1
#define TARIFF_START_TIME 2
#define TARIFF_END_TIME 3
#define TARIFF_FEE_UNIT 4
#define TARIFF_RATE_UNIT 5
#define TARIFF_CALC_PRIORITY 6
#define TARIFF_EVENT_PRIORITY 7
#define TARIFF_OFR_ID 8
#define TARIFF_OFR_NAME 9
#define TARIFF_STRATEGY_ID 10
#define TARIFF_STRATEGY_NAME 11
#define TARIFF_REDIS_FIELD_MAX_NUM 12

#define HD_OFR_ID 0
#define HD_TARIFF_ID 1
#define HD_TARIFF_SEQ 2
#define HD_GROUP 3
#define HR_STATE 4
#define HR_DISCOUNT 5
#define HD_ID 6
#define HD_NAME 7
#define HD_TYPE 8
#define HD_STATE 9
#define HD_PRIORITY 10
#define HD_BEGIN_DATE 11
#define HD_END_DATE 12
#define HD_REDIS_FIELD_MAX_NUM 13


#define ACCT_EFF_DATE 0
#define ACCT_EXP_DATE 1
#define ACCT_OFR_ID 2
#define ACCT_OFR_INST_ID 3
#define ACCT_REDIS_FIELD_MAX_NUM 4
const char *ACCT_REDIS_FN[ACCT_REDIS_FIELD_MAX_NUM] = {
"effTime",// 0
"expTime",// 1
"ofrId",// 2
"ofrInstId",// 3
};

const char *HD_REDIS_FN[HD_REDIS_FIELD_MAX_NUM] = {
"ofrId",// 0
"tariffId",// 1
"tariffSeq",// 2
"hdGroup",// 3
"hrState",// 4
"hrDiscount",// 5
"hdId",// 6
"hdName",// 7
"hdType",// 8
"hdState",// 9
"hdPriority",// 10
"hdBeginDate",// 11
"hdEndDate",// 12
};

const char *RR_REDIS_FN[RR_REDIS_FIELD_MAX_NUM] = {
"serviceNbr",// 0
"prdInstId",// 1
"pricingSectionId",// 2
"ofrInstId",// 3
"ofrId",// 4
"ofrName",// 5
"beginTime",// 6
"endTime",// 7
"freeValue",// 8
"usedValue",// 9
"measureDomain",// 10
"tariffId",// 11
"strategyId",// 12
"startRefTime",// 13
"endRefTime",// 14
"calcPriority",// 15
"eventPriority",// 16
"rateUnit",// 17
"unitFee",// 18
};

const char *TARIFF_REDIS_FN[TARIFF_REDIS_FIELD_MAX_NUM] = {
"tariffId",// 0
"measureDomain",// 1
"startTime",// 2
"endTime",// 3
"feeUnit",// 4
"rateUnit",// 5
"calcPriority",// 6
"eventPriority",// 7
"ofrId",// 8
"ofrName",// 9
"strategyId",// 10
"strategyName",// 11
};

/*
double      balance;// float, %f
double      promo_balance;// float, %f
long long       data;// %lld
int         mo_voice;// %d
int         mt_voice;// %d
u_short     sms;// %hu
u_long      validity_date;// %lu
int         warning_sms_flag;// %d
*/

#define OCS_REDIS_PORT 6379
#define OCS_REDIS_NUM 2// master and slave

typedef struct OCS_redis_struct {
	BOOL is_local_master;
	u32 local_ip;
	u32 peer_ip;
	redisContext *unix_cxt;
	redisContext *peer_cxt;
}OCS_redis_struct;
static OCS_redis_struct redis_mngt = {TRUE, 0, 0, NULL, NULL};
//static pthread_t log_task_id;

static void RedisRelMaster()
{
	if (redis_mngt.is_local_master)
	{
		if (redis_mngt.unix_cxt)
		{
			redisFree(redis_mngt.unix_cxt);
			redis_mngt.unix_cxt = NULL;
		}
		redislog(RS_WARN, "Release current master unix cxt, and set master to peer!");
	}
	else
	{
		if (redis_mngt.peer_cxt)
		{
			redisFree(redis_mngt.peer_cxt);
			redis_mngt.peer_cxt = NULL;
		}
		redislog(RS_WARN, "Release current master peer cxt, and set master to unix socket!");
	}
	redis_mngt.is_local_master = redis_mngt.is_local_master ? FALSE : TRUE;
}

int RedisInit(u32 localip, u32 peerip)
{
	if (redis_mngt.unix_cxt != NULL)
	{
		redisFree(redis_mngt.unix_cxt);
	}

	if (redis_mngt.peer_cxt != NULL)
	{
		redisFree(redis_mngt.peer_cxt);
	}

	// connect local redis by unix socket
	struct timeval timeout = { 1, 500000 }; // 1.5 seconds
	redis_mngt.local_ip = localip;
	struct in_addr addr_local;
	memcpy(&addr_local, &localip, 4);
	redis_mngt.unix_cxt = redisConnectWithTimeout( inet_ntoa(addr_local), OCS_REDIS_PORT, timeout);
	//redis_mngt.unix_cxt = redisConnectUnixWithTimeout("/var/run/kvdb.sock", timeout);
	if (redis_mngt.unix_cxt == NULL || redis_mngt.unix_cxt->err) {
		if (redis_mngt.unix_cxt)
		{
			printf("Connection unix socket error: %s\n", redis_mngt.unix_cxt->errstr);
			redisFree(redis_mngt.unix_cxt);
			redis_mngt.unix_cxt = NULL;
		}
		else
		{
			printf("Connection unix socket error: can't allocate redis context\n");
		}
		exit(1);
	} else {
		redisReply *reply = redisCommand(redis_mngt.unix_cxt, "AUTH %s", "helloearth");
		if (reply != NULL)
		{
			if (reply->type == REDIS_REPLY_ERROR) {
				printf("Authentication error: %s\n", reply->str);
				freeReplyObject(reply);
			}
			else
			{
				freeReplyObject(reply);
			}
		}
	}

	// if local redis is master
	redisReply *reply = NULL;
	reply = redisCommand(redis_mngt.unix_cxt, "INFO");//"INFO replication");
	printf("INFO replication: %s\n", reply->str);
	if (strstr(reply->str, "role:master") != NULL || strstr(reply->str, "role:active-replica") != NULL)
	{
		redis_mngt.is_local_master = TRUE;
	}
	else
	{
		redis_mngt.is_local_master = FALSE;
	}
	freeReplyObject(reply);
	//

	// connect peer redis by tcp socket
	redis_mngt.peer_ip = peerip;
	struct in_addr addr_peer;
	memcpy(&addr_peer, &peerip, 4);
	redis_mngt.peer_cxt = redisConnectWithTimeout( inet_ntoa(addr_peer), OCS_REDIS_PORT, timeout);
	if (redis_mngt.peer_cxt == NULL || redis_mngt.peer_cxt->err) {
		if (redis_mngt.peer_cxt)
		{
			printf("Connection redis socket error: %s\n", redis_mngt.peer_cxt->errstr);
			redisFree(redis_mngt.peer_cxt);
			redis_mngt.peer_cxt = NULL;
		}
		else
		{
			printf("Connection redis socket error: can't allocate redis context\n");
		}
		//return false;
	} else {
		reply = redisCommand(redis_mngt.peer_cxt, "AUTH %s", "helloearth");
		if (reply != NULL)
		{
			if (reply->type == REDIS_REPLY_ERROR) {
				printf("Authentication error: %s\n", reply->str);
				freeReplyObject(reply);
			}
			else
			{
				freeReplyObject(reply);
			}
		}
	}

	//RedisLoadFromRedis();

	/*openlog("OcsRedis", LOG_CONS|LOG_PID, LOG_LOCAL2);
    syslog(LOG_INFO, 
           "This is a syslog test message generated by program '%s'\n",
           "zjd");
    closelog();*/
    
    //RedisCreateLogTask(&log_task_id);

	return 1;
}

void RedisLinkMon()
{
	static u32 timer = 0;

	if (++timer < (CYCLE_STATE_INTERVAL*3))
	{
		return;
	}
	timer = 0;

	struct timeval timeout = { 0, 500000 }; // 0.5 seconds
	if (redis_mngt.unix_cxt == NULL)
	{
		redis_mngt.unix_cxt = redisConnectUnixWithTimeout("/var/run/kvdb.sock", timeout);
		if (redis_mngt.unix_cxt == NULL || redis_mngt.unix_cxt->err) {
			if (redis_mngt.unix_cxt)
			{
				redisFree(redis_mngt.unix_cxt);
				redis_mngt.unix_cxt = NULL;
				redislog(RS_WARN, "unix socket connect fail!");
			}
			else
			{
				redislog(RS_WARN, "unix socket connect fail, err:NA!");
			}
		}
		redis_mngt.is_local_master = FALSE;
	}
	else
	{
		// if local redis is master
		redisReply *reply = NULL;
		reply = redisCommand(redis_mngt.unix_cxt, "INFO replication");
		if (reply == NULL)
		{
			redisFree(redis_mngt.unix_cxt);
			redis_mngt.unix_cxt = NULL;
			redis_mngt.is_local_master = FALSE;
			redislog(RS_WARN, "get local INFO fail, lost unix socket!");
		}
		else
		{
			// printf("INFO replication: %s\n", reply->str);
			if (strstr(reply->str, "role:master") != NULL)
			{
				redis_mngt.is_local_master = TRUE;
			}
			else
			{
				redis_mngt.is_local_master = FALSE;
			}
			freeReplyObject(reply);
		}
	}

	if (redis_mngt.is_local_master || (redis_mngt.peer_ip == 0))
	{
		// read and write in local redis;
		return;
	}

	if (redis_mngt.peer_cxt)
	{
		// if local redis is master
		redisReply *reply = NULL;
		reply = redisCommand(redis_mngt.peer_cxt, "INFO replication");
		if (reply == NULL)
		{
			redisFree(redis_mngt.peer_cxt);
			redis_mngt.peer_cxt = NULL;
			redis_mngt.is_local_master = TRUE;
			redislog(RS_WARN, "get peer INFO fail, lost peer[M] socket!");
		}
		else
		{
			if (strstr(reply->str, "role:master") != NULL)
			{
				redis_mngt.is_local_master = FALSE;
			}
			else
			{
				redis_mngt.is_local_master = TRUE;
			}
			freeReplyObject(reply);
			return;// as master, and already connected
		}
	}
	
	// try to connect peer redis for redis write
	struct in_addr addr_peer;
	memcpy(&addr_peer, &redis_mngt.peer_ip, 4);
	redis_mngt.peer_cxt = redisConnectWithTimeout( inet_ntoa(addr_peer), OCS_REDIS_PORT, timeout);
	if (redis_mngt.peer_cxt == NULL || redis_mngt.peer_cxt->err) {
		if (redis_mngt.peer_cxt)
		{
			//printf("Connection redis socket error: %s\n", redis_mngt.peer_cxt->errstr);
			redisFree(redis_mngt.peer_cxt);
			redis_mngt.peer_cxt = NULL;
			redislog(RS_WARN, "peer socket connect fail!");
		}
		else
		{
			redislog(RS_WARN, "peer socket connect fail, NA!");
		}
	}

	return;
}

int RedisFlushDb()
{
	redisReply *reply = NULL;
	int ret = 0;

	/* FLUSHDB */
	redisContext *cxt = redis_mngt.is_local_master?redis_mngt.unix_cxt:redis_mngt.peer_cxt;
	if (cxt == NULL)
	{
		return ret;
	}
	reply = redisCommand(cxt, "FLUSHDB");// clear current db
	if (reply == NULL)// connection lost
	{
		RedisRelMaster();
		return ret;
	}
	//printf("FLUSHDB: %s\n", reply->str);

	if ((reply->type == REDIS_REPLY_ERROR) && (strncmp(reply->str, "READONLY", 8) == 0))
	{
		redislog(RS_WARN, "%s change to READONLY slave redis?", redis_mngt.is_local_master?"local":"peer");
		redis_mngt.is_local_master = redis_mngt.is_local_master ? FALSE : TRUE;
	}
	else if (reply->type != REDIS_REPLY_ERROR)// success
	{
		ret = 1;
	}
	freeReplyObject(reply);

	return ret;
}

int RedisUpdateUsedBundle(char *service_nbr, int service_type, int rr_id, unsigned long long used_bundle_this_time)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	int i, ret = 0;
	for (i=0; i<OCS_REDIS_NUM; i++)
	{
		cxt = redis_mngt.is_local_master?redis_mngt.unix_cxt:redis_mngt.peer_cxt;
		if (cxt == NULL)
		{
			redis_mngt.is_local_master = redis_mngt.is_local_master ? FALSE : TRUE;
			continue;
		}
		
		reply = redisCommand(cxt, "HINCRBY Rr:%s:%02d:%d usedValue %llu", 
							service_nbr, service_type, rr_id, used_bundle_this_time);
	
		if (reply == NULL)// connection lost
		{
			RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
			continue;
		}

		if ((reply->type == REDIS_REPLY_ERROR) && (strncmp(reply->str, "READONLY", 8) == 0))
		{
			redislog(RS_WARN, "%s change to READONLY slave redis?", redis_mngt.is_local_master?"local":"peer");
			redis_mngt.is_local_master = redis_mngt.is_local_master ? FALSE : TRUE;
			freeReplyObject(reply);
		}
		else if (reply->type != REDIS_REPLY_ERROR)
		{
			freeReplyObject(reply);
			ret = 1;
			redislog(RS_DEBUG, "%s rr_id[%d], add used Bundle %llu!", \
					service_nbr, rr_id, used_bundle_this_time);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", service_nbr, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

typedef struct Tariff_redis_st
{
	char service_num[24];
	char prefix[24];
	int service_type;

	char time_now[32];
	char rr_time_now[32];
	int secOfDayTime;
	int week_day;
	
	int unit;
	int unit_charge;
	int discount;
	int plan_id;
	int bundle_plan_id;
	unsigned long long free_value;
	unsigned long long used_value;
}Tariff_redis_st;

typedef struct Hd_redis_st
{
	int ofr_id;
	int tariff_id;
	int tariff_seq;
	int hd_group;
	int hr_state;
	int hr_discount;

	int hd_id;
	char hd_name[64];
	char hd_type[4];
	char hd_state[4];
	int hd_priority;
	char begin_time[32];
	char end_time[32];
}Hd_redis_st;

typedef struct Rr_redis_st
{
	int prd_inst_id;
	int pricing_section_id;
	int ofr_inst_id;
	int rr_id;
	int ofr_id;
	char ofr_name[128];
	char begin_time[32];
	char end_time[32];
	long long bundle_value;
	long long used_value;
	char measure_domain[8];
	int tariff_id;
	int strategy_id;
	int start_ref_time;
	int end_ref_time;
	int calc_priority;
	int event_priority;

	int rate_unit;
	int unit_fee;
}Rr_redis_st;

typedef struct Ofr_redis_st
{
	int tariff_id;
	char measure_domain[8];
	int start_ref_time;
	int end_ref_time;
	int unit;
	int unit_charge;
	int discount;
	int calc_priority;
	int event_priority;
	int ofr_id;
	char ofr_name[128];
	int strategy_id;
	char strategy_name[128];
}Ofr_redis_st;

#define MIN_RR_KEY_LEN 14
int RedisCheckPrefix(int tariff_id, char *prefix);
int RedisGetOfrIdByKey(char *key, int *ofrId, int *ofrInstId);
int RedisGetRrInfo(char *key, Tariff_redis_st *pTariff, Rr_redis_st *pRr)
{
	redisReply *reply = NULL;

	if (redis_mngt.unix_cxt == NULL)
	{
		redislog(RS_ERROR, "lost unix socket!");
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		return -1;
	}

	Rr_redis_st rr_info;
	reply = redisCommand(redis_mngt.unix_cxt, "HMGET %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", key, 
						RR_REDIS_FN[RR_PRD_INST_ID], RR_REDIS_FN[RR_PRICING_SECTION_ID], RR_REDIS_FN[RR_OFR_INST_ID], 
						RR_REDIS_FN[RR_OFR_ID], RR_REDIS_FN[RR_OFR_NAME], RR_REDIS_FN[RR_BEGIN_TIME], RR_REDIS_FN[RR_END_TIME],
						RR_REDIS_FN[RR_FREE_VALUE], RR_REDIS_FN[RR_USED_VALUE], RR_REDIS_FN[RR_MEASURE_DOMAIN], RR_REDIS_FN[RR_TARIFF_ID],
						RR_REDIS_FN[RR_STRATEGY_ID], RR_REDIS_FN[RR_START_REF_TIME], RR_REDIS_FN[RR_END_REF_TIME], 
						RR_REDIS_FN[RR_CALC_PRIORITY], RR_REDIS_FN[RR_EVENT_PRIORITY], RR_REDIS_FN[RR_RATE_UNIT], RR_REDIS_FN[RR_UNIT_FEE]
						);

	if (reply == NULL)
	{
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		redislog(RS_ERROR, "Get RR info by key %s fail, error: no connection?!", key);
		return -1;
	}
	
	int j, count = 0;
	if (reply->type == REDIS_REPLY_ARRAY) {
		for (j = 0; j < reply->elements; j++) {
			//printf("%u) %s\n", j, reply->element[j]->str);
			if (reply->element[j]->str == NULL)
			{
				continue;
			}

			switch (j+1)
			{
			case RR_PRD_INST_ID:
				count++;
				rr_info.prd_inst_id = atoi(reply->element[j]->str);
				break;
			case RR_PRICING_SECTION_ID:
				count++;
				rr_info.pricing_section_id = atoi(reply->element[j]->str);
				break;
			case RR_OFR_INST_ID:// plan id
				rr_info.ofr_inst_id = atoi(reply->element[j]->str);
				break;
			case RR_OFR_ID:
				rr_info.ofr_id = atoi(reply->element[j]->str);
				break;
			case RR_OFR_NAME:
				strcpy(rr_info.ofr_name, reply->element[j]->str);
				break;
			case RR_BEGIN_TIME:
				strcpy(rr_info.begin_time, reply->element[j]->str);
				break;
			case RR_END_TIME:
				strcpy(rr_info.end_time, reply->element[j]->str);
				break;
			case RR_FREE_VALUE:
				rr_info.bundle_value = strtoll(reply->element[j]->str, NULL, 0);
				break;
			case RR_USED_VALUE:
				rr_info.used_value = strtoll(reply->element[j]->str, NULL, 0);
				break;
			case RR_MEASURE_DOMAIN:
				strcpy(rr_info.measure_domain, reply->element[j]->str);
				break;
			case RR_TARIFF_ID:
				rr_info.tariff_id = atoi(reply->element[j]->str);
				break;
			case RR_STRATEGY_ID:
				rr_info.strategy_id = atoi(reply->element[j]->str);
				break;
			case RR_START_REF_TIME:
				rr_info.start_ref_time = atoi(reply->element[j]->str);
				break;
			case RR_END_REF_TIME:
				rr_info.end_ref_time = atoi(reply->element[j]->str);
				break;
			case RR_CALC_PRIORITY:
				rr_info.calc_priority = atoi(reply->element[j]->str);
				break;
			case RR_EVENT_PRIORITY:
				rr_info.event_priority = atoi(reply->element[j]->str);
				break;
			case RR_RATE_UNIT:
				rr_info.rate_unit = atoi(reply->element[j]->str);
				break;
			case RR_UNIT_FEE:
				rr_info.unit_fee = atoi(reply->element[j]->str);
				break;
			default:
				break;
			}
		}
		if (rr_info.used_value < 0)
		{
			rr_info.used_value = 0;
		}

		if ((strcmp(rr_info.end_time, pTariff->rr_time_now) < 0)
			|| (rr_info.used_value >= rr_info.bundle_value))
		{// already expired, or used up, TODO: remove redis RR record
			redislog(RS_INFO, "RR expire: end_time[%s], now_time[%s], used[%lld]/total[%lld]", 
								rr_info.end_time, pTariff->rr_time_now, rr_info.used_value, rr_info.bundle_value);
			freeReplyObject(reply);
			return 0;
		}
		if ((pTariff->secOfDayTime < rr_info.start_ref_time)
			|| (pTariff->secOfDayTime > rr_info.end_ref_time))
		{
			redislog(RS_INFO, "RR mismatch time of day: secOfDayTime[%d], start_ref_time[%d], end_ref_time[%d]", 
								pTariff->secOfDayTime, rr_info.start_ref_time, rr_info.end_ref_time);
			freeReplyObject(reply);
			return 0;
		}
		// match prefix for voice call
		if ((pTariff->service_type != 2) && RedisCheckPrefix(rr_info.strategy_id, pTariff->prefix) <= 0)
		{
			freeReplyObject(reply);
			return 0;
		}
		//
		if (pRr->prd_inst_id == 0)// the first one
		{
			memcpy(pRr, &rr_info, sizeof(rr_info));
			pRr->rr_id = atoi(&key[7+strlen(pTariff->service_num)]);// skip Rr:service_num:measure_domain:
		}
		else// compare priority
		{
			if ((rr_info.calc_priority > pRr->calc_priority)
				|| ((rr_info.calc_priority == pRr->calc_priority) && (rr_info.event_priority > pRr->event_priority))
				|| ((rr_info.calc_priority == pRr->calc_priority) && (rr_info.event_priority == pRr->event_priority)
					&& (strcmp(rr_info.end_time, pRr->end_time) < 0)))
			{
				memcpy(pRr, &rr_info, sizeof(rr_info));
				pRr->rr_id = atoi(&key[7+strlen(pTariff->service_num)]);// skip Rr:service_num:measure_domain:
			}
		}
	}
	else
	{
		redislog(RS_ERROR, "Get RR info by key %s fail, error: %s!", key, (reply->str!=NULL)?reply->str:"unknown");
		freeReplyObject(reply);
		return -1;
	}
	
	redislog(RS_INFO, "Get RR info by key %s succ!", key);
	freeReplyObject(reply);

	return 1;
}

int RedisCheckPrefix(int strategy_id, char *prefix)
{
	redisReply *reply = NULL;

	if (redis_mngt.unix_cxt == NULL)
	{
		redislog(RS_ERROR, "lost unix socket!");
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		return -1;
	}

	reply = redisCommand(redis_mngt.unix_cxt, "SISMEMBER Prefix:%d %s", strategy_id, prefix);

	if (reply == NULL)
	{
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		redislog(RS_ERROR, "Check prefix %s of strategy_id[%d] fail, error: no connection?!", prefix, strategy_id);
		return -1;
	}
	
	// read data
	int ret = -1;
	if (reply->type == REDIS_REPLY_INTEGER)
	{
		if (reply->integer >= 1)
		{
			redislog(RS_DEBUG, "prefix %s is in strategy_id[%d].", prefix, strategy_id);
			ret = 1;
		}
		else
		{
			//redislog(RS_DEBUG, "prefix %s is not in strategy_id[%d].", prefix, tariff_id);
			ret = 0;
		}
	}
	//
	freeReplyObject(reply);
	return ret;
}

int RedisGetTariffInfo1(char *key, Tariff_redis_st *pTariff, Ofr_redis_st *pOfr)
{
	redisReply *reply = NULL;

	if (redis_mngt.unix_cxt == NULL)
	{
		redislog(RS_ERROR, "lost unix socket!");
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		return -1;
	}

	Ofr_redis_st ofr_info;
	reply = redisCommand(redis_mngt.unix_cxt, "HMGET %s %s %s %s %s %s %s %s %s %s %s %s %s", key, 
						TARIFF_REDIS_FN[TARIFF_TARIFF_ID], TARIFF_REDIS_FN[TARIFF_MEASURE_DOMAIN], TARIFF_REDIS_FN[TARIFF_START_TIME], 
						TARIFF_REDIS_FN[TARIFF_END_TIME], TARIFF_REDIS_FN[TARIFF_FEE_UNIT], TARIFF_REDIS_FN[TARIFF_RATE_UNIT], 
						TARIFF_REDIS_FN[TARIFF_CALC_PRIORITY], TARIFF_REDIS_FN[TARIFF_EVENT_PRIORITY], TARIFF_REDIS_FN[TARIFF_OFR_ID], 
						TARIFF_REDIS_FN[TARIFF_OFR_NAME], TARIFF_REDIS_FN[TARIFF_STRATEGY_ID], TARIFF_REDIS_FN[TARIFF_STRATEGY_NAME]
						);

	if (reply == NULL)
	{
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		redislog(RS_ERROR, "Get tariff by key %s fail, error: no connection?!", key);
		return -1;
	}
	
	int j, count = 0;
	if (reply->type == REDIS_REPLY_ARRAY) {
		for (j = 0; j < reply->elements; j++) {
			//printf("%u) %s\n", j, reply->element[j]->str);
			if (reply->element[j]->str == NULL)
			{
				continue;
			}

			switch (j)
			{
			case TARIFF_TARIFF_ID:
				count++;
				ofr_info.tariff_id = atoi(reply->element[j]->str);
				break;
			case TARIFF_MEASURE_DOMAIN:
				count++;
				strcpy(ofr_info.measure_domain, reply->element[j]->str);
				break;
			case TARIFF_START_TIME:// plan id
				ofr_info.start_ref_time = atoi(reply->element[j]->str);
				break;
			case TARIFF_END_TIME:
				ofr_info.end_ref_time = atoi(reply->element[j]->str);
				break;
			case TARIFF_FEE_UNIT:
				ofr_info.unit_charge = atoi(reply->element[j]->str);
				break;
			case TARIFF_RATE_UNIT:
				ofr_info.unit = atoi(reply->element[j]->str);
				break;
			case TARIFF_CALC_PRIORITY:
				ofr_info.calc_priority = atoi(reply->element[j]->str);
				break;
			case TARIFF_EVENT_PRIORITY:
				ofr_info.event_priority = atoi(reply->element[j]->str);
				break;
			case TARIFF_OFR_ID:
				ofr_info.ofr_id = atoi(reply->element[j]->str);
				break;
			case TARIFF_OFR_NAME:
				strcpy(ofr_info.ofr_name, reply->element[j]->str);
				break;
			case TARIFF_STRATEGY_ID:
				ofr_info.strategy_id = atoi(reply->element[j]->str);
				break;
			case TARIFF_STRATEGY_NAME:
				strcpy(ofr_info.strategy_name, reply->element[j]->str);
				break;
			default:
				break;
			}
		}

		if ((pTariff->secOfDayTime < ofr_info.start_ref_time)
			|| (pTariff->secOfDayTime > ofr_info.end_ref_time))
		{
			redislog(RS_INFO, "Tariff mismatch time of day: secOfDayTime[%d], start_ref_time[%d], end_ref_time[%d]", 
								pTariff->secOfDayTime, ofr_info.start_ref_time, ofr_info.end_ref_time);
			freeReplyObject(reply);
			return 0;
		}
		// match prefix for voice call
		if ((pTariff->service_type != 2) && RedisCheckPrefix(ofr_info.strategy_id, pTariff->prefix) <= 0)
		{
			freeReplyObject(reply);
			return 0;
		}
		//
		if (pOfr->tariff_id == 0)// the first one
		{
			memcpy(pOfr, &ofr_info, sizeof(ofr_info));
		}
		else// compare priority
		{
			if ((ofr_info.calc_priority > pOfr->calc_priority)
				|| ((ofr_info.calc_priority == pOfr->calc_priority) && (ofr_info.event_priority > pOfr->event_priority)))
			{
				memcpy(pOfr, &ofr_info, sizeof(ofr_info));
			}
		}
	}
	else
	{
		redislog(RS_ERROR, "Get tariff by key %s fail, error: %s!", key, (reply->str!=NULL)?reply->str:"unknown");
		freeReplyObject(reply);
		return -1;
	}
	
	redislog(RS_INFO, "Get tariff by key %s succ", key);
	freeReplyObject(reply);

	return 1;
}

int RedisGetOfrId(char *service_nbr, int *ofrInstId)
{
	redisReply *reply = NULL;

	if (redis_mngt.unix_cxt == NULL)
	{
		redislog(RS_ERROR, "lost unix socket!");
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		return -1;
	}


	// scan tariff record
	int count = 0;
	int cursor = 0;
	int nbkeys = 0;
	int ofrId = -1;//, ofrInstId = 0;
	do
	{
		count++;
		reply = redisCommand(redis_mngt.unix_cxt, "SSCAN Acct:%s %d", service_nbr, cursor);
		if (reply == NULL)
		{
			RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
			return -1;
		}
		int type = reply ? (reply->type) : 0;

		if (type == REDIS_REPLY_STRING || type == REDIS_REPLY_ERROR)
			printf("SCAN reply str %s\r\n", reply->str);
		else if (type == REDIS_REPLY_ARRAY)
		{
			size_t nbelem = reply->elements;

			if (nbelem > 1 && reply->element[1]->type == REDIS_REPLY_ARRAY)
			{
				struct redisReply *replseqkeys = reply->element[1];
				size_t nbcurkeys = replseqkeys->elements, ik;
				//printf("#%ld; SCAN nbcurkeys %zd", count, nbcurkeys);
				for (ik = 0; ik < nbcurkeys; ik++)
				{
					struct redisReply *replcurkey = replseqkeys->element[ik];
					int curkeytype = replcurkey->type;
					if (curkeytype == REDIS_REPLY_STRING)
					{
						nbkeys++;
						char acctKey[48];
						sprintf(acctKey, "Acct:%s:%s", service_nbr, replcurkey->str);
						redislog(RS_DEBUG, "Get Acct key: %s", acctKey);
						if (RedisGetOfrIdByKey(acctKey, &ofrId, ofrInstId) > 0)
						{
							break;
							// compare and update
						}
					}
				}
			}

			if ((nbelem > 0) && (reply->element[0]->type == REDIS_REPLY_STRING))
			{
				cursor = atol(reply->element[0]->str);
				printf("#%d; SSCAN updated cursor %d", count, cursor);
			}
			else
			{
				cursor = -1;
				printf("#%d; SSCAN nocursor", count);
			}
		}
		freeReplyObject (reply);
		reply = NULL;
		if (ofrId > 0) {
			break;
		}
	}while (cursor > 0);
	return ofrId;


	/*Rr_redis_st rr_info;
	reply = redisCommand(redis_mngt.unix_cxt, "HGET Acct:%s %s", service_nbr, 
						RR_REDIS_FN[RR_OFR_ID]
						);

	if (reply == NULL)
	{
		//RedisRelMaster();
		redislog(RS_ERROR, "Get ofr id by key Acct:%s fail, error: no connection?!", service_nbr);
		return -1;
	}
	
	// read data
	if ((reply->type == REDIS_REPLY_STRING) && (reply->len > 0))
	{
		int ofr_id = atoi(reply->str);
		redislog(RS_DEBUG, "Get ofr id[%d] by key Acct:%s succ.", ofr_id, service_nbr);
		freeReplyObject(reply);
		return ofr_id;
	}
	else
	{
		freeReplyObject(reply);
		return -1;
	}
	*/
}

static char date_time_str[24];
void get_cur_data_time_str()
{
    time_t time_ptr;
    struct tm *tmp_ptr = NULL;
    int year,month,day,hour,minute,sec;


    time(&time_ptr);

    tmp_ptr = localtime(&time_ptr);

    year = tmp_ptr->tm_year + 1900;
    month = tmp_ptr->tm_mon + 1;
    day = tmp_ptr->tm_mday;

    hour = tmp_ptr->tm_hour;
    minute = tmp_ptr->tm_min;
    sec = tmp_ptr->tm_sec;
    
    sprintf(date_time_str, "%04d-%02d-%02d %02d:%02d:%02d",year,month,day,hour,minute,sec);
    return ;
}

int RedisGetOfrIdByKey(char *key, int *ofrId, int *ofrInstId)
{
	redisReply *reply = NULL;

	if (redis_mngt.unix_cxt == NULL)
	{
		redislog(RS_ERROR, "lost unix socket!");
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		return -1;
	}

	//Rr_redis_st rr_info;
	reply = redisCommand(redis_mngt.unix_cxt, "HMGET %s %s %s %s %s", key, 
						ACCT_REDIS_FN[ACCT_EFF_DATE],ACCT_REDIS_FN[ACCT_EXP_DATE],ACCT_REDIS_FN[ACCT_OFR_ID],ACCT_REDIS_FN[ACCT_OFR_INST_ID]
						);

	if (reply == NULL)
	{
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		redislog(RS_ERROR, "Get ofr id by key %s fail, error: no connection?!", key);
		return -1;
	}
	
	int j, count = 0;
	char effDate[48], expDate[48];
	if (reply->type == REDIS_REPLY_ARRAY) {
		for (j = 0; j < reply->elements; j++) {
			//printf("%u) %s\n", j, reply->element[j]->str);
			if (reply->element[j]->str == NULL)
			{
				continue;
			}

			switch (j)
			{
			case ACCT_EFF_DATE:
				count++;
				strcpy(effDate, reply->element[j]->str);
				break;
			case ACCT_EXP_DATE:
				count++;
				strcpy(expDate, reply->element[j]->str);
				break;
			case ACCT_OFR_ID:
				count++;
				*ofrId = atoi(reply->element[j]->str);
				break;
			case ACCT_OFR_INST_ID:
				count++;
				*ofrInstId = atoi(reply->element[j]->str);
				break;
			default:
				break;
			}
		}
		get_cur_data_time_str();
		if (strncmp(effDate, date_time_str, 19)<=0 || strncmp(expDate, date_time_str, 19)>=0)
		{// match
		}
		else
		{
			*ofrId = 0;
		}
	}
	else
	{
		redislog(RS_ERROR, "Get ofrId by key %s fail, error: %s!", key, (reply->str!=NULL)?reply->str:"unknown");
		freeReplyObject(reply);
		return -1;
	}
	
	redislog(RS_INFO, "Get ofrId by key %s succ!", key);
	freeReplyObject(reply);

	return *ofrId;

}

int RedisGetOfrIdByServiceNbr(char *service_nbr)
{
	redisReply *reply = NULL;

	if (redis_mngt.unix_cxt == NULL)
	{
		redislog(RS_ERROR, "lost unix socket!");
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		return -1;
	}

	//Rr_redis_st rr_info;
	reply = redisCommand(redis_mngt.unix_cxt, "HGET Acct:%s %s", service_nbr, 
						ACCT_REDIS_FN[ACCT_OFR_ID]
						);

	if (reply == NULL)
	{
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		redislog(RS_ERROR, "Get ofr id by key Acct:%s fail, error: no connection?!", service_nbr);
		return -1;
	}
	
	// read data
	if ((reply->type == REDIS_REPLY_STRING) && (reply->len > 0))
	{
		int ofr_id = atoi(reply->str);
		redislog(RS_DEBUG, "Get ofr id[%d] by key Acct:%s succ.", ofr_id, service_nbr);
		freeReplyObject(reply);
		return ofr_id;
	}
	else
	{
		freeReplyObject(reply);
		return -1;
	}
	//
}

void setTariffByRrInfo(Tariff_redis_st *pTariff, Rr_redis_st *pRr)
{
	pTariff->unit = pRr->rate_unit;
	pTariff->unit_charge = pRr->unit_fee;
	pTariff->discount = 100;
	pTariff->plan_id = pRr->ofr_inst_id;
	pTariff->bundle_plan_id = pRr->rr_id;
	pTariff->free_value = pRr->bundle_value;
	pTariff->used_value = pRr->used_value;
	return;
}

void setTariffByOfrInfo(Tariff_redis_st *pTariff, Ofr_redis_st *pOfr)
{
	pTariff->unit = pOfr->unit;
	pTariff->unit_charge = pOfr->unit_charge;
	pTariff->discount = pOfr->discount;
	pTariff->plan_id = 0;
	pTariff->bundle_plan_id = 0;
	pTariff->free_value = 0;
	pTariff->used_value = 0;
	return;
}

int RedisGetHdInfo(char *key, Tariff_redis_st *pTariff, Hd_redis_st *pHd)
{
	redisReply *reply = NULL;

	if (redis_mngt.unix_cxt == NULL)
	{
		redislog(RS_ERROR, "lost unix socket!");
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		return -1;
	}

	Hd_redis_st hd_info;
	reply = redisCommand(redis_mngt.unix_cxt, "HMGET %s %s %s %s %s %s %s %s %s %s %s %s %s %s", key, 
						HD_REDIS_FN[HD_OFR_ID], HD_REDIS_FN[HD_TARIFF_ID], HD_REDIS_FN[HD_TARIFF_SEQ], 
						HD_REDIS_FN[HD_GROUP], HD_REDIS_FN[HR_STATE], HD_REDIS_FN[HR_DISCOUNT], HD_REDIS_FN[HD_ID],
						HD_REDIS_FN[HD_NAME], HD_REDIS_FN[HD_TYPE], HD_REDIS_FN[HD_STATE], HD_REDIS_FN[HD_PRIORITY],
						HD_REDIS_FN[HD_BEGIN_DATE], HD_REDIS_FN[HD_END_DATE]
						);

	if (reply == NULL)
	{
		RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
		redislog(RS_ERROR, "Get HD info by key %s fail, error: no connection?!", key);
		return -1;
	}
	
	int j, count = 0;
	if (reply->type == REDIS_REPLY_ARRAY) {
		for (j = 0; j < reply->elements; j++) {
			//printf("%u) %s\n", j, reply->element[j]->str);
			if (reply->element[j]->str == NULL)
			{
				continue;
			}

			switch (j)
			{
			case HD_OFR_ID:
				count++;
				hd_info.ofr_id = atoi(reply->element[j]->str);
				break;
			case HD_TARIFF_ID:
				count++;
				hd_info.tariff_id = atoi(reply->element[j]->str);
				break;
			case HD_TARIFF_SEQ:
				hd_info.tariff_seq = atoi(reply->element[j]->str);
				break;
			case HD_GROUP:
				hd_info.hd_group = atoi(reply->element[j]->str);
				break;
			case HR_STATE:
				hd_info.hr_state = atoi(reply->element[j]->str);
				break;
			case HR_DISCOUNT:
				hd_info.hr_discount = atoi(reply->element[j]->str);
				break;
			case HD_ID:
				hd_info.hd_id = atoi(reply->element[j]->str);
				break;
			case HD_NAME:
				strcpy(hd_info.hd_name, reply->element[j]->str);
				break;
			case HD_TYPE:
				strcpy(hd_info.hd_type, reply->element[j]->str);
				break;
			case HD_STATE:
				strcpy(hd_info.hd_state, reply->element[j]->str);
				break;
			case HD_PRIORITY:
				hd_info.tariff_id = atoi(reply->element[j]->str);
				break;
			case HD_BEGIN_DATE:
				strcpy(hd_info.begin_time, reply->element[j]->str);
				break;
			case HD_END_DATE:
				strcpy(hd_info.end_time, reply->element[j]->str);
				break;
			default:
				break;
			}
		}

		if (hd_info.hr_state != 1 || strncmp(hd_info.hd_state, "L0R", 3) != 0)
		{
			freeReplyObject(reply);
			return 0;// inactive
		}

		int bHoliday = 0;
		if (strncmp(hd_info.hd_type, "02", 2) == 0)// weekly, 1-7
		{
			if (('0' + pTariff->week_day >= hd_info.begin_time[0]) && ('0' + pTariff->week_day <= hd_info.end_time[0]))
			{
				bHoliday = 1;
			}
		}
		else if (strncmp(hd_info.hd_type, "03", 2) == 0)// holiday period, mmdd-mmdd
		{
			if ((strncmp(&pTariff->rr_time_now[4], hd_info.begin_time, 4) >= 0) && (strncmp(&pTariff->rr_time_now[4], hd_info.end_time, 4) <= 0))
			{
				bHoliday = 1;
			}
		}
		else if (strncmp(hd_info.hd_type, "04", 2) == 0)// day time period, HHmmss-HHmmss
		{
			if ((strncmp(&pTariff->rr_time_now[8], hd_info.begin_time, 6) >= 0) && (strncmp(&pTariff->rr_time_now[8], hd_info.end_time, 6) <= 0))
			{
				bHoliday = 1;
			}
		}
		if (bHoliday == 1)
		{
			if ((pHd->tariff_id == 0) || (hd_info.hd_priority > pHd->hd_priority))// new, or higer priority
			{
				memcpy(pHd, &hd_info, sizeof(hd_info));
			}
		}
	}
	else
	{
		redislog(RS_ERROR, "Get HD info by key %s fail, error: %s!", key, (reply->str!=NULL)?reply->str:"unknown");
		freeReplyObject(reply);
		return -1;
	}
	
	redislog(RS_INFO, "Get HD info by key %s succ!", key);
	freeReplyObject(reply);

	return 1;
}

void scanHolidayDiscount(Tariff_redis_st *pTariff, Ofr_redis_st *pOfr)
{
	redisReply *reply = NULL;
	int count = 0, cursor = 0, nbkeys = 0;
	//Tariff_redis_st tariff_temp;
	Hd_redis_st hd_info = {0,0,0,0};
	
	// Scan Holiday discount record
	do
	{
		count++;
		// scan bundle first
		//reply = redisCommand(redis_mngt.unix_cxt, "SCAN %d MATCH Hd:%d:* COUNT 1000", cursor, pOfr->tariff_id);
		reply = redisCommand(redis_mngt.unix_cxt, "SSCAN HdSet:%d %d", pOfr->tariff_id, cursor);
		if (reply == NULL)
		{
			RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
			return ;
		}
		int type = reply ? (reply->type) : 0;

		if (type == REDIS_REPLY_STRING || type == REDIS_REPLY_ERROR)
			printf("SCAN reply str %s\r\n", reply->str);
		else if (type == REDIS_REPLY_ARRAY)
		{
			size_t nbelem = reply->elements;

			if (nbelem > 1 && reply->element[1]->type == REDIS_REPLY_ARRAY)
			{
				struct redisReply *replseqkeys = reply->element[1];
				size_t nbcurkeys = replseqkeys->elements, ik;
				//printf("#%ld; SCAN nbcurkeys %zd", count, nbcurkeys);
				for (ik = 0; ik < nbcurkeys; ik++)
				{
					struct redisReply *replcurkey = replseqkeys->element[ik];
					int curkeytype = replcurkey->type;
					if (curkeytype == REDIS_REPLY_STRING)
					{
						nbkeys++;
						//printf("got key#%ld : %s", nbkeys, replcurkey->str);
						// get rr info, and compare
						redislog(RS_DEBUG, "Get Hd key: %s, len[%zu]", replcurkey->str, replcurkey->len);
						if (replcurkey->len >= MIN_RR_KEY_LEN)
						{
							if (RedisGetHdInfo(replcurkey->str, pTariff, &hd_info) > 0)
							{
								
								// compare and update
							}
						}
					}
				}
			}

			if ((nbelem > 0) && (reply->element[0]->type == REDIS_REPLY_STRING))
			{
				cursor = atol(reply->element[0]->str);
				printf("#%d; SSCAN updated cursor %d", count, cursor);
			}
			else
			{
				cursor = -1;
				printf("#%d; SSCAN nocursor", count);
			}
		}
		freeReplyObject (reply);
		reply = NULL;
	}while (cursor > 0);

	if (hd_info.tariff_id > 0)// get holiday discount
	{
		redislog(RS_DEBUG, "Get Hd: discount[%d], tariff_id[%d], hd_id[%d].", hd_info.hr_discount, hd_info.tariff_id, hd_info.hd_id);
		pTariff->discount = hd_info.hr_discount;
	}
}

int RedisScanTariff(Tariff_redis_st *target_tariff)
{
	redisReply *reply = NULL;
	int count = 0, cursor = 0, nbkeys = 0;
	//Tariff_redis_st tariff_temp;
	Rr_redis_st rr_info = {0,0,0,0};
	
	time_t t;
	time(&t);
	struct tm *tmp_time = localtime(&t);
	target_tariff->secOfDayTime = tmp_time->tm_sec + tmp_time->tm_min*60 + tmp_time->tm_hour * 3600;
	strftime(target_tariff->time_now, sizeof(target_tariff->time_now), "%04Y-%02m-%02d %H:%M:%S", tmp_time);
	strftime(target_tariff->rr_time_now, sizeof(target_tariff->rr_time_now), "%04Y%02m%02d%H%M%S", tmp_time);
	target_tariff->week_day = tmp_time->tm_wday + 1;

	// Scan Rr record first
	do
	{
		count++;
		// scan bundle first
		//reply = redisCommand(redis_mngt.unix_cxt, "SCAN %d MATCH Rr:%s:%02d:* COUNT 1000", cursor, target_tariff->service_num, target_tariff->service_type);
		reply = redisCommand(redis_mngt.unix_cxt, "SSCAN RrSet:%s:%02d %d", target_tariff->service_num, target_tariff->service_type, cursor);
		if (reply == NULL)
		{
			RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
			return -1;
		}
		int type = reply ? (reply->type) : 0;

		if (type == REDIS_REPLY_STRING || type == REDIS_REPLY_ERROR)
			printf("SCAN reply str %s\r\n", reply->str);
		else if (type == REDIS_REPLY_ARRAY)
		{
			size_t nbelem = reply->elements;

			if (nbelem > 1 && reply->element[1]->type == REDIS_REPLY_ARRAY)
			{
				struct redisReply *replseqkeys = reply->element[1];
				size_t nbcurkeys = replseqkeys->elements, ik;
				//printf("#%ld; SCAN nbcurkeys %zd", count, nbcurkeys);
				for (ik = 0; ik < nbcurkeys; ik++)
				{
					struct redisReply *replcurkey = replseqkeys->element[ik];
					int curkeytype = replcurkey->type;
					if (curkeytype == REDIS_REPLY_STRING)
					{
						nbkeys++;
						//printf("got key#%ld : %s", nbkeys, replcurkey->str);
						// get rr info, and compare
						//if (replcurkey->len >= MIN_RR_KEY_LEN)
						//{
							char rrKey[48];
							sprintf(rrKey, "Rr:%s:%02d:%s",target_tariff->service_num, target_tariff->service_type, replcurkey->str);
							redislog(RS_DEBUG, "Get RR key: %s", rrKey);
							if (RedisGetRrInfo(rrKey, target_tariff, &rr_info) > 0)
							{
								
								// compare and update
							}
						//}
					}
				}
			}

			if ((nbelem > 0) && (reply->element[0]->type == REDIS_REPLY_STRING))
			{
				cursor = atol(reply->element[0]->str);
				printf("#%d; SSCAN updated cursor %d", count, cursor);
			}
			else
			{
				cursor = -1;
				printf("#%d; SSCAN nocursor", count);
			}
		}
		freeReplyObject (reply);
		reply = NULL;
	}while (cursor > 0);

	if (rr_info.prd_inst_id > 0)// get rr bundle succ, return
	{
		setTariffByRrInfo(target_tariff, &rr_info);
		redislog(RS_INFO, "Get RR info succ: plan[%d],rr_id[%d],avail[%lld],used[%lld]",
							rr_info.ofr_inst_id, rr_info.rr_id, rr_info.bundle_value, rr_info.used_value);
		return 0;
	}

	int ofr_inst_id = 0;
	int ofr_id = RedisGetOfrId(target_tariff->service_num, &ofr_inst_id);
	if (ofr_id < 0)
	{
		return -1;
	}
	// scan tariff record
	count = 0;
	cursor = 0;
	nbkeys = 0;
	Ofr_redis_st ofr_info = {0};
	do
	{
		count++;
		// scan bundle first
		//reply = redisCommand(redis_mngt.unix_cxt, "SCAN %d MATCH Tariff:%d:%02d:* COUNT 1000", cursor, ofr_id, target_tariff->service_type);
		reply = redisCommand(redis_mngt.unix_cxt, "SSCAN TariffSet:%d:%02d %d", ofr_id, target_tariff->service_type, cursor);
		if (reply == NULL)
		{
			RedisInit(redis_mngt.local_ip, redis_mngt.peer_ip);
			return -1;
		}
		int type = reply ? (reply->type) : 0;

		if (type == REDIS_REPLY_STRING || type == REDIS_REPLY_ERROR)
			printf("SCAN reply str %s\r\n", reply->str);
		else if (type == REDIS_REPLY_ARRAY)
		{
			size_t nbelem = reply->elements;

			if (nbelem > 1 && reply->element[1]->type == REDIS_REPLY_ARRAY)
			{
				struct redisReply *replseqkeys = reply->element[1];
				size_t nbcurkeys = replseqkeys->elements, ik;
				//printf("#%ld; SCAN nbcurkeys %zd", count, nbcurkeys);
				for (ik = 0; ik < nbcurkeys; ik++)
				{
					struct redisReply *replcurkey = replseqkeys->element[ik];
					int curkeytype = replcurkey->type;
					if (curkeytype == REDIS_REPLY_STRING)
					{
						nbkeys++;
						//printf("got key#%ld : %s", nbkeys, replcurkey->str);
						// get rr info, and compare
						//if (replcurkey->len >= 8)
						//{
							char tariffKey[48];
							sprintf(tariffKey, "Tariff:%d:%02d:%s", ofr_id, target_tariff->service_type, replcurkey->str);
							redislog(RS_DEBUG, "Get Tariff key: %s", tariffKey);
							if (RedisGetTariffInfo1(tariffKey, target_tariff, &ofr_info) > 0)
							{
								break;
								// compare and update
							}
						//}
					}
				}
			}

			if ((nbelem > 0) && (reply->element[0]->type == REDIS_REPLY_STRING))
			{
				cursor = atol(reply->element[0]->str);
				printf("#%d; SSCAN updated cursor %d", count, cursor);
			}
			else
			{
				cursor = -1;
				printf("#%d; SSCAN nocursor", count);
			}
		}
		freeReplyObject (reply);
		reply = NULL;
		if (ofr_info.tariff_id > 0)
		{
			break;
		}
	}while (cursor > 0);

	if (ofr_info.tariff_id > 0)// get ofr succ, return
	{
		ofr_info.discount = 100;
		setTariffByOfrInfo(target_tariff, &ofr_info);
		target_tariff->plan_id = ofr_inst_id;
		
		// Get Holiday Discount
		scanHolidayDiscount(target_tariff, &ofr_info);
		
		redislog(RS_INFO, "Get Ofr info succ: ofr&tariff id[%d/%d], unit[%d],unit charge[%d]",
							ofr_info.ofr_id, ofr_info.tariff_id, ofr_info.unit, ofr_info.unit_charge);
		return 0;
	}
	
	redislog(RS_ERROR, "Get RR and Ofr fail for service_num[%s],service type[%d]",
						target_tariff->service_num, target_tariff->service_type);

	return -1;
}

extern _state_machine	*smshm_ptr;
extern int		logMsgFlag;
//extern _debug_mib  debugmib;
void DebugMsg(void *ptr, const char *fmt, ...);
int RedisGetTariffInfo(u_short portid, char *service_nbr, int service_type, char *called_num)
{
	_state_data *shmp = (_state_data *) smshm_ptr->state_data;    
	
	if(portid>=MAX_FSM_PROCESS)
	{
		return -1;
	}
	shmp += portid;

	redislog(RS_DEBUG, "Try to get tariff for service_num[%s],service type[%d],prefix[%s]",
						service_nbr, service_type, called_num);
	Tariff_redis_st avail_tariff;
	strcpy(avail_tariff.service_num, service_nbr);
	avail_tariff.service_type = service_type;
	strcpy(avail_tariff.prefix, called_num);
	
	int ret = RedisScanTariff(&avail_tariff);

	if(ret != 0)
	{
		shmp->process_info.rest_if.query_tariff_status = 3; //not found

		// log message
		if((logMsgFlag & 0x01) || shmp->process_info.logMsisdn)
		{
			DebugMsg(debugmib.display.asciiOut,
					"[%04d], No tariff for service_num[%s],service type[%d]",
					portid, service_nbr, service_type);
		}
		redislog(RS_WARN, "No tariff for service_num[%s],service type[%d]",
							service_nbr, service_type);
		/*if((logMsgFlag & 0x04) || (logMsgFlag & 0x02))
		{
			DebugMsg(debugmib.display.asciiOut,
				"    TS: %ld.%03d,  %s, no data tariff errcode[%d]", \
				tvnow.tv_sec, tvnow.tv_usec/1000, __FUNCTION__, ret);
		}*/
		
		return 0;
	}
	
	{
		shmp->process_info.rest_if.query_tariff_status = 2; //succeed

		/*
		shmp->process_info.tariff.unit_time = pRes->unit_time;
		shmp->process_info.tariff.tierRating.tierNumber = 0;
		shmp->process_info.tariff.tierRating.rating[0].base_fee = pRes->unit_charge;
		*/
		
		shmp->process_info.rest_if.unit  = avail_tariff.unit;
		shmp->process_info.rest_if.unit_charge = avail_tariff.unit_charge*1.0/100.0; //in CRM, the unit is *100 for better precision
		shmp->process_info.rest_if.tariff_discount = avail_tariff.discount;
		shmp->process_info.rest_if.plan_id = avail_tariff.plan_id;
		shmp->process_info.rest_if.bundle_plan_id = avail_tariff.bundle_plan_id;		
		shmp->process_info.rest_if.total_plan_value = avail_tariff.free_value;
		shmp->process_info.rest_if.used_plan_value = avail_tariff.used_value;
		
		if((logMsgFlag & 0x01) || shmp->process_info.logMsisdn)
		{
			DebugMsg(debugmib.display.asciiOut,
					"[%04d], rrID[%d], ofrInstId[%d], Get tariff: %d*%6.3f * %d, %lld-%lld",
					portid, avail_tariff.bundle_plan_id, avail_tariff.plan_id, avail_tariff.unit, shmp->process_info.rest_if.unit_charge, avail_tariff.discount,
				avail_tariff.free_value, avail_tariff.used_value);
		}
		redislog(RS_DEBUG, "Get tariff: %d*%6.3f * %d, %lld-%lld",
				avail_tariff.unit, shmp->process_info.rest_if.unit_charge, avail_tariff.discount,
				avail_tariff.free_value, avail_tariff.used_value);
		/*if((logMsgFlag & 0x04) || (logMsgFlag & 0x02))
		{
			DebugMsg(debugmib.display.asciiOut,
				"    TS: %ld.%03d,  %s, port[%d] tariff: %d*%6.3f * %d, %lld-%lld", \
				tvnow.tv_sec, tvnow.tv_usec/1000, __FUNCTION__, portid,
				avail_tariff.unit, shmp->process_info.rest_if.unit_charge, avail_tariff.discount,
				avail_tariff.free_value, avail_tariff.used_value);
		}*/
	}
		
	return 1;

}

// Simple log start =====================================================================
#ifndef LOGLEVEL
#define LOGLEVEL RS_DEBUG
#endif

static const char* s_loginfo[] = {
    [RS_ERROR] = "ERROR",
    [RS_WARN]  = "WARN",
    [RS_INFO]  = "INFO",
    [RS_DEBUG] = "DEBUG",
};

static void get_timestamp(char *buffer)
{
    time_t t;
    struct tm *p;
    struct timeval tv;
    int len;
    int millsec;

    t = time(NULL);
    p = localtime(&t);

    gettimeofday(&tv, NULL);
    millsec = (int)(tv.tv_usec / 1000);

    /* 时间格式：[2011-11-15 12:47:34:888] */
    len = snprintf(buffer, 32, "[%04d-%02d-%02d %02d:%02d:%02d:%03d] ",
        p->tm_year+1900, p->tm_mon+1,
        p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, millsec);

    buffer[len] = '\0';
}

void mylog1(const char* filename, int line, enum RedisLogLevel level, const char* fmt, ...)
{
	return;
    if(level > LOGLEVEL)
        return;

    va_list arg_list;
    char buf[256];
    memset(buf, 0, 256);
    va_start(arg_list, fmt);
    vsnprintf(buf, 256, fmt, arg_list);
    char time[32] = {0};
    
    // 去掉*可能*存在的目录路径，只保留文件名
    const char* tmp = strrchr(filename, '/');
    if (!tmp) tmp = filename;
    else tmp++;
    get_timestamp(time);

    //printf("%s[%s] [%s:%d] %s\n", time, s_loginfo[level], tmp, line, buf);
	RedisAddLogStr(time, s_loginfo[level], tmp, line, buf);

    va_end(arg_list);

}
// Simple log end =====================================================================

// Redis log writer
static OCS_redis_log_t redis_log = {0, 0};
static int curHour = 24;
static char auditFileName[64] = {0};
static char auditOpenPath[128] = {0};
static FILE *fp = NULL;

static int RedisCreateFirstLog()
{
    time_t currentTime;
    struct 	tm *log_time;
    
    currentTime = time(NULL);
    log_time = localtime(&currentTime);
    /* format file name */
    sprintf(auditFileName, "OcsRedis_%04d-%02d-%02d-%02d:%02d:%02d.log", log_time->tm_year + 1900, log_time->tm_mon +1, \
            log_time->tm_mday, log_time->tm_hour, log_time->tm_min, log_time->tm_sec);

    sprintf(auditOpenPath, "/var/log/redis/%s", auditFileName);
    curHour = log_time->tm_hour;

    return 0;
}

static int RedisCheckCurHour()
{
    time_t currentTime;
    struct 	tm *log_time;

    currentTime = time(NULL);
    log_time = localtime(&currentTime);
    if(log_time->tm_hour != curHour)// new hour begin, create new log name
    {
        curHour = log_time->tm_hour;

        /* create the next audit file */
        sprintf(auditFileName, "OcsRedis_%04d-%02d-%02d-%02d:%02d:00.log", log_time->tm_year + 1900, log_time->tm_mon +1, \
                log_time->tm_mday, log_time->tm_hour, log_time->tm_min);
        sprintf(auditOpenPath, "/var/log/redis/%s", auditFileName);
    }

    return 0;
}

static int RedisLogWrite()
{
    char *pStr = NULL;
    RedisCheckCurHour();
    
	if(redis_log.header != redis_log.tailer)
	{
        if((fp = fopen(auditOpenPath, "a")) != NULL)
        {
            do
            {
                pStr = redis_log.log_str[redis_log.header];
                fwrite(pStr, 1, strlen(pStr), fp);

                /* clear this item after it was written to file */
                //memset(pStr, 0, sizeof(MAX_REDIS_LOG_LINES));
                redis_log.header = (redis_log.header+1) % (MAX_REDIS_LOG_LINES);
            }while(redis_log.header != redis_log.tailer);
            fclose(fp);
        }
        else
        {
            printf("RedisLogWrite(): fail to open log!");
        }
    }
    
    return 0;
}


void RedisAddLogStr(char *time, const char *level, const char *filename, int line, char *text)
{
    //char *pStr = NULL;
	
    if((redis_log.tailer+1) % MAX_REDIS_LOG_LINES == redis_log.header)
    {// buffer is full
        //printf("RedisAddLogStr(): list full, fail to add log!");
        return;
    }
    else
    {
        //strncpy(redis_log.log_str[redis_log.tailer], text, MAX_REDIS_LINE_LEN-1);
		snprintf(redis_log.log_str[redis_log.tailer], 512, "%s[%s] [%s:%d] %s\n", \
				time, level, filename, line, text);
        redis_log.tailer = (redis_log.tailer+1) % MAX_REDIS_LOG_LINES;
    }
    
    return;
}

static void *RedisWriteLogTask(void *pParam)
{
    RedisCreateFirstLog();

	while(1)
	{
        RedisLogWrite();
		usleep(10);
	}
	return NULL;
}

int RedisCreateLogTask(pthread_t *idTask)
{
	pthread_attr_t attr;
	struct sched_param priority;

	if(0 != pthread_attr_init(&attr))
	{
		return -1;
	}
	if(0 != pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM))/*bind light process*/
	{
		return -1;
	}
	if(0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))/*detach thread*/
	{
		return -1;
	}

	/*set the thread priority and strategy, before create the thread*/
	if(0 != pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) /*return 0 if success*/
	{
		return -1;
	}
	priority.sched_priority = 75;//priority higher
	if(0 != pthread_attr_setschedparam(&attr, (struct sched_param*)&priority))
	{
		return -1;
	}

	if(0 != pthread_create(idTask, &attr, RedisWriteLogTask, NULL))
	{
		return -1;
	}

	/*pthread_setschedparam (*idTask, SCHED_FIFO, &priority);*/
	return 0;
}



