#include "../include/pps_redis.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>

// all Redis field of one KEY
#define OCS_REDIS_FIELD_NULL 0
#define OCS_REDIS_FIELD_BALANCE 1
#define OCS_REDIS_FIELD_PROMO_BALANCE 2
#define OCS_REDIS_FIELD_BUNDLE_DATA 3
#define OCS_REDIS_FIELD_BUNDLE_MOV 4
#define OCS_REDIS_FIELD_BUNDLE_MTV 5
#define OCS_REDIS_FIELD_BUNDLE_SMS 6
#define OCS_REDIS_FIELD_BUNDLE_VALIDITY 7
#define OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG 8
#define OCS_REDIS_FIELD_MAX_NUM 9

const char *OCS_REDIS_FN[OCS_REDIS_FIELD_MAX_NUM] = {
"NULL",// 0
"BALANCE",// 1
"PROMO_BALANCE",// 2
"BUNDLE_DATA",// 3
"BUNDLE_MOV",// 4
"BUNDLE_MTV",// 5
"BUNDLE_SMS",// 6
"BUNDLE_VALIDITY",// 7
"BUNDLE_WARN_SMS_FLAG",// 8
};

/*
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 peer_ip;
	redisContext *unix_cxt;
	redisContext *peer_cxt;
}OCS_redis_struct;
static OCS_redis_struct redis_mngt = {TRUE, 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.unix_cxt = redisConnectUnixWithTimeout("/var/run/redis.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);
	}

	// if local redis is master
	redisReply *reply = NULL;
	reply = redisCommand(redis_mngt.unix_cxt, "INFO replication");
	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);
	//

	// 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;
	}

	//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/redis.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 RedisSetUserBalanceFromOcs(_prepaid_info *pUsr, double balance)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	char msisdn[32];
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    msisdn[len] = 0;

	int ret = 0;
	int i;
	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,"HSET %s %s %f", 
							msisdn, OCS_REDIS_FN[OCS_REDIS_FIELD_BALANCE], balance);
		
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 set balance to %.3f!", msisdn, balance);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}


int RedisUpdateUserBalance(OCS_usr_redis *usr_redis)
{
	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, 
							"HMSET %s %s %f %s %f", usr_redis->msisdn, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BALANCE], usr_redis->usr_balance, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_PROMO_BALANCE], usr_redis->pro_balance);
		//printf("HMSET (new mbox): %lld\n", reply->integer);
	
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 set balance to %.3f, pro_balance to %.3f!", \
					usr_redis->msisdn, usr_redis->usr_balance, usr_redis->pro_balance);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", usr_redis->msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

int RedisUpdateUserInfo(OCS_usr_redis *usr_redis)
{
	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, 
							"HMSET %s %s %f %s %f %s %lld %s %d %s %d %s %hu %s %lu %s %d", usr_redis->msisdn, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BALANCE], usr_redis->usr_balance, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_PROMO_BALANCE], usr_redis->pro_balance, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_DATA], usr_redis->data, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MOV], usr_redis->mo_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MTV], usr_redis->mt_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_SMS], usr_redis->sms, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_VALIDITY], usr_redis->validity_date, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG], usr_redis->warning_sms_flag);
		//printf("HMSET (new mbox): %lld\n", reply->integer);
	
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 set balance[%.3f], pro_balance[%.3f], data[%lld], mov[%d], mtv[%d], sms[%d], validity[%lu]!", \
					usr_redis->msisdn, usr_redis->usr_balance, usr_redis->pro_balance, usr_redis->data, \
					usr_redis->mo_voice, usr_redis->mt_voice, usr_redis->sms, usr_redis->validity_date);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", usr_redis->msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

int RedisUpdateUserInfoFromOcs(_prepaid_info *pUsr)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	char msisdn[32];
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    msisdn[len] = 0;

	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, 
							"HMSET %s %s %f %s %f %s %lld %s %d %s %d %s %hu %s %lu %s %d", msisdn, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BALANCE], pUsr->balance, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_PROMO_BALANCE], pUsr->promo_balance, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_DATA], pUsr->bundle_info.data, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MOV], pUsr->bundle_info.mo_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MTV], pUsr->bundle_info.mt_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_SMS], pUsr->bundle_info.sms, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_VALIDITY], pUsr->bundle_info.validity_date, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG], pUsr->bundle_info.warning_sms_flag);
		//printf("HMSET (new mbox): %lld\n", reply->integer);
	
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 set balance[%.3f], pro_balance[%.3f], data[%lld], mov[%d], mtv[%d], sms[%d], validity[%lu]!", \
					msisdn, pUsr->balance, pUsr->promo_balance, pUsr->bundle_info.data, \
					pUsr->bundle_info.mo_voice, pUsr->bundle_info.mt_voice, pUsr->bundle_info.sms, \
					pUsr->bundle_info.validity_date);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

int RedisUpdateBundleInfoFromOcs(_prepaid_info *pUsr)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	char msisdn[32];
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    msisdn[len] = 0;

	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, 
							"HMSET %s %s %lld %s %d %s %d %s %hu %s %lu %s %d", msisdn, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_DATA], pUsr->bundle_info.data, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MOV], pUsr->bundle_info.mo_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MTV], pUsr->bundle_info.mt_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_SMS], pUsr->bundle_info.sms, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_VALIDITY], pUsr->bundle_info.validity_date, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG], pUsr->bundle_info.warning_sms_flag);
		//printf("HMSET (new mbox): %lld\n", reply->integer);
	
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 set data[%lld], mov[%d], mtv[%d], sms[%d], validity[%lu], warn_sms[%d]!", \
					msisdn, pUsr->bundle_info.data, \
					pUsr->bundle_info.mo_voice, pUsr->bundle_info.mt_voice, pUsr->bundle_info.sms, \
					pUsr->bundle_info.validity_date, pUsr->bundle_info.warning_sms_flag);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

int RedisUpdateUsrFieldFromOcs(_prepaid_info *pUsr, int field_id)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	char msisdn[32];
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    msisdn[len] = 0;

	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;
		}
		
		switch (field_id)
		{
		case OCS_REDIS_FIELD_BALANCE:
			reply = redisCommand(cxt,"HSET %s %s %f", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->balance);
			redislog(RS_DEBUG, "%s set balance[%.3f]!", msisdn, pUsr->balance);
			break;
		case OCS_REDIS_FIELD_PROMO_BALANCE:
			reply = redisCommand(cxt,"HSET %s %s %f", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->promo_balance);
			redislog(RS_DEBUG, "%s set pro_balance[%.3f]!", msisdn, pUsr->promo_balance);
			break;
		case OCS_REDIS_FIELD_BUNDLE_DATA:
			reply = redisCommand(cxt,"HSET %s %s %lld", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->bundle_info.data);
			redislog(RS_DEBUG, "%s set data[%lld]!", msisdn, pUsr->bundle_info.data);
			break;
		case OCS_REDIS_FIELD_BUNDLE_MOV:
			reply = redisCommand(cxt,"HSET %s %s %d", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->bundle_info.mo_voice);
			redislog(RS_DEBUG, "%s set mov[%d]!", msisdn, pUsr->bundle_info.mo_voice);
			break;
		case OCS_REDIS_FIELD_BUNDLE_MTV:
			reply = redisCommand(cxt,"HSET %s %s %d", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->bundle_info.mt_voice);
			redislog(RS_DEBUG, "%s set mtv[%d]!", msisdn, pUsr->bundle_info.mt_voice);
			break;
		case OCS_REDIS_FIELD_BUNDLE_SMS:
			reply = redisCommand(cxt,"HSET %s %s %hu", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->bundle_info.sms);
			redislog(RS_DEBUG, "%s set sms[%d]!", msisdn, pUsr->bundle_info.sms);
			break;
		case OCS_REDIS_FIELD_BUNDLE_VALIDITY:
			reply = redisCommand(cxt,"HSET %s %s %lu", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->bundle_info.validity_date);
			redislog(RS_DEBUG, "%s set validity[%lu]!", msisdn, pUsr->bundle_info.validity_date);
			break;
		case OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG:
			reply = redisCommand(cxt,"HSET %s %s %d", 
								msisdn, OCS_REDIS_FN[field_id], pUsr->bundle_info.warning_sms_flag);
			redislog(RS_DEBUG, "%s set warn sms flag[%d]!", msisdn, pUsr->bundle_info.warning_sms_flag);
			break;
		default:
			return -1;
		}
		
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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;
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", pUsr->msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

int RedisClearUserBundle(OCS_usr_redis *pUsr)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	pUsr->data = 0;
	pUsr->mo_voice = 0;
	pUsr->mt_voice = 0;
	pUsr->sms = 0;
	pUsr->validity_date = 0;
	pUsr->warning_sms_flag = 0;
	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, 
							"HMSET %s %s %lld %s %d %s %d %s %hu %s %lu %s %d", pUsr->msisdn, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_DATA], pUsr->data, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MOV], pUsr->mo_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MTV], pUsr->mt_voice, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_SMS], pUsr->sms, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_VALIDITY], pUsr->validity_date, 
							OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG], pUsr->warning_sms_flag);
		//printf("HMSET (new mbox): %lld\n", reply->integer);
	
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 clear bundle!", pUsr->msisdn);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", pUsr->msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

extern _ram_data *ramshm_ptr;
// run from debug command line: redis load
void RedisCreateDbFromOcs()
{
	int  i, j, len;
	_prepaid_info  *pUsr = NULL;
	
	for(j=0; j<MSISDN_HEAD_NUM; j++)
	for(i=0; i<UNIT_STORE_ACCOUNT; i++)
	{
	  pUsr = &ramshm_ptr->prepaid_data.prepaid_info[j][i];
	  len = pUsr->msisdn[9];
	  if(len==0) continue;

	  //balance = balance + shmp->balance + shmp->promo_balance;
	  RedisUpdateUserInfoFromOcs(pUsr);
	}
	
	redislog(RS_INFO, "create Redis DB from OCS!");
}


// run from debug command line: redis save
void RedisSaveDbToOcs()
{
	int  i, j, len;
	_prepaid_info  *pUsr = NULL;
	
	for(j=0; j<MSISDN_HEAD_NUM; j++)
	for(i=0; i<UNIT_STORE_ACCOUNT; i++)
	{
	  pUsr = &ramshm_ptr->prepaid_data.prepaid_info[j][i];
	  len = pUsr->msisdn[9];
	  if(len==0) continue;

	  //balance = balance + shmp->balance + shmp->promo_balance;
	  RedisGetUserInfo2Ocs(pUsr);
	}
	
	redislog(RS_INFO, "load Redis DB to OCS!");
}


/*void RedisLoadFromRedis()
{
	redisReply *reply = NULL;
	int count = 0, cursor = 0, nbkeys = 0;
	VMS_mbox_struct mbox_tmp;
	VMS_mbox_struct *mbox_ptr = &mbox_tmp;

	do
	{
		count++;
		reply = redisCommand(redis_mngt.unix_cxt, "SCAN %d", cursor);
		int type = reply ? (reply->type) : 0;

		if (type == REDIS_REPLY_STRING || type == REDIS_REPLY_ERROR)
			printf("SCAN reply str %s", 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;
				//printf("#%ld; SCAN nbcurkeys %zd", count, nbcurkeys);
				for (size_t 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);
						memset(mbox_ptr, 0, sizeof(VMS_mbox_struct));
						if (replcurkey->len == VMS_ISDN_LEN)
						{
							memcpy(mbox_ptr->msisdn, replcurkey->str, VMS_ISDN_LEN);
							if (RedisGetMboxInfo(mbox_ptr) > 0)
							{
								vms_update_mailbox(mbox_ptr);
							}
						}
					}
				}
			}

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

int RedisUpdateUserField(OCS_usr_redis *pUsr, int field_id)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	/*char msisdn[32];
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
        return -1;
    }
    BcdToAscii(msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    msisdn[len] = 0;*/

	int ret = 0;
	int i;
	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;
		}
		
		switch (field_id)
		{
		case OCS_REDIS_FIELD_BALANCE:
			reply = redisCommand(cxt,"HSET %s %s %f", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->usr_balance);
			redislog(RS_DEBUG, "%s set balance[%.3f]!", pUsr->msisdn, pUsr->usr_balance);
			break;
		case OCS_REDIS_FIELD_PROMO_BALANCE:
			reply = redisCommand(cxt,"HSET %s %s %f", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->pro_balance);
			redislog(RS_DEBUG, "%s set pro_balance[%.3f]!", pUsr->msisdn, pUsr->pro_balance);
			break;
		case OCS_REDIS_FIELD_BUNDLE_DATA:
			reply = redisCommand(cxt,"HSET %s %s %lld", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->data);
			redislog(RS_DEBUG, "%s set data[%lld]!", pUsr->msisdn, pUsr->data);
			break;
		case OCS_REDIS_FIELD_BUNDLE_MOV:
			reply = redisCommand(cxt,"HSET %s %s %d", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->mo_voice);
			redislog(RS_DEBUG, "%s set data[%d]!", pUsr->msisdn, pUsr->mo_voice);
			break;
		case OCS_REDIS_FIELD_BUNDLE_MTV:
			reply = redisCommand(cxt,"HSET %s %s %d", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->mt_voice);
			redislog(RS_DEBUG, "%s set mtv[%d]!", pUsr->msisdn, pUsr->mt_voice);
			break;
		case OCS_REDIS_FIELD_BUNDLE_SMS:
			reply = redisCommand(cxt,"HSET %s %s %hu", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->sms);
			redislog(RS_DEBUG, "%s set sms[%d]!", pUsr->msisdn, pUsr->sms);
			break;
		case OCS_REDIS_FIELD_BUNDLE_VALIDITY:
			reply = redisCommand(cxt,"HSET %s %s %lu", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->validity_date);
			redislog(RS_DEBUG, "%s set data[%lu]!", pUsr->msisdn, pUsr->validity_date);
			break;
		case OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG:
			reply = redisCommand(cxt,"HSET %s %s %d", 
								pUsr->msisdn, OCS_REDIS_FN[field_id], pUsr->warning_sms_flag);
			redislog(RS_DEBUG, "%s set warn sms flag[%d]!", pUsr->msisdn, pUsr->warning_sms_flag);
			break;
		default:
			return -1;
		}
		
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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;
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s fail, error: %s!", pUsr->msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
	}

	return ret;
}

int RedisGetUserInfo(_prepaid_info *pUsr, OCS_usr_redis *usr_redis)
{
	redisReply *reply = NULL;
	memset(usr_redis, 0, sizeof(OCS_usr_redis));

	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(usr_redis->msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    usr_redis->msisdn[len] = 0;

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

	reply = redisCommand(redis_mngt.unix_cxt,"HMGET %s %s %s %s %s %s %s %s %s", usr_redis->msisdn, 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BALANCE], OCS_REDIS_FN[OCS_REDIS_FIELD_PROMO_BALANCE], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_DATA], OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MOV], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MTV], OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_SMS], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_VALIDITY], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG]);
	//printf("HMGET (name_greeting): %s\n", reply->str);

	if (reply == NULL)
	{
		RedisRelMaster();
		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 OCS_REDIS_FIELD_BALANCE:
				count++;
				usr_redis->usr_balance = strtod(reply->element[j]->str, NULL);
				break;
			case OCS_REDIS_FIELD_PROMO_BALANCE:
				count++;
				usr_redis->pro_balance = strtod(reply->element[j]->str, NULL);
				break;
			case OCS_REDIS_FIELD_BUNDLE_DATA:
				usr_redis->data = atoll(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_MOV:
				usr_redis->mo_voice = atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_MTV:
				usr_redis->mt_voice = atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_SMS:
				usr_redis->sms = (u_short)atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_VALIDITY:
				usr_redis->validity_date = strtoul(reply->element[j]->str, NULL, 0);
				break;
			case OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG:
				usr_redis->warning_sms_flag = atoi(reply->element[j]->str);
				break;
			default:
				break;
			}
		}
	}
	else
	{
		redislog(RS_ERROR, "%s fail, error: %s!", usr_redis->msisdn, (reply->str!=NULL)?reply->str:"unknown");
	}
	
	freeReplyObject(reply);

	if (count < 2)
	{
		return 0;
	}
	
	return 1;
}

int RedisGetBundleInfo(_prepaid_info *pUsr, OCS_usr_redis *usr_redis)
{
	redisReply *reply = NULL;
	memset(usr_redis, 0, sizeof(OCS_usr_redis));

	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(usr_redis->msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    usr_redis->msisdn[len] = 0;

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

	reply = redisCommand(redis_mngt.unix_cxt,"HMGET %s %s %s %s %s %s %s", usr_redis->msisdn, 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_DATA], OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MOV], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MTV], OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_SMS], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_VALIDITY], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG]);
	//printf("HMGET (name_greeting): %s\n", reply->str);

	if (reply == NULL)
	{
		RedisRelMaster();
		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+3)
			{
			case OCS_REDIS_FIELD_BUNDLE_DATA:
				usr_redis->data = atoll(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_MOV:
				usr_redis->mo_voice = atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_MTV:
				usr_redis->mt_voice = atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_SMS:
				usr_redis->sms = (u_short)atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_VALIDITY:
				usr_redis->validity_date = strtoul(reply->element[j]->str, NULL, 0);
				break;
			case OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG:
				usr_redis->warning_sms_flag = atoi(reply->element[j]->str);
				break;
			default:
				break;
			}
		}
	}
	else
	{
		redislog(RS_ERROR, "%s fail, error: %s!", usr_redis->msisdn, (reply->str!=NULL)?reply->str:"unknown");
	}
	
	freeReplyObject(reply);

	return 1;
}

int RedisGetUserInfo2Ocs(_prepaid_info *pUsr)
{
	redisReply *reply = NULL;

	char msisdn[32];
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    msisdn[len] = 0;

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

	reply = redisCommand(redis_mngt.unix_cxt,"HMGET %s %s %s %s %s %s %s %s %s", msisdn, 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BALANCE], OCS_REDIS_FN[OCS_REDIS_FIELD_PROMO_BALANCE], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_DATA], OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MOV], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_MTV], OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_SMS], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_VALIDITY], 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG]);
	//printf("HMGET (name_greeting): %s\n", reply->str);

	if (reply == NULL)
	{
		RedisRelMaster();
		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 OCS_REDIS_FIELD_BALANCE:
				count++;
				pUsr->balance = strtod(reply->element[j]->str, NULL);
				break;
			case OCS_REDIS_FIELD_PROMO_BALANCE:
				count++;
				pUsr->promo_balance = strtod(reply->element[j]->str, NULL);
				break;
			case OCS_REDIS_FIELD_BUNDLE_DATA:
				pUsr->bundle_info.data = atoll(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_MOV:
				pUsr->bundle_info.mo_voice = atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_MTV:
				pUsr->bundle_info.mt_voice = atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_SMS:
				pUsr->bundle_info.sms = (u_short)atoi(reply->element[j]->str);
				break;
			case OCS_REDIS_FIELD_BUNDLE_VALIDITY:
				pUsr->bundle_info.validity_date = strtoul(reply->element[j]->str, NULL, 0);
				break;
			case OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG:
				pUsr->bundle_info.warning_sms_flag = atoi(reply->element[j]->str);
				break;
			default:
				break;
			}
		}
	}
	else
	{
		redislog(RS_ERROR, "%s fail, error: %s!", msisdn, (reply->str!=NULL)?reply->str:"unknown");
	}
	
	freeReplyObject(reply);

	if (count < 2)
	{
		return 0;
	}
	
	return 1;
}

int RedisGetUserField(_prepaid_info *pUsr, OCS_usr_redis *usr_redis, int field_id)
{
	redisReply *reply = NULL;
	memset(usr_redis, 0, sizeof(OCS_usr_redis));

	// get str msisdn
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(usr_redis->msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    usr_redis->msisdn[len] = 0;
	//

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

	reply = redisCommand(redis_mngt.unix_cxt,"HGET %s %s", usr_redis->msisdn, 
						OCS_REDIS_FN[field_id]);
	//printf("HMGET (name_greeting): %s\n", reply->str);

	if (reply == NULL)
	{
		RedisRelMaster();
		return -1;
	}
	
	// read data
	if ((reply->type == REDIS_REPLY_STRING) && (reply->len > 0))
	{
		switch (field_id)
		{
		case OCS_REDIS_FIELD_BALANCE:
			usr_redis->usr_balance = strtod(reply->str, NULL);
			break;
		case OCS_REDIS_FIELD_PROMO_BALANCE:
			usr_redis->pro_balance = strtod(reply->str, NULL);
			break;
		case OCS_REDIS_FIELD_BUNDLE_DATA:
			usr_redis->data = atoll(reply->str);
			break;
		case OCS_REDIS_FIELD_BUNDLE_MOV:
			usr_redis->mo_voice = atoi(reply->str);
			break;
		case OCS_REDIS_FIELD_BUNDLE_MTV:
			usr_redis->mt_voice = atoi(reply->str);
			break;
		case OCS_REDIS_FIELD_BUNDLE_SMS:
			usr_redis->sms = (u_short)atoi(reply->str);
			break;
		case OCS_REDIS_FIELD_BUNDLE_VALIDITY:
			usr_redis->validity_date = strtoul(reply->str, NULL, 0);
			break;
		case OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG:
			usr_redis->warning_sms_flag = atoi(reply->str);
			break;
		default:
			return -1;
		}

		freeReplyObject(reply);
		return 0;
	}
	else
	{
		redislog(RS_ERROR, "%s get field[%d] fail, error: %s!", usr_redis->msisdn, field_id, (reply->str!=NULL)?reply->str:"unknown");
		freeReplyObject(reply);
		return -1;
	}
}

int RedisGetUserBalance(_prepaid_info *pUsr, OCS_usr_redis *usr_redis)
{
	redisReply *reply = NULL;
	memset(usr_redis, 0, sizeof(OCS_usr_redis));

	// get str msisdn
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(usr_redis->msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    usr_redis->msisdn[len] = 0;
	//

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

	reply = redisCommand(redis_mngt.unix_cxt,"HMGET %s %s %s", usr_redis->msisdn, 
						OCS_REDIS_FN[OCS_REDIS_FIELD_BALANCE], OCS_REDIS_FN[OCS_REDIS_FIELD_PROMO_BALANCE]);
	//printf("HMGET (name_greeting): %s\n", reply->str);

	if (reply == NULL)
	{
		RedisRelMaster();
		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 OCS_REDIS_FIELD_BALANCE:
				count++;
				usr_redis->usr_balance = strtod(reply->element[j]->str, NULL);
				break;
			case OCS_REDIS_FIELD_PROMO_BALANCE:
				count++;
				usr_redis->pro_balance = strtod(reply->element[j]->str, NULL);
				break;
			default:
				break;
			}
		}
	}
	else
	{
		redislog(RS_ERROR, "%s fail, error: %s!", usr_redis->msisdn, (reply->str!=NULL)?reply->str:"unknown");
	}
	
	freeReplyObject(reply);

	if (count < 2)
	{
		return 0;
	}
	
	return 1;
}

int RedisDelUserInfo(_prepaid_info *pUsr)
{
	redisReply *reply = NULL;
	redisContext *cxt = NULL;

	char msisdn[32];
	int len = pUsr->msisdn[MAX_MSISDN_BLEN-1];
    if(len<1)
    {
		redislog(RS_ERROR, "err msisdn!");
        return -1;
    }
    BcdToAscii(msisdn, pUsr->msisdn, (MAX_MSISDN_BLEN-1)*2);
    msisdn[len] = 0;

	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, "DEL %s", msisdn);
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 deleted!", msisdn);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s del fail, error: %s!", msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
		//printf("DEL (mbox): %s\n", reply->str);
	}

	return ret;
}

int RedisDelUserInfoByMsisdn(char *msisdn)
{
	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, "DEL %s", msisdn);
		if (reply == NULL)// connection lost
		{
			RedisRelMaster();
			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 deleted!", msisdn);
			break;
		}
		else
		{
			redislog(RS_ERROR, "%s del fail, error: %s!", msisdn, (reply->str!=NULL)?reply->str:"unknown");
			freeReplyObject(reply);
			break;
		}
		//printf("DEL (mbox): %s\n", reply->str);
	}

	return ret;
}

void RedisTest(int i)
{/*
	//static int test = 0;
	redisReply *reply;

	// Try a HSET, reply->str: READONLY You can't write against a read only slave.
	reply = redisCommand(redis_mngt.unix_cxt, "HSET %b %s %s", "8675590001", (size_t) 10, "name", "zjd");
	printf("HSET name: %s\n", reply->str);
	if ((reply->type == REDIS_REPLY_ERROR) && (strncmp(reply->str, "READONLY", 8) == 0))
	{
		redis_mngt.is_local_master = FALSE;
		freeReplyObject(reply);
		reply = redisCommand(redis_mngt.peer_cxt, "HSET %s %s %s", "8675590001", "name", "zjd");
		printf("HSET name: %s\n", reply->str);// HSET name: (null)// reply->type == REDIS_REPLY_INTEGER; reply->integer == 1;
	}
	freeReplyObject(reply);*/
	_prepaid_info ocs_user;
    memcpy(ocs_user.msisdn, "\x86\x75\x50\x01", 4);
    ocs_user.msisdn[9] = 8;
    double balance = 1.1;

    OCS_usr_redis redis_user;
    strcpy(redis_user.msisdn, "86755001");

	switch (i)
	{
        case 1:
            balance = 1.1;
            RedisSetUserBalanceFromOcs(&ocs_user, balance);
            break;
        case 2:
            redis_user.usr_balance = 2.2;
            redis_user.pro_balance = 3.2;
            RedisUpdateUserBalance(&redis_user);
            break;
        case 3:
            redis_user.usr_balance = 2.3;
            redis_user.pro_balance = 3.3;
            redis_user.data = 1003;
            redis_user.mo_voice = 3;
            redis_user.mt_voice = 3;
            redis_user.sms =33;
            redis_user.validity_date = 3003;
            redis_user.warning_sms_flag = 3;
            RedisUpdateUserInfo(&redis_user);
            break;
        case 4:
            ocs_user.balance = 2.4;
            ocs_user.promo_balance = 3.4;
            ocs_user.bundle_info.data = 1004;
            ocs_user.bundle_info.mo_voice = 4;
            ocs_user.bundle_info.mt_voice = 4;
            ocs_user.bundle_info.sms = 34;
            ocs_user.bundle_info.validity_date = 3004;
            ocs_user.bundle_info.warning_sms_flag = 4;
            RedisUpdateUserInfoFromOcs(&ocs_user);
            break;
        case 5:
            ocs_user.bundle_info.data = 1005;
            ocs_user.bundle_info.mo_voice = 5;
            ocs_user.bundle_info.mt_voice = 5;
            ocs_user.bundle_info.sms = 35;
            ocs_user.bundle_info.validity_date = 3005;
            ocs_user.bundle_info.warning_sms_flag = 5;
            RedisUpdateBundleInfoFromOcs(&ocs_user);
            break;
        case 6:
            ocs_user.balance = 1000002.6;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_BALANCE);
            ocs_user.promo_balance = 1000003.6;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_PROMO_BALANCE);
            ocs_user.bundle_info.data = 10241024102410246;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_BUNDLE_DATA);
            ocs_user.bundle_info.mo_voice = 75536;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_BUNDLE_MOV);
            ocs_user.bundle_info.mt_voice = 65536;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_BUNDLE_MTV);
            ocs_user.bundle_info.sms = 3006;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_BUNDLE_SMS);
            ocs_user.bundle_info.validity_date = 0x1234567890666666;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_BUNDLE_VALIDITY);
            ocs_user.bundle_info.warning_sms_flag = 6;
            RedisUpdateUsrFieldFromOcs(&ocs_user, OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG);
            break;
        case 7:
            RedisClearUserBundle(&redis_user);
            break;
        case 8:
            RedisGetUserInfo(&ocs_user, &redis_user);
            break;
        case 9:
            RedisGetBundleInfo(&ocs_user, &redis_user);
            break;
        case 10:
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_BALANCE);
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_PROMO_BALANCE);
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_BUNDLE_DATA);
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_BUNDLE_MOV);
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_BUNDLE_MTV);
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_BUNDLE_SMS);
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_BUNDLE_VALIDITY);
            RedisGetUserField(&ocs_user, &redis_user, OCS_REDIS_FIELD_BUNDLE_WARN_SMS_FLAG);
            break;
        case 11:
            RedisGetUserBalance(&ocs_user, &redis_user);
            break;
        case 12:
            RedisDelUserInfo(&ocs_user);
            break;
        case 13:
            RedisDelUserInfoByMsisdn("86755001");
            break;
        default:
            break;
	}
#if 0
	// Try a HSET, reply->str: READONLY You can't write against a read only slave.
	reply = redisCommand(redis_mngt.unix_cxt, "HSET %s %s %s", "8675590001", "name", "zjd");
	printf("HSET name: %s\n", reply->str);
	if ((reply->type == REDIS_REPLY_ERROR) && (strncmp(reply->str, "READONLY", 8) == 0))
	{
		redis_mngt.is_local_master = false;
		freeReplyObject(reply);
		reply = redisCommand(redis_mngt.peer_cxt, "HSET %s %s %s", "8675590001", "name", "zjd");
		printf("HSET name: %s\n", reply->str);// HSET name: (null)// reply->type == REDIS_REPLY_INTEGER; reply->integer == 1;
	}
	freeReplyObject(reply);
#endif
	/*test++;
	if (test == 100)
	{
		// PING server
		reply = redisCommand(redis_mngt.unix_cxt, "PING");
		printf("PING: %s\n", reply->str);
		freeReplyObject(reply);

		// Set a key
		reply = redisCommand(redis_mngt.unix_cxt, "HSET %s %s %s", "8675590001", "name", "jd");
		printf("SET: %s\n", reply->str);
		freeReplyObject(reply);

		// Set a key using binary safe API
		reply = redisCommand(redis_mngt.unix_cxt, "HSET %s %b %b", "8675590001", "bar", (size_t) 3, "hello", (size_t) 5);
		printf("SET (binary API): %s\n", reply->str);
		freeReplyObject(reply);

		// Try a HGET
		reply = redisCommand(redis_mngt.unix_cxt, "HGET %s %s", "8675590001", "name");
		printf("GET name: %s\n", reply->str);
		freeReplyObject(reply);
	}*/
}

// 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, ...)
{
    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, char *level, 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;
}



