
#include    "../include/includes.h"
#include    "../include/ppsm.h"
#include    "../include/ramdata.h"
#include    "../include/ppsinf.h"
#include    "../include/message.h"
#include    "../include/iofunc.h"
#include    "../include/version.h"
#include    "../include/pps_public.h"
#include    "../include/cdrme.h"
#include 	"../include/param.h"

#include 	"../include/gprs.h"
#include    <stddef.h>
#include    "../include/plat_inc.h"
#include    "../../../../plat/diameter/include/gy_message.h"

/*------------------------------------------------*/
#define BYTES_PER_BASIC_FEE				(BYTES_PER_KB*(tariff->chargeUnit>1?tariff->chargeUnit:1))
#define MINI_CHARGED_BYTES				(tariff->miniChargeUnit*BYTES_PER_BASIC_FEE)
#define MINI_CHARGED_FEE				(tariff->miniChargeUnit*tariff->basicFee + tariff->initialAdjustment)

#define MIN_VOLUME_QUOTA_BYTES			1024
#define MIN_DURATION_QUOTA_SECOND		(60*15)

#define	MAX_VOLUME_BYTES_EACH_TIME		(radiusItem->maxVolumeQuotaEachTime > MIN_VOLUME_QUOTA_BYTES?radiusItem->maxVolumeQuotaEachTime:MIN_VOLUME_QUOTA_BYTES)
#define	MAX_DURATION_SEC_EACH_TIME		(radiusItem->maxDurationQuotaEachTime > MIN_DURATION_QUOTA_SECOND?radiusItem->maxVolumeQuotaEachTime:MIN_DURATION_QUOTA_SECOND)

#define	MAX_VQ_BYTES_EACH_TIME_POST		(1024*1024*5)/*	5MB	*/
#define	MAX_VTH_BYTES_DIFF_POST			(1024*1024*1)/*1MB */

#define MAX_BYTES_PER_SESSION			(((long long)100)*1024*1024*1024)/*	10G	*/

#define pps_set_gprs_state(val) do { \
	gprsProc->state = val; \
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]\33[32m%s\33[0m transfer to state \33[36m%s\33[0m", portid, __FUNCTION__, #val); \
} while(0)


#define pps_set_postpaid_state(val) do { \
	gprsProc->postpaidState = val; \
	gprsProc->accessRequestTimer = 0;\
	gprsProc->radiusCommand = PPS_RADIUS_NO_CMD;\
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]\33[32m%s\33[0m transfer to state \33[36m%s\33[0m", portid, __FUNCTION__, #val); \
} while(0)

#define pps_set_prepaid_state(val) do { \
	gprsProc->prepaidState = val;\
	gprsProc->accessRequestTimer = 0;\
	gprsProc->radiusCommand = PPS_RADIUS_NO_CMD;\
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]\33[32m%s\33[0m transfer to state \33[36m%s\33[0m", portid, __FUNCTION__, #val); \
} while(0)

extern 	int 							subSystemID;
extern _state_machine					*smshm_ptr;
extern 	PPSParamStruct					ppsParam;
extern	_ram_data						*ramshm_ptr;
extern 	_tariff_private 				*blf_shmp;
extern 	_pps_report						pps_report;

extern 	struct	timeval 				tvnow;

/*--------------------------------------------------------------------*/

extern 	int StoreCDR(_tocdr_report *pcdr);

extern int sendRadiusAccessReject(u_short portid,u_char *reason);
extern int sendRadiusAccessAccept(u_short portid,BOOL volumeFlag,BOOL durationFlag);
extern int sendRadiusDisconnectReq(u_short portid);

void SendGprsCDR(_account_info *caller_info,_state_data 	*shmp);
static BOOL isGprsRoaming(_state_data *shmp);
static void updateVolumeThisMonth(_account_info *ptr,long long amount);
static void UpdateGprsCapInfo(_account_info *ptr,float charge, long long amount, _state_data *sta_ptr);
static BOOL isFirstAccessAccept(_state_data *shmp);
static long long getRefundBytes(_state_data 	*shmp);
static long long getReportedBytes(_state_data 	*shmp);
static int inactiveGsnRefundVolume(_state_data 	*shmp);
static int inactiveGsntRefund(_state_data 	*shmp);
extern long pps_get_quota_per_request();
extern long long pps_get_account_bundle_data(_state_data 	*shmp);
extern  _prepaid_info	*getPrepaidInfo(_account_info *account);
extern long pps_get_postpaid_quota_for_first_request();
extern int	subscribe_bundle_plan_send_warning_info(_state_data *shmp);
/*--------------------------------------------------------------------*/

BOOL isPrepaidUser(_account_info *ptr)
{
	if(ptr->head >= MSISDN_HEAD_NUM ||ptr->tail > UNIT_STORE_ACCOUNT)
		return FALSE;
	return TRUE;
}

PPS_COS_RADIUS_ITEM	*getCosRadiusItem(_account_info *ptr)// user profile ==> cos ID ==> Radius Item
{
	COSTable		*cosTableItem;
	int				cosID=0;
	
	//PutLogFunID("getCosRadiusItem");// too many?

	if(isPrepaidUser(ptr) == TRUE)
  		cosID = GetLocalCosID(ptr);
	else
		cosID = 0;
	cosTableItem = &ppsParam.ppsTables.cosTable[cosID];
	
	return &cosTableItem->gprs[0];	
}

static ulong allocatePostpaidVolumeQuota(_state_data 		*shmp)
{	
	//PPS_COS_RADIUS_ITEM		*radiusItem;
	_gprs_process			*gprsProc;
	long long				volumeQuota;
		
	gprsProc = &shmp->process_info.ppsService.gprs_process;	
	//radiusItem = getCosRadiusItem(&shmp->caller_info);
	volumeQuota = MAX_VQ_BYTES_EACH_TIME_POST;

	if(gprsProc->vqAllocated.integer >= MAX_BYTES_PER_SESSION)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]Max volume(10G) per session reached",shmp->process_info.portid);
		return 0;
	}

	if(isPrepaidUser(&shmp->caller_info) == TRUE)
	{
		if(shmp->process_info.cap_flag)
		{
			UpdateGprsCapInfo(&shmp->caller_info,0,volumeQuota,shmp);
		}
		updateVolumeThisMonth(&shmp->caller_info,volumeQuota);
	}
	
	gprsProc->vqAllocated.integer += volumeQuota ;
	gprsProc->vqAllocated.exponent = 0;
	gprsProc->vthAllocated.integer = gprsProc->vqAllocated.integer-MAX_VTH_BYTES_DIFF_POST;
	gprsProc->vthAllocated.exponent = 0;
	gprsProc->vqConsumed.integer += volumeQuota;
	gprsProc->vqAllocatedThisTime = volumeQuota;
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]allocate %lld bytes,vq is %lld,vth is %lld at %s",
					shmp->process_info.portid,volumeQuota,gprsProc->vqAllocated.integer,gprsProc->vthAllocated.integer,__FUNCTION__);
	return volumeQuota;	
}

static ulong allocateDurationQuota(_state_data 		*shmp)
{
	PPS_COS_RADIUS_ITEM		*radiusItem;
	_gprs_process			*gprsProc;
	ulong				durationQuota;
	

	return 0;
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	radiusItem = getCosRadiusItem(&shmp->caller_info);
	
	durationQuota = MAX_DURATION_SEC_EACH_TIME;
	
	gprsProc->dqAllocated += durationQuota;
	gprsProc->dthAllocated = gprsProc->dqAllocated - radiusItem->durationThresholdDiff;		

	pps_print_event(shmp->process_info.logMsisdn,"[%05d]allocate %ld seconds,dq is %ld,dth is %ld at %s",
					shmp->process_info.portid ,durationQuota,gprsProc->dqAllocated,gprsProc->dthAllocated,__FUNCTION__);	
	return radiusItem->maxDurationQuotaEachTime;
}

static long long calculateVolume(RADIUS_TLV_DECIMAL	*volume)
{
	long long eResult=1;
	int 	i,exponent;

	if(volume->exponent < 0)
	{
		pps_print_error(0,"Not support float in Volume Quota");
		return -1;
	}
	exponent = abs(volume->exponent);
	for(i=0;i<exponent;i++)
	{
		eResult *= 10; 
	}

	return volume->integer*eResult;	
}

int pps_get_gprs_inactive_timer(_state_data *shmp)
{
	PPS_COS_RADIUS_ITEM		*radiusItem;
	
	radiusItem = getCosRadiusItem(&shmp->caller_info);
	if(radiusItem->inactiveSessionLimit <= 0)
		return 900;		
	return radiusItem->inactiveSessionLimit;

}


BOOL isAccessTimeout(_state_data 		*shmp)
{
	PPS_COS_RADIUS_ITEM		*radiusItem;
	_gprs_process			*gprsProc;
	//ulong					durationQuota;

	radiusItem = getCosRadiusItem(&shmp->caller_info);
	if(radiusItem->inactiveSessionLimit <= 0)// COS->GPRS Service Control->Inactive Session Limit
		return FALSE;		
	//durationQuota = MAX_DURATION_SEC_EACH_TIME;
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	if(gprsProc->accessRequestTimer/CYCLE_STATE_INTERVAL > radiusItem->inactiveSessionLimit)
		return TRUE;
	gprsProc->accessRequestTimer++;
	return FALSE;
}


int resetAccessTimeout(_state_data 		*shmp)
{
	_gprs_process			*gprsProc;

	gprsProc = &shmp->process_info.ppsService.gprs_process;
	gprsProc->accessRequestTimer = 0;

	return 1;
}

static BOOL isWaitTerTimeOut(_state_data 	*shmp,int time)
{
	_gprs_process			*gprsProc;

	gprsProc = &shmp->process_info.ppsService.gprs_process;
	if(gprsProc->accessRequestTimer/CYCLE_STATE_INTERVAL > time)
		return TRUE;
	gprsProc->accessRequestTimer++;
	return FALSE;
}

static BOOL isReachGprsNotificationLimit(_account_info 		*ptr)
{
	PPS_COS_RADIUS_ITEM		*radiusItem;
	_prepaid_data    		*shmp=&ramshm_ptr->prepaid_data;
	//COSTable				*cosTableItem;
	//double 					promo_balance,balance;
	//float					chargedBalance = 0,chargedPromotion = 0;
	

	radiusItem = getCosRadiusItem(ptr);
	if(!radiusItem->notificationWhenSessionTerminate)
		return TRUE;
	if(radiusItem->maxSmsForGprsNotificationPerDay <= 0)
		return TRUE;
    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		if(shmp->prepaid_info[ptr->head][ptr->tail].gprsNotificationTimes < radiusItem->maxSmsForGprsNotificationPerDay)
		{			
			shmp->prepaid_info[ptr->head][ptr->tail].gprsNotificationTimes++;
			return FALSE;
		}
	}
	return TRUE;
}

static BOOL isVolumeThresholdReached(_state_data 		*shmp)
{
	long long 				vthAllocated,vthReported;
	_RADIUS_REPORTED_INFO	*radiusReportedInfo;

	return TRUE;
	radiusReportedInfo = &shmp->process_info.ppsService.gprs_process.radiusReportedInfo;
	vthAllocated = calculateVolume(&shmp->process_info.ppsService.gprs_process.vthAllocated);
	if(vthAllocated <= 0)
		return FALSE;
	vthReported = calculateVolume(&radiusReportedInfo->vq.vq);
	if(vthReported <= 0)
		return FALSE;
	if(vthAllocated <= vthReported)
		return TRUE;
	return FALSE;
}


static BOOL isDurationThresholdReached(_state_data 		*shmp)
{
	DWORD 					dthAllocated,dthReported;
	_RADIUS_REPORTED_INFO	*radiusReportedInfo;

	radiusReportedInfo = &shmp->process_info.ppsService.gprs_process.radiusReportedInfo;
	dthAllocated = shmp->process_info.ppsService.gprs_process.dthAllocated;
	dthReported = radiusReportedInfo->dq.dq.value;
	if(dthAllocated <= dthReported)
		return TRUE;
	return FALSE;
}

int intiateGprsReportedInfo(_state_data 		*shmp)
{
	_gprs_process		*gprsProc;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	memset(&gprsProc->radiusReportedInfo,0,sizeof(_RADIUS_REPORTED_INFO));
	return 1;
}


static int volumeRefund(_state_data 	*shmp)
{
	_gprs_process			*gprsProc;
	//_gprs_tariff			*tariff;
	long long				refundBytes;
	int						instance;
	//PPS_COS_RADIUS_ITEM		*radiusItem;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;


	refundBytes = getRefundBytes(shmp);

	//radiusItem = getCosRadiusItem(&shmp->caller_info);
	if(refundBytes > gprsProc->vqAllocatedThisTime + MAX_VTH_BYTES_DIFF_POST)
		refundBytes = gprsProc->vqAllocatedThisTime + MAX_VTH_BYTES_DIFF_POST;
		
	gprsProc->vqConsumed.integer -= refundBytes;	
	shmp->caller_info.charge = 0;
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info,0,-refundBytes,shmp);
	}
	updateVolumeThisMonth(&shmp->caller_info,-refundBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;	
	SendAccountRealSync(instance,0.0,0.0,OMC_OCODE_EDIT);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]Consumed(%lld bytes);refund %lld bytes at %s",
					shmp->process_info.portid ,gprsProc->vqConsumed.integer,refundBytes,__FUNCTION__);
	return 1;
}

static int inactiveGsnRefundVolume(_state_data 	*shmp)
{
	_gprs_process			*gprsProc;
	//_gprs_tariff			*tariff;
	long long				refundBytes;
	int						instance;
	//PPS_COS_RADIUS_ITEM		*radiusItem;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	refundBytes = gprsProc->vqAllocatedThisTime;
	gprsProc->vqConsumed.integer -= refundBytes;	
	shmp->caller_info.charge = 0;
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info,0,-refundBytes,shmp);
	}
	updateVolumeThisMonth(&shmp->caller_info,-refundBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;	
	SendAccountRealSync(instance,0.0,0.0,OMC_OCODE_EDIT);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]Inactive timeout,Consumed(%lld bytes);refund %lld bytes at %s",
					shmp->process_info.portid ,gprsProc->vqConsumed.integer,refundBytes,__FUNCTION__);
	return 0;
}


static int pps_gprs_postpaid_process(u_short portid)
{
	_gprs_process		*gprsProc;
	_state_data 		*shmp;
	int 				result = SERVICE_KEEP_STATE;
	BOOL				volumeFlag=FALSE,durationFlag=FALSE;
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	
	switch(gprsProc->postpaidState)
	{
		case pps_gprs_idle:
			if(gprsProc->radiusCommand == PPS_RADIUS_NEW_SERVICE)
			{
				gprsProc->radiusCommand = PPS_RADIUS_NO_CMD;
				shmp->cdr_info.seizuretime = tvnow.tv_sec;
				shmp->cdr_info.answertime = tvnow.tv_sec;	
				if(allocatePostpaidVolumeQuota(shmp) > 0)
				{
					volumeFlag = TRUE;				
					sendRadiusAccessAccept(portid,volumeFlag,durationFlag);
					pps_set_postpaid_state(pps_gprs_wait_request);
				}
				else
				{
					sendRadiusAccessReject(portid,(u_char *)"Max volume(3.8G) per session reached");
					pps_set_postpaid_state(pps_gprs_service_terminate);
				}
			}
			else				
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]Unsupported update result:%d at state:%d",portid, gprsProc->radiusCommand,gprsProc->state);
				result = SERVICE_EXIT_PPS;
			}
		break;
		case pps_gprs_wait_request:
			if(isAccessTimeout(shmp) == TRUE)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]GSN access timeout at state:%d",portid,gprsProc->state);
				pps_set_postpaid_state(pps_gprs_access_timeout);
				break;
			}
			
			switch(gprsProc->radiusCommand)
			{
				case PPS_RADIUS_NO_CMD:
				break;								
				case PPS_RADIUS_THRESHOLD_REACHED:						
					gprsProc->radiusCommand = PPS_RADIUS_NO_CMD;				
					if(isVolumeThresholdReached(shmp) == TRUE)
					{
						if(allocatePostpaidVolumeQuota(shmp) > 0)
							volumeFlag = TRUE;
					}					
					sendRadiusAccessAccept(portid,volumeFlag,durationFlag);				
				break;	
				case PPS_RADIUS_QUOTA_REACHED:								
				case PPS_RADIUS_REMOTE_DISC:					
				case PPS_RADIUS_CLIENT_SERVICE_TER:					
				case PPS_RADIUS_ACCESS_SERVICE_TER:
				case PPS_RADIUS_SERVICE_NOT_EST:
					if(isPrepaidUser(&shmp->caller_info) == TRUE)
					{
						volumeRefund(shmp);
					}
					sendRadiusAccessAccept(portid,0,0);		
					pps_set_postpaid_state(pps_gprs_service_terminate);	
				break;	
				default:
					pps_print_event(shmp->process_info.logMsisdn,"[%05d]Ignore unsupported update result:%d at state:%d"
									,portid, gprsProc->radiusCommand,gprsProc->state);
				break;				
			}
		break;		
		case pps_gprs_access_timeout:
			/*if(isPrepaidUser(&shmp->caller_info) == TRUE)
			{
				inactiveGsnRefundVolume(shmp);
			}*/
			sendRadiusDisconnectReq(portid);
			pps_set_postpaid_state(pps_gprs_wait_terminate);
		break;
		case pps_gprs_wait_terminate:
			if(isWaitTerTimeOut(shmp,10) == TRUE)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]GSN access timeout at state:pps_gprs_wait_terminate",portid);
				pps_set_postpaid_state(pps_gprs_service_terminate);	
				break;
			}
			switch(gprsProc->radiusCommand)
			{
				case PPS_RADIUS_NO_CMD:
				break;																			
				case PPS_RADIUS_QUOTA_REACHED:					
				case PPS_RADIUS_REMOTE_DISC:					
				case PPS_RADIUS_CLIENT_SERVICE_TER:					
				case PPS_RADIUS_ACCESS_SERVICE_TER:
				case PPS_RADIUS_SERVICE_NOT_EST:				
					if(isPrepaidUser(&shmp->caller_info) == TRUE)
					{
						volumeRefund(shmp);						
					}
					sendRadiusAccessAccept(portid,0,0);		
					pps_set_postpaid_state(pps_gprs_service_terminate);	
				break;				
				default:/*	not going to handle disconnect ack or nak	*/					
					pps_print_event(shmp->process_info.logMsisdn,"[%05d]ignore command:%d at pps_gprs_wait_terminate"
									,portid,gprsProc->radiusCommand);
				break;
			}	
		break;
		case pps_gprs_service_terminate:			
			shmp->cdr_info.releasetime = tvnow.tv_sec;
			if(isPrepaidUser(&shmp->caller_info) == TRUE)
			{
				SendGprsCDR(&shmp->caller_info,shmp);
			}			
			result = SERVICE_EXIT_PPS;
		break;
	}
	return result;
}

BOOL isUseLegal(_account_info 		*userInfo,u_char *reason)
{
	_prepaid_info 		*prepaidInfo;
	
	if((prepaidInfo = getPrepaidInfo(userInfo)) == NULL)
		return FALSE;
	
	if(prepaidInfo->status != T_NORMAL)
	{		
		sprintf((char *)reason,"Unacceptable user status:%d",prepaidInfo->status);
		return FALSE;
	}
	return TRUE;	
}

BOOL checkMiniBalanceForGprs(_state_data 		*shmp)
{
	PPS_COS_RADIUS_ITEM		*radiusItem;
	//_gprs_process			*gprsProc;
	double					balance;
	
	//gprsProc = &shmp->process_info.ppsService.gprs_process;
	radiusItem = getCosRadiusItem(&shmp->caller_info);
	if(radiusItem->miniBalanceForGprs <= 0)
		return TRUE;
	balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+GetOverdraft(&shmp->caller_info);
	if(balance < radiusItem->miniBalanceForGprs)
		return FALSE;
	return TRUE;
}

static int findCOSIDBySgsn(DWORD sgsnIP)
{
	int 						i;
	PPS_RADIUS_SGSN_INFO		*gsnItem;
	struct in_addr				ip_addr;
	u_char 						ipAsc[64];

	ip_addr.s_addr = sgsnIP;	
	sprintf((char *)ipAsc, "%s",inet_ntoa(ip_addr));
	for(i=0;i<MaxRadiusSgsnItem;i++)
	{
		gsnItem = &ppsParam.ppsTables.sgsn[i];
		if(gsnItem->sgsnIP == sgsnIP)
		{
			pps_print_event(0,"%s related cos ID:%d",ipAsc,gsnItem->cosID);
			return gsnItem->cosID;
		}
	}					
	pps_print_error(0,"%s not matched in SGSN talbe",ipAsc);				
	return -1;
}

static int findIndexBySgsn(DWORD sgsnIP)
{
	int 						i;
	PPS_RADIUS_SGSN_INFO		*gsnItem;
	struct in_addr				ip_addr;
	u_char 						ipAsc[64];

	ip_addr.s_addr = sgsnIP;	
	sprintf((char *)ipAsc, "%s",inet_ntoa(ip_addr));	
	for(i=0;i<MaxRadiusSgsnItem;i++)
	{
		gsnItem = &ppsParam.ppsTables.sgsn[i];
		if(gsnItem->sgsnIP== sgsnIP)
		{
			pps_print_event(0,"%s related sgsn table index:%d",ipAsc,i);
			return i;
		}
	}
	pps_print_event(0,"%s not matched in SGSN table",ipAsc);		
	return -1;
}

int findTariffIDByAPNName(char *apn_name)
{
	int 						i, len;
	PPS_RADIUS_SGSN_INFO		*gsnItem;

	len = strlen(apn_name);
	
	for(i=0;i<MaxRadiusSgsnItem;i++)
	{
		gsnItem = &ppsParam.ppsTables.sgsn[i];
		if(strncasecmp((char *)gsnItem->apnName , apn_name, len) == 0)
		{
			pps_print_event(0,"%s related tariff ID:%d", apn_name ,gsnItem->tariffID);
			return gsnItem->tariffID;
		}
	}	
	
	pps_print_error(0,"%s not matched in SGSN talbe", apn_name);				
	return -1;
}


static BOOL isGprsRoaming(_state_data *shmp)
{
	if(shmp->process_info.roamingFlag == 1)
		return TRUE;
	return FALSE;
}

void getGprsCapInfo(_account_info *ptr,long long *gprsVolume,double *gprsCharged)
{
 	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetGprssCapInfo");
		
    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		*gprsVolume =  shmp->prepaid_info[ptr->head][ptr->tail].gprsVolume;
		*gprsCharged =  shmp->prepaid_info[ptr->head][ptr->tail].gprsBalance;		
	}
	else
	{
		*gprsVolume = 0;
		*gprsCharged = 0;	
	}    		
}

int GetGprsCapTariffID(_state_data *shmp)
{	/*	threshold volume unit is KB		*/
	int						tariff_id = -1;//,cos_id=0,i;
	long long				gprsVolume;
	double					gprsCharged;
	long long				threholdPoint;
	u32						c_threshold0=0,c_threshold1=0,c_threshold2=0;
	
	GPRS_VOLUME_CAP_PLAN	*capPlanPtr;
	PPS_COS_RADIUS_ITEM		*radiusItem;
	//_gprs_process			*gprsProc;

	
	PutLogFunID("GetGprsCapTariffID");
	
	radiusItem = getCosRadiusItem(&shmp->caller_info);			
	
	capPlanPtr = &radiusItem->capPlan[0];
	
	getGprsCapInfo(&shmp->caller_info,&gprsVolume,&gprsCharged);// Get CAP GPRS volume & balance from user profile
       if(gprsVolume<0 || gprsVolume>4000000000000LL) gprsVolume = 0;
	if(gprsCharged<0)  gprsCharged = 0;
	
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]Get Cap GPRS info, volume=%lld, charge=%lld",
		shmp->process_info.portid, gprsVolume, gprsCharged);

	
	c_threshold0 = capPlanPtr->threshold0;
	c_threshold1 = capPlanPtr->threshold1;
	c_threshold2 = capPlanPtr->threshold2;		
			
	if(capPlanPtr->type == 0)
	{
		threholdPoint = gprsVolume >> 10;
	}
	else
		threholdPoint = (long long )(gprsCharged);
	
	if(c_threshold0>0)
	{
		if(threholdPoint<c_threshold0)
		{
			tariff_id = capPlanPtr->tariffID0;		
			shmp->process_info.cap_seg = 0;
		}
		else if(c_threshold1>0)
		{
			if(threholdPoint<c_threshold1)
			{
				tariff_id = capPlanPtr->tariffID1;		
				shmp->process_info.cap_seg = 1;
			}
			else if(c_threshold2>0)
			{
				if(threholdPoint<c_threshold2)
				{
					tariff_id = capPlanPtr->tariffID2;
					shmp->process_info.cap_seg = 2;
				}
				else				
				{					
					tariff_id = capPlanPtr->tariffID3;
					shmp->process_info.cap_seg = 3;
				}
			}
			else
			{
				tariff_id = capPlanPtr->tariffID2;
				shmp->process_info.cap_seg = 2;
			}
		}
		else
		{
			tariff_id = capPlanPtr->tariffID1;	
			shmp->process_info.cap_seg = 1;
		}
	}				
	else
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]threshold 0 is 0,error!");
					
	return tariff_id;
}


static _gprs_tariff *getGprsVolumeTariffWithRoamAndCap(_state_data 	*shmp)
{
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff;
	int						tariffID;
	PPS_COS_RADIUS_ITEM		*radiusItem;
	COSTable				*cosTableItem;
	int						cosID;
	  			
	shmp->process_info.cap_flag = 0;
	
	radiusItem = getCosRadiusItem(&shmp->caller_info);	// user profile ==> cos ID ==> Radius Item
	gprsProc = &shmp->process_info.ppsService.gprs_process;	
	
	if(radiusItem->gprsChargedBy == 1)/* 0:define by cos; 1:defined by incoming GSN IP(by gy avp: Origin-Host ==> apn, charged by access location) */
	{				
		if(shmp->process_info.ro_if.gy_ro_enable)
			cosID = findTariffIDByAPNName(shmp->process_info.ro_if.apn);// Search Radius sgsn table by apn, get tariff ID==>cosID
		else
			cosID = findCOSIDBySgsn(gprsProc->sgsnIP);// Search Radius sgsn table by sgsn IP(fixed to 0x1234a8c0), get cosID
		if(cosID < 0)
		{
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]charged by location,can not find cosID by IP",shmp->process_info.portid);
			return NULL;
		}
		else
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]Normal,charged by location,cos id:%d",shmp->process_info.portid,cosID);
	}
	else
	{
		cosID = GetLocalCosID(&shmp->caller_info);// user profile ==> cos ID, get cos ID by user data again!!!
	}

	/*if(shmp->process_info.ro_if.gy_ro_enable)//for GY billing, APN->tariffID mapping is defined
	{
		tariffID = cosID;// confusing, cosID is actually sgsnItem->tariffID!!!!!!
	}
	else
	{*/
		cosTableItem = &ppsParam.ppsTables.cosTable[cosID];
		tariffID = cosTableItem->tariff[0].moTariffID%MAX_ZONE_NUM;
	//}

	tariff = &blf_shmp->gprs_tariff[tariffID][0];
	if(tariff->destIp == 0)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]invalid gprs tariff:%d",shmp->process_info.portid,tariff->tariffID);
		return NULL;
	}	
	if(tariff->cap_flag)
	{			
		tariffID = GetGprsCapTariffID(shmp);
		if(tariffID >= 0)
		{
			tariff = &blf_shmp->gprs_tariff[tariffID][0];
			if(tariff->destIp == 0)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]invalid gprs tariff:%d",shmp->process_info.portid,tariff->tariffID);
				return NULL;
			}
			shmp->process_info.cap_flag = tariff->cap_flag;
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]Cap plan,charged by tariff id:%d",shmp->process_info.portid,tariff->tariffID);
		}			
		else
		{
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]charged by cap plan error,no releated cap point",shmp->process_info.portid);
			return NULL;
		}
		return tariff;
	}
	else
	{		
		pps_print_event(shmp->process_info.logMsisdn,"[%05d]Normal,charged by tariff id:%d",shmp->process_info.portid,tariff->tariffID);
		return tariff;
	}
	return NULL;
}


static void CheckGprsCAPSMSNotification(_state_data *staPtr, _account_info *accountPtr, u_char cos_id, 
	  float charge, int amount ) /* cap_type: 0=volume, 1=charged */
{
	int 					cap_seg, notifyFlag=0;//cap_plan,
	float					c_threshold=0;
	_prepaid_info			*subPtr=NULL;

	COSTable				*cosTableItem;	
	GPRS_VOLUME_CAP_PLAN	*capPlanPtr;	
	long long					c_threshold_v = 0, gprsVolume;
	
	PutLogFunID("CheckGprsCAPSMSNotification");

	if(staPtr->process_info.cap_notify_flag) /* already send notification SM  in this call */
		return;
	
	//cap_plan = staPtr->process_info.cap_plan;
	cap_seg = staPtr->process_info.cap_seg;
	if(cap_seg>=3) return; /* reach the last CAP segment */

	subPtr = &ramshm_ptr->prepaid_data.prepaid_info[accountPtr->head][accountPtr->tail];
	
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];	
	capPlanPtr = &cosTableItem->gprs[0].capPlan[0];

	gprsVolume = subPtr->gprsVolume;
	if(gprsVolume<0 || gprsVolume>4000000000000LL)
		gprsVolume = 0;
	
	switch(capPlanPtr->type)
	{
		case 0:	/*	volume	*/		
			if(cap_seg == 0)
			{
				c_threshold_v = (capPlanPtr->threshold0 << 10);
			}
			else if(cap_seg == 1)
			{
				c_threshold_v = (capPlanPtr->threshold1 << 10);
			}
			else if(cap_seg == 2)
			{
				c_threshold_v = (capPlanPtr->threshold2 << 10);	
			}
			if((subPtr->gprsVolume + amount)>=c_threshold_v)
			{
				if( gprsVolume < c_threshold_v)
				{
			
					notifyFlag = 1;			
					pps_print_event(staPtr->process_info.logMsisdn,"[%05d]gprs volume reach cap plan threshold point:%d,threshold:%ld KB",
									staPtr->process_info.portid,cap_seg,c_threshold_v);
				}
			}
		break;
		case 1:			
			if(cap_seg == 0)
				c_threshold = (float)(capPlanPtr->threshold0);
			else if(cap_seg == 1)
				c_threshold = (float)(capPlanPtr->threshold1);
			else if(cap_seg == 2)
				c_threshold = (float)(capPlanPtr->threshold2);	
			if((subPtr->gprsBalance+ charge)>=c_threshold)
			{
				if(subPtr->gprsBalance < c_threshold)
				{
			
					notifyFlag = 1;			
					pps_print_event(staPtr->process_info.logMsisdn,"[%05d]gprs charge Fee reach cap plan threshold point:%d,threshold:%0.4f",
									staPtr->process_info.portid,cap_seg,c_threshold);
				}
			}
		break;
	}
	
	if(notifyFlag && (subPtr->cap_sms_notify_counter<=cap_seg))
	{
		subPtr->cap_sms_notify_counter = cap_seg+1;
		PPSSendUserSMSNotification(accountPtr->head, accountPtr->tail, (char *)getSmsSentenceNative(0,SMS_CAPGRPS_NOTIFY));
		staPtr->process_info.cap_notify_flag = 1;
	}
			
}


static void UpdateGprsBundleInfo(_account_info *ptr,float charge, long long amount, _state_data *sta_ptr)
{
 	_prepaid_data		*shmp=&ramshm_ptr->prepaid_data;
	_prepaid_info		*subPtr;
	//u_char				cos_id;
	//COSTable		*cosTableItem;	
	//int 				cap_type;
	
	PutLogFunID("UpdateGprsCapInfo");
		
	if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{		
		subPtr = &shmp->prepaid_info[ptr->head][ptr->tail];

		/*
		cos_id = subPtr->cos_id%MaxCosTableItem;
		cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
		if(cosTableItem->smsNotificationInquiry[0].smsNotificationWhenCapPlanChanged)
		{
			CheckGprsCAPSMSNotification(sta_ptr,ptr,cos_id,charge, amount);
		}
		cap_type = cosTableItem->gprs[0].capPlan[0].type;
		if(cap_type==0)//
		{
			subPtr->gprsVolume += amount;							
		}
		else
		{
			subPtr->gprsBalance += charge;
		}		
		*/
		subPtr->bundle_info.data += amount;
		if (subPtr->bundle_info.data <=0)
		{
			subPtr->bundle_info.data = 0;
		}

		if (subPtr->bundle_info.validity_date < tvnow.tv_sec)
		{
			subPtr->bundle_info.data = 0;
		}
	}    		
}

	
static void UpdateGprsCapInfo(_account_info *ptr,float charge, long long amount, _state_data *sta_ptr)
{
 	_prepaid_data		*shmp=&ramshm_ptr->prepaid_data;
	_prepaid_info		*subPtr;
	u_char				cos_id;
	COSTable			*cosTableItem;	
	int 				cap_type;
	
	PutLogFunID("UpdateGprsCapInfo");
		
    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{		
		subPtr = &shmp->prepaid_info[ptr->head][ptr->tail];

		cos_id = subPtr->cos_id%MaxCosTableItem;
		cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
		if(cosTableItem->smsNotificationInquiry[0].smsNotificationWhenCapPlanChanged)
		{// If reach next cap, send notification or not.
			CheckGprsCAPSMSNotification(sta_ptr,ptr,cos_id,charge, amount);
		}
		cap_type = cosTableItem->gprs[0].capPlan[0].type;
		if(cap_type==0)/*	volume		*/
		{
			subPtr->gprsVolume += amount;							
		}
		else
		{
			subPtr->gprsBalance += charge;
		}		
	}    		
}

static void updateVolumeThisMonth(_account_info *ptr,long long amount)
{
	_prepaid_data		*shmp=&ramshm_ptr->prepaid_data;
	_prepaid_info		*subPtr;
	
	if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{		
		subPtr = &shmp->prepaid_info[ptr->head][ptr->tail];
		subPtr->gprsVolumeThisMonth += amount;

		if (subPtr->gprsVolumeThisMonth < 0)
		{
			subPtr->gprsVolumeThisMonth = 0;
		}
	}
}

static long long allocateFirstPrepaidVolumeQuota(_state_data 	*shmp,u_char *failReason)
{	
	PPS_COS_RADIUS_ITEM		*radiusItem;
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff;
	double					balance;
	float					discount,chargedMiniRequested,chargedThisTime;
	int						instance;
	long long				miniChargedBytes,quotaBytes=0,max_quota_bytes;
	CHARGED_FEE				*chargedFee;
	
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	if(gprsProc->userType != PPS_PREPAID_USER)
		return -1;
	tariff = getGprsVolumeTariffWithRoamAndCap(shmp);
	if(tariff == NULL) 
	{ 
		strcpy((char *)failReason,"no gprs tariff assigned");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]no gprs tariff assigned",shmp->process_info.portid);
		return -1;
	}
	else
	{
		gprsProc->gprsTariff = tariff;
		gprsProc->destIP = tariff->destIp;
	}
	chargedFee = &gprsProc->chargedFee;
	memset(chargedFee,0,sizeof(CHARGED_FEE));
	chargedMiniRequested = 0;
	chargedThisTime = 0;
	radiusItem = getCosRadiusItem(&shmp->caller_info);
	max_quota_bytes = MAX_VOLUME_BYTES_EACH_TIME;
	balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+GetOverdraft(&shmp->caller_info);
	discount = (float)GetDiscount(tvnow.tv_sec,tariff->discountID)/100;	
	chargedMiniRequested = MINI_CHARGED_FEE*discount;
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]tariff:need allocate:%lld bytes,mini charge: %lld bytes(units:%d),mini charged fee:%0.4f(adj:%d),bascic fee:%0.4f,discount:%0.4f",
					shmp->process_info.portid ,(long long)MAX_VOLUME_BYTES_EACH_TIME,(long long)MINI_CHARGED_BYTES,tariff->miniChargeUnit,
					MINI_CHARGED_FEE,tariff->initialAdjustment,tariff->basicFee,discount);

	if(balance < chargedMiniRequested)	
	{
		strcpy((char *)failReason,"low fee,can't afford mini requested fee");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]low fee,can't afford mini requested fee",shmp->process_info.portid );
		return -1;
	}
	gprsProc->vqAllocated.integer = 0;
	miniChargedBytes = MINI_CHARGED_BYTES;
	if(miniChargedBytes > max_quota_bytes) 
	{
		gprsProc->vqAllocated.integer = miniChargedBytes;
		gprsProc->vqAllocated.exponent = 0;
		gprsProc->vthAllocated.integer = gprsProc->vqAllocated.integer - radiusItem->volumeThresholdDiff;
		gprsProc->vthAllocated.exponent = 0;
		chargedThisTime = chargedMiniRequested;
		quotaBytes = miniChargedBytes;
	}
	else
	{
		long long 	extraBytes;
		float		extraCharged;
		
		chargedThisTime = chargedMiniRequested;
		extraBytes = max_quota_bytes - miniChargedBytes;
		extraCharged = ((extraBytes+BYTES_PER_BASIC_FEE-1)/BYTES_PER_BASIC_FEE)*tariff->basicFee*discount; 
		if(balance >= chargedThisTime+extraCharged)
		{				
			gprsProc->vqAllocated.integer = max_quota_bytes;
			gprsProc->vqAllocated.exponent = 0;
			gprsProc->vthAllocated.integer = gprsProc->vqAllocated.integer - radiusItem->volumeThresholdDiff;
			gprsProc->vthAllocated.exponent = 0;
			chargedThisTime += extraCharged;
			quotaBytes = max_quota_bytes;
		}
		else
		{
			extraCharged = (balance - chargedMiniRequested)/discount;
			extraBytes = (extraCharged/tariff->basicFee)*BYTES_PER_BASIC_FEE;
			gprsProc->vqAllocated.integer = miniChargedBytes + extraBytes;
			gprsProc->vqAllocated.exponent = 0;
			gprsProc->vthAllocated.integer = 0;
			gprsProc->vthAllocated.exponent = 0;
			chargedThisTime = balance;
			quotaBytes = miniChargedBytes + extraBytes;
		}
	}
	gprsProc->discount = discount;
	shmp->caller_info.charge += chargedThisTime;
	gprsProc->vqConsumed.integer = quotaBytes;
	gprsProc->vqAllocatedThisTime = quotaBytes;
	UpdateBalance(shmp,&shmp->caller_info, -chargedThisTime,chargedFee);	
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info,chargedThisTime,quotaBytes,shmp);
	} 
	updateVolumeThisMonth(&shmp->caller_info,quotaBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;
	SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);
		pps_print_event(shmp->process_info.logMsisdn,"[%05d]allocate %lld bytes,charged %0.4f(balance:%0.4f,promotion:%0.4f),vq is %lld,vth is %lld at %s",
					shmp->process_info.portid ,quotaBytes,chargedThisTime,chargedFee->balance,chargedFee->promotion,
					gprsProc->vqAllocated.integer,gprsProc->vthAllocated.integer,__FUNCTION__);
	return quotaBytes;
}


static long long allocateOtherPrepaidVolumeQuota(_state_data 	*shmp,u_char *failReason)
{	
	PPS_COS_RADIUS_ITEM		*radiusItem;
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff;
	double					balance;
	float					discount,chargedThisTime,quotaCharged;
	int						instance;
	CHARGED_FEE				*chargedFee;
	long long				quotaBytes,max_quota_bytes;
		
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	if(gprsProc->vqAllocated.integer >= MAX_BYTES_PER_SESSION)
	{
		strcpy((char *)failReason,"Max volume(10G) per session reached");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]Max volume(10G) per session reached",shmp->process_info.portid);
		return -2;
	}
	if(gprsProc->userType != PPS_PREPAID_USER)
		return -1;
	tariff = getGprsVolumeTariffWithRoamAndCap(shmp);
	if(tariff == NULL)
	{
		strcpy((char *)failReason,"no gprs tariff assigned");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]no gprs tariff assigned",shmp->process_info.portid);
		return -1;
	}
	else
	{
		gprsProc->gprsTariff = tariff;
		gprsProc->destIP = tariff->destIp;
	}
	
	radiusItem = getCosRadiusItem(&shmp->caller_info);
	max_quota_bytes = MAX_VOLUME_BYTES_EACH_TIME;
	balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+GetOverdraft(&shmp->caller_info);
	if(balance <= 0)
	{
		strcpy((char *)failReason,"low fee");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]low fee",shmp->process_info.portid );
		return -1;
	}
	if(isPoolChildReachFeeLimit(&shmp->caller_info) == 2)
	{
		strcpy((char *)failReason,"Child account,reach limited fee");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s child account,reach limited fee",shmp->process_info.portid ,shmp->caller_info.number);
		return -1;
	}
	chargedFee = &gprsProc->chargedFee;
	memset(chargedFee,0,sizeof(CHARGED_FEE));
	chargedThisTime = 0;
	quotaCharged = 0;
	discount = (float)GetDiscount(tvnow.tv_sec,tariff->discountID)/100;
		
	quotaCharged = ((max_quota_bytes+BYTES_PER_BASIC_FEE-1)/BYTES_PER_BASIC_FEE)*tariff->basicFee*discount;

	pps_print_event(shmp->process_info.logMsisdn,"[%05d]tariff:need allocate:%lld bytes,bascic fee:%0.4f,discount:%0.4f",
					shmp->process_info.portid ,max_quota_bytes,tariff->basicFee,discount);
	
	if(balance >= quotaCharged)
	{
		gprsProc->vqAllocated.integer += max_quota_bytes;
		gprsProc->vqAllocated.exponent = 0;
		gprsProc->vthAllocated.integer = gprsProc->vqAllocated.integer - radiusItem->volumeThresholdDiff;
		gprsProc->vthAllocated.exponent = 0;
		chargedThisTime += quotaCharged;
		quotaBytes = max_quota_bytes;
	}
	else
	{
		long long 	extraBytes;
		float		extraCharged;

		extraCharged = balance/discount;
		extraBytes = (extraCharged/tariff->basicFee)*BYTES_PER_BASIC_FEE;
		gprsProc->vqAllocated.integer += extraBytes;
		gprsProc->vqAllocated.exponent = 0;		
		gprsProc->vthAllocated.integer = 0;
		gprsProc->vthAllocated.exponent = 0;
		chargedThisTime = balance;
		quotaBytes = extraBytes;
	}
	gprsProc->discount = discount;	
	shmp->caller_info.charge += chargedThisTime;
	gprsProc->vqConsumed.integer += quotaBytes;
	gprsProc->vqAllocatedThisTime = quotaBytes;
	UpdateBalance(shmp,&shmp->caller_info, -chargedThisTime,chargedFee);	
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info,chargedThisTime,quotaBytes,shmp);
	} 
	updateVolumeThisMonth(&shmp->caller_info,quotaBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;
	SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]allocate %lld bytes,charged %0.4f(balance:%0.4f,promotion:%0.4f),vq is %lld,vth is %lld at %s",
					shmp->process_info.portid ,quotaBytes,chargedThisTime,chargedFee->balance,chargedFee->promotion,
					gprsProc->vqAllocated.integer,gprsProc->vthAllocated.integer,__FUNCTION__);
	return quotaBytes;
}

static long long getRefundBytes(_state_data 	*shmp)
{
	_gprs_process		*gprsProc;
	long long 			allocatedBytes,consumedBytes;

	gprsProc = &shmp->process_info.ppsService.gprs_process;
	allocatedBytes = calculateVolume(&gprsProc->vqAllocated);
	if(allocatedBytes < 0)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]get allocated volume quota when Refund",shmp->process_info.portid );
		return 0;
	}
	consumedBytes = calculateVolume(&gprsProc->radiusReportedInfo.vq.vq);
	if(consumedBytes < 0)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]get reported volume quota when Refund",shmp->process_info.portid );
		return 0;
	}
	if(allocatedBytes > consumedBytes)
		return allocatedBytes - consumedBytes;
	return 0;
}

static long long getReportedBytes(_state_data 	*shmp)
{
	_gprs_process		*gprsProc;
	long long			consumedBytes;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	consumedBytes = calculateVolume(&gprsProc->radiusReportedInfo.vq.vq);
	if(consumedBytes < 0)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]get reported volume quota when Refund",shmp->process_info.portid );
		return 0;
	}

	return consumedBytes;
}


static 	void refund(_account_info *ptr, float refundFee,CHARGED_FEE *chargeFee)
{
	_prepaid_data		*shmp=&ramshm_ptr->prepaid_data;
	//double				promo_balance,balance;
	float				refundPromotioin = 0,refundBalance = 0,absPromotion;
		
	PutLogFunID("refund");
	
	if(refundFee<=0)
	{
		chargeFee->balance = 0;
		chargeFee->promotion= 0;
		return;
	}
	absPromotion = abs(chargeFee->promotion);
	if(absPromotion > 0)
	{
		if(refundFee > absPromotion)
		{
			refundPromotioin = absPromotion;
			refundBalance = refundFee - absPromotion;
		}
		else
			refundPromotioin = refundFee;
	}
	else 
	{
		refundPromotioin = 0;
		refundBalance = refundFee;
	}
	chargeFee->balance = refundBalance;
	chargeFee->promotion= refundPromotioin;
	shmp->prepaid_info[ptr->head][ptr->tail].promo_balance += refundPromotioin;
	if(refundBalance <= ppsParam.serviceControl.maxBalance)
		shmp->prepaid_info[ptr->head][ptr->tail].balance += refundBalance;
	if(shmp->prepaid_info[ptr->head][ptr->tail].balance < 0)
		shmp->prepaid_info[ptr->head][ptr->tail].balance  = 0;
	
	increPoolChildCharged(ptr,-(refundBalance+refundPromotioin));
	debugmib.display.totalCharge -= refundBalance+refundPromotioin;
}

static int firstAccessAcceptRefund(_state_data 		*shmp)
{
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff;
	//PPS_COS_RADIUS_ITEM		*radiusItem;
	long long				refundBytes;
	float					refundFee;//,basicFee;
	float					discount;
	int						instance;
	CHARGED_FEE				*chargedFee;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
		
	//radiusItem = getCosRadiusItem(&shmp->caller_info);
	tariff = gprsProc->gprsTariff;	
	discount = gprsProc->discount;	

	if(tariff->basicFee == 0 || (discount == 0 && shmp->process_info.cap_flag==0))
		return 0;
	
	if(getReportedBytes(shmp) <= MINI_CHARGED_BYTES)
	{
		refundBytes = gprsProc->vqConsumed.integer - MINI_CHARGED_BYTES;
		refundFee = shmp->caller_info.charge - MINI_CHARGED_FEE*discount;
		gprsProc->vqConsumed.integer = MINI_CHARGED_BYTES;
		shmp->caller_info.charge = MINI_CHARGED_FEE*discount;		
	}
	else		
	{
		refundBytes = getRefundBytes(shmp);
		gprsProc->vqConsumed.integer -= refundBytes;
		refundFee = (refundBytes/BYTES_PER_BASIC_FEE)*tariff->basicFee*discount;	
		shmp->caller_info.charge -= refundFee;
	}

	chargedFee = &gprsProc->chargedFee;	
	
	refund(&shmp->caller_info, refundFee,chargedFee);
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info,-refundFee,-refundBytes,shmp);
	}
	updateVolumeThisMonth(&shmp->caller_info,-refundBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;

	SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]Consumed(%lld bytes,%0.4f fee);refund %lld bytes,fee(balance:%0.4f,promotion:%0.4f) at %s",
					shmp->process_info.portid ,gprsProc->vqConsumed.integer,shmp->caller_info.charge,
					refundBytes,chargedFee->balance,chargedFee->promotion,__FUNCTION__);
	return 1;
}

static int otherAccessAcceptRefund(_state_data 	*shmp)
{
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff;
	long long				refundBytes;
	float					refundFee;
	float					discount;
	int						instance;
	CHARGED_FEE				*chargedFee;
	PPS_COS_RADIUS_ITEM		*radiusItem;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;


	refundBytes = getRefundBytes(shmp);

	radiusItem = getCosRadiusItem(&shmp->caller_info);
	if(refundBytes > gprsProc->vqAllocatedThisTime + radiusItem->volumeThresholdDiff)
		refundBytes = gprsProc->vqAllocatedThisTime + radiusItem->volumeThresholdDiff;
		
	gprsProc->vqConsumed.integer -= refundBytes;
	tariff = gprsProc->gprsTariff;	
	discount = gprsProc->discount;

	if(tariff->basicFee == 0 || (discount == 0 && shmp->process_info.cap_flag==0))
		return 0;
	
	refundFee = (refundBytes/BYTES_PER_BASIC_FEE)*tariff->basicFee*discount;	

	chargedFee = &gprsProc->chargedFee;
	shmp->caller_info.charge -= refundFee;
	refund(&shmp->caller_info, refundFee,chargedFee);
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info,-refundFee,-refundBytes,shmp);
	}
	updateVolumeThisMonth(&shmp->caller_info,-refundBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;	
	SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]Consumed(%lld bytes,%0.4f fee);refund %lld bytes,fee(balance:%0.4f promotion:%0.4f) at %s",
					shmp->process_info.portid ,gprsProc->vqConsumed.integer,shmp->caller_info.charge,
					refundBytes,chargedFee->balance,chargedFee->promotion,__FUNCTION__);
	return 1;
}

static int inactiveGsntRefund(_state_data 	*shmp)
{
	_gprs_process			*gprsProc;
	long long				refundBytes;
	float					refundFee;
	int						instance;
	CHARGED_FEE				*chargedFee;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	chargedFee = &gprsProc->chargedFee;
	
	refundBytes = gprsProc->vqAllocatedThisTime;
	refundFee = abs(chargedFee->balance+chargedFee->promotion);
	
	gprsProc->vqConsumed.integer -= refundBytes;	
	refund(&shmp->caller_info, refundFee,chargedFee);
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info,-refundFee,-refundBytes,shmp);
	}
	updateVolumeThisMonth(&shmp->caller_info,-refundBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;	
	SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]inactive timeout,Consumed(%lld bytes,%0.4f fee);refund %lld bytes,fee(balance:%0.4f promotion:%0.4f) at %s",
					shmp->process_info.portid ,gprsProc->vqConsumed.integer,shmp->caller_info.charge,
					refundBytes,chargedFee->balance,chargedFee->promotion,__FUNCTION__);
	return 1;
}

static BOOL isFirstAccessAccept(_state_data *shmp)
{
	_gprs_process			*gprsProc;

	gprsProc = &shmp->process_info.ppsService.gprs_process;
	if(gprsProc->qid - 1 == INIT_QID)
		return TRUE;
	return FALSE;
}

static int pps_gprs_prepaidVolume_process(u_short portid)
{
	_gprs_process		*gprsProc;
	_state_data 		*shmp;
	int 				result = SERVICE_KEEP_STATE,rst;
	BOOL				volumeFlag=FALSE,durationFlag=FALSE;
	u_char				failReason[128]={0};
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	switch(gprsProc->prepaidState)
	{
		case pps_gprs_idle:
			if(gprsProc->radiusCommand == PPS_RADIUS_NEW_SERVICE)
			{
				gprsProc->radiusCommand = PPS_RADIUS_NO_CMD;
				shmp->cdr_info.seizuretime = tvnow.tv_sec;
				shmp->cdr_info.answertime = tvnow.tv_sec;		
				
				if(checkMiniBalanceForGprs(shmp) == FALSE)
				{
					sendRadiusAccessReject(portid, (u_char *)"Low Balance for GPRS service");						
					pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s Low Balance for GPRS service",portid,shmp->caller_info.number);
					pps_set_prepaid_state(pps_gprs_service_terminate);
					break;
				}	
				rst = allocateFirstPrepaidVolumeQuota(shmp,failReason);				
				if(rst > 0)
				{
					volumeFlag = TRUE;;			
					sendRadiusAccessAccept(portid,volumeFlag,durationFlag);
					pps_set_prepaid_state(pps_gprs_wait_request);	
				}
				else
				{
					pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s %s",portid,shmp->caller_info.number,failReason);
					sendRadiusAccessReject(portid,failReason);
					pps_set_prepaid_state(pps_gprs_service_terminate);	
				}
			}
		break;
		case pps_gprs_wait_request:		
			if(isAccessTimeout(shmp) == TRUE)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]GSN access timeout at state:pps_gprs_wait_request",portid);
				pps_set_prepaid_state(pps_gprs_access_timeout);	
				break;
			}
			switch(gprsProc->radiusCommand)
			{
				case PPS_RADIUS_NO_CMD:
				break;								
				case PPS_RADIUS_THRESHOLD_REACHED:					
					gprsProc->radiusCommand = PPS_RADIUS_NO_CMD;			
					if(isVolumeThresholdReached(shmp) == TRUE)
					{
						if(allocateOtherPrepaidVolumeQuota(shmp,failReason) > 0)
						{
							volumeFlag = TRUE;
							sendRadiusAccessAccept(portid,volumeFlag,durationFlag);		
						}
						else
						{
							sendRadiusAccessReject(portid,failReason);
							//pps_set_prepaid_state(pps_gprs_wait_quota_reached);
							pps_set_prepaid_state(pps_gprs_access_timeout);
							break;
						}
					}
				break;									
				case PPS_RADIUS_QUOTA_REACHED:					
				case PPS_RADIUS_REMOTE_DISC:					
				case PPS_RADIUS_CLIENT_SERVICE_TER:					
				case PPS_RADIUS_ACCESS_SERVICE_TER:
				case PPS_RADIUS_SERVICE_NOT_EST:
					if(isFirstAccessAccept(shmp)==TRUE)
						firstAccessAcceptRefund(shmp);
					else
						otherAccessAcceptRefund(shmp);
					sendRadiusAccessAccept(portid,0,0);		
					pps_set_prepaid_state(pps_gprs_service_terminate);	
				break;								
				default:
					pps_print_event(shmp->process_info.logMsisdn,"[%05d]Ignore unsupported update result:%d at state:%d"
									, portid,gprsProc->radiusCommand,gprsProc->state);
				break;				
			}
		break;		
		case pps_gprs_wait_quota_reached:
			if(isAccessTimeout(shmp) == TRUE)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]GSN access timeout at state:pps_gprs_wait_quota_reached",portid);
				pps_set_prepaid_state(pps_gprs_access_timeout);	
				break;
			}
			switch(gprsProc->radiusCommand)
			{
				case PPS_RADIUS_NO_CMD:
				break;																			
				case PPS_RADIUS_QUOTA_REACHED:					
				case PPS_RADIUS_REMOTE_DISC:					
				case PPS_RADIUS_CLIENT_SERVICE_TER:					
				case PPS_RADIUS_ACCESS_SERVICE_TER:
				case PPS_RADIUS_SERVICE_NOT_EST:
					if(isFirstAccessAccept(shmp)==TRUE)
						firstAccessAcceptRefund(shmp);
					else
						otherAccessAcceptRefund(shmp);
					sendRadiusAccessAccept(portid,0,0);		
					pps_set_prepaid_state(pps_gprs_service_terminate);	
				break;								
				default:
					pps_print_event(shmp->process_info.logMsisdn,"[%05d]Ignore unsupported update result:%d at state:%d"
									, portid,gprsProc->radiusCommand,gprsProc->state);
				break;				
			}
		break;
		case pps_gprs_access_timeout:
			//inactiveGsntRefund(shmp);
			sendRadiusDisconnectReq(portid);
			pps_set_prepaid_state(pps_gprs_wait_terminate);
		break;
		case pps_gprs_wait_terminate:
			if(isWaitTerTimeOut(shmp,10) == TRUE)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]GSN access timeout at state:pps_gprs_wait_terminate",portid);
				pps_set_prepaid_state(pps_gprs_service_terminate);	
				break;
			}
			switch(gprsProc->radiusCommand)
			{
				case PPS_RADIUS_NO_CMD:
				break;																			
				case PPS_RADIUS_QUOTA_REACHED:					
				case PPS_RADIUS_REMOTE_DISC:					
				case PPS_RADIUS_CLIENT_SERVICE_TER:					
				case PPS_RADIUS_ACCESS_SERVICE_TER:
				case PPS_RADIUS_SERVICE_NOT_EST:				
					if(isFirstAccessAccept(shmp)==TRUE)
						firstAccessAcceptRefund(shmp);
					else
						otherAccessAcceptRefund(shmp);
					sendRadiusAccessAccept(portid,0,0);		
					pps_set_prepaid_state(pps_gprs_service_terminate);	
				break;				
				default:/*	not going to handle disconnect ack or nak	*/					
					pps_print_event(shmp->process_info.logMsisdn,"[%05d]Ignore command:%d at pps_gprs_wait_terminate"
									,portid,gprsProc->radiusCommand);
				break;
			}			
		break;
		case pps_gprs_service_terminate:
			shmp->cdr_info.releasetime = tvnow.tv_sec;
			SendGprsCDR(&shmp->caller_info,shmp);
			result = SERVICE_EXIT_PPS;
		break;
	}
	return result;
}

int pps_gprs_process(u_short portid)
{
	_gprs_process		*gprsProc;
	_state_data 		*shmp;
	int 				result = SERVICE_KEEP_STATE;
	u_char				failReason[128]={0};
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	switch(gprsProc->state)
	{
		case pps_gprs_idle:
			switch(gprsProc->radiusCommand)
			{
				case PPS_RADIUS_NO_CMD:
				break;				
				case PPS_RADIUS_NEW_SERVICE:
					if(GetAccountPos(&shmp->caller_info) == TRUE)
					{
						PPS_COS_RADIUS_ITEM		*radiusItem;
						if(isUseLegal(&shmp->caller_info,failReason) == FALSE)
						{							
							pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s",shmp->process_info.portid ,failReason);
							sendRadiusAccessReject(portid,failReason);
							memset(gprsProc,0,sizeof(_gprs_process));
							pps_set_gprs_state(pps_gprs_idle);
							result = SERVICE_EXIT_PPS;
							break;
						}
						radiusItem = getCosRadiusItem(&shmp->caller_info);
						if(radiusItem->gprsChargeFlag == 0)
						{
							gprsProc->userType = PPS_POSTPAID_USER;
							pps_set_gprs_state(pps_gprs_postpaid);
							pps_print_event(shmp->process_info.logMsisdn,"[%05d]Prepaid user:%s ask for gprs service,but no need to charge it",portid,shmp->caller_info.number);
							break;
						}
						gprsProc->userType = PPS_PREPAID_USER;
						shmp->context_info.callType = CT_GPRS;
						pps_set_gprs_state(pps_gprs_prepaid);
						pps_print_event(shmp->process_info.logMsisdn,"[%05d]Prepaid user:%s ask for gprs service",portid,shmp->caller_info.number);
					}	
					else
					{						
						gprsProc->userType = PPS_POSTPAID_USER;
						pps_set_gprs_state(pps_gprs_postpaid);
						pps_print_event(shmp->process_info.logMsisdn,"[%05d]Postpaid user:%s ask for gprs service",portid,shmp->caller_info.number);
					}
				break;				
				default:
					pps_print_error(shmp->process_info.logMsisdn,"[%05d]Unsupported update result:%d at state:%d",portid, gprsProc->radiusCommand,gprsProc->state);
					result = SERVICE_EXIT_PPS;
				break;				
			}
		break;		
		case pps_gprs_postpaid:
			return pps_gprs_postpaid_process(portid);
		break;
		case pps_gprs_prepaid:
			return pps_gprs_prepaidVolume_process(portid);
		break;
	}
	return result;
}

static long long GetAccountVolume(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetAccountMsisdn");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		return  shmp->prepaid_info[head][tail].gprsVolumeThisMonth;		
	}
    return 0;
}

static int sendGprsSessionInfo(_state_data *shmp,long long volume,ulong duration,float charge)
{
	u_char			msg[MAX_SMS_LEN*4] = {0};
	long long 		volumeThisMonth;
		
	if(isReachGprsNotificationLimit(&shmp->caller_info))
		return 1;
	strcpy((char *)msg, (char *)getSmsSentenceNative(0,SMS_GPRS_VOLUME_CONSUMED));
	sprintf((char *)msg+strlen((char *)msg),"%lldKB,",(volume+BYTES_PER_KB-1)>>10);
	strcat((char *)msg, (char *)getSmsSentenceNative(0,SMS_GPRS_DURATION));
	sprintf((char *)msg+strlen((char *)msg),"%ldS,",duration);
	strcat((char *)msg, (char *)getSmsSentenceNative(0,SMS_GPRS_CHARGED));

	fillMoneyToContent(charge, (char *)msg+strlen((char *)msg));

	volumeThisMonth = GetAccountVolume(shmp->caller_info.head,shmp->caller_info.tail);
	msg[strlen((char *)msg)] = '.';
	strcat((char *)msg+strlen((char *)msg), (char *)getSmsSentenceNative(0,SMS_GPRS_VOLUME_THIS_MONTH));	
	sprintf((char *)msg+strlen((char *)msg),"%lldKB.",(volumeThisMonth+BYTES_PER_KB-1)>>10);
	
	return smppSendMOSM((char *)shmp->caller_info.number, (char *)ppsParam.ppsTables.serviceNumber[GPRSPrepaidPostion].number
						, msg, strlen((char *)msg),0);
}

int reportGprsCDRIncrement(int duration, float charge, long gprsVolume)
{
	int index=0;
	
	PutLogFunID("reportGprsCDRIncrement");
	
	index = pps_report.ptrTail;
	pps_report.daily_report[index].call_report.gprs_record.gprsVolume += gprsVolume;
	pps_report.daily_report[index].call_report.gprs_record.gprsDuration += duration;
	pps_report.daily_report[index].call_report.gprs_record.gprsCharge += charge;
	
	pps_report.daily_report[index].balance_summary.today_charge += charge;
	return 1;
}


void SendGprsCDR(_account_info *caller_info,_state_data 	*shmp)
{
    _tocdr_report  	pcdr;
    char           	buf[BUFSIZE]={""}; 
	int			   	balance;
	int			 	head, tail;
	long			charge;
	ulong			duration,volume;
	_gprs_process	*gprsProc;
	struct in_addr	nas_addr;
	long long consumed_data_value=0;			
	
	PutLogFunID("SendGprsCDR");

	if(shmp == NULL)
		return;

	if(caller_info->charge < 0.0)
		return;
	if(caller_info->charge > 1000000.00)
		return;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;

	if(0)
	{
		consumed_data_value = gprsProc->vqConsumed.integer;
	}
	if(1)
	{
		consumed_data_value = gprsProc->dqConsumed;
	}
	
	if( consumed_data_value == 0)
		return;
	
	memset(&pcdr,0,sizeof(_tocdr_report));
	
	head = caller_info->head;
	tail = caller_info->tail;
	
	pcdr.mtype = MO_CDR_TYPE;

	if (consumed_data_value < 0)
	{
		pcdr.type[0] = CT_GPRS_REFUND;// add a new cdr type for GPRS refund
		pcdr.type[1] = CT_GPRS_REFUND>>8;
		consumed_data_value = -consumed_data_value;
	}
	else
	{
		pcdr.type[0] = CT_GPRS;
		pcdr.type[1] = CT_GPRS>>8;
	}
	
    duration = shmp->cdr_info.releasetime - shmp->cdr_info.answertime;
	
    AsciiTime(buf, shmp->cdr_info.seizuretime);
	AsciiToBcd(pcdr.seizuretime,buf,14);
    AsciiTime(buf, shmp->cdr_info.answertime);
	AsciiToBcd(pcdr.answertime,buf,14);
    AsciiTime(buf, shmp->cdr_info.releasetime);
	AsciiToBcd(pcdr.releasetime,buf,14);
	
	memcpy(pcdr.duration,&duration,4);

	charge = caller_info->charge;
	charge = (int)(charge + 0.5);// ceil ==> int
       memcpy(pcdr.charge,&charge,4);
       balance = (int) (GetAccountBalance(head, tail) + 0.5);
       memcpy(pcdr.balance,&balance,4);

    GetAccountMsisdn(head,tail,&pcdr.msisdn[1]);
	ByteRev(pcdr.msisdn,9,0);
	pcdr.msisdn[0]=0x91;
	memcpy(pcdr.caller,pcdr.msisdn,9);
   	buf[0] = 0;

	//gprsProc = &shmp->process_info.ppsService.gprs_process;

	nas_addr.s_addr = gprsProc->sgsnIP;	
	sprintf((char *)pcdr.gprsSgsnIP, "%s",inet_ntoa(nas_addr));
	
	nas_addr.s_addr = gprsProc->gsnIP;	
	sprintf((char *)pcdr.gprsGgsnIP, "%s",inet_ntoa(nas_addr));

	nas_addr.s_addr = gprsProc->destIP;	
	sprintf((char *)pcdr.gprsDestIP, "%s",inet_ntoa(nas_addr));

	volume = (consumed_data_value+BYTES_PER_KB-1) >> 10;
	pcdr.gprsVolume[0] = volume >> 24;
	pcdr.gprsVolume[1] = volume >> 16;
	pcdr.gprsVolume[2] = volume >> 8;
	pcdr.gprsVolume[3] = volume;
	
	pcdr.tcos[0] = NORMAL_CLEARING%256;

      memcpy(pcdr.plan_id, &shmp->process_info.rest_if.plan_id, 4);

	GprsCDRStatistics(&pcdr,charge,volume);
	StoreCDR(&pcdr);
	pps_real_update_user_file(head, tail);
	sendGprsSessionInfo(shmp,consumed_data_value,duration,charge);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]%s send gprs cdr",shmp->process_info.portid ,caller_info->number);
}

int allocate_ps_data_volume_quota(_state_data *shmp, char *failReason)
{	
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff, gprsTariff;
	float					balance,chargedThisTime,quotaCharged;
	float					basicFee = 0.0;
	float					discount;
	int					instance;
	CHARGED_FEE		*chargedFee;
	u_long 				quotaBytes,max_quota_bytes;
	long long	 			left_bundle_volume = 0;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	gprsProc->vqAllocatedThisTime = 0;

	if(shmp->caller_info.status == T_BLACKLIST ||
		shmp->caller_info.status == T_RELEASED || shmp->caller_info.status == T_EXTERNAL)// user status by user profile
	{
		strcpy(failReason,"Invalid account status0");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d] Invalid account status",shmp->process_info.portid);
		return -1;
	}

	if(shmp->caller_info.status != T_NORMAL ||
		shmp->caller_info.expired_flag == 1)// user status (read from user data)
	{
		strcpy(failReason,"Invalid account status");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d] Invalid account status",shmp->process_info.portid);
		return -2;
	}
	
	if(gprsProc->vqAllocated.integer >= MAX_BYTES_PER_SESSION)// Max 10G during the session
	{
		strcpy(failReason,"Max volume(10G) per session reached");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d] Max volume(10G) per session reached",shmp->process_info.portid);
		return -3;
	}

	if(OCS_with_external_CRM())
	{
#ifdef OCS_USE_REDIS_DB// get tariff and bundle
		pps_send_query_tariff_req(shmp->process_info.portid);
#endif
		if(shmp->process_info.rest_if.query_tariff_status == 3)
		{
			strcpy(failReason,"no gprs tariff assigned");
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]no gprs tariff assigned",shmp->process_info.portid);

#ifndef OCS_USE_REDIS_DB
			pps_send_query_tariff_req(shmp->process_info.portid);
#endif
			return -4;
		}
		memset(&gprsTariff, 0x00, sizeof(_gprs_tariff));
		tariff = gprsProc->gprsTariff = &gprsTariff;
		gprsProc->gprsTariff->chargeUnit = shmp->process_info.rest_if.unit/1024;
		gprsProc->gprsTariff->basicFee = shmp->process_info.rest_if.unit_charge;
		discount = shmp->process_info.rest_if.tariff_discount;
		gprsProc->discount = 	discount;	

	}
	else
	{
		tariff = getGprsVolumeTariffWithRoamAndCap(shmp);// read Gprs tariff
		if(tariff == NULL)
		{
			strcpy(failReason,"no gprs tariff assigned");
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]no gprs tariff assigned",shmp->process_info.portid);
			return -4;
		}
		else
		{
			gprsProc->gprsTariff = tariff;
			gprsProc->destIP = tariff->destIp;
		}
	}
	
	max_quota_bytes = pps_get_quota_per_request(); //MAX_VQ_BYTES_EACH_TIME_POST;// Quota for every CCR request

	if(OCS_with_external_CRM())
	{
		if(shmp->process_info.rest_if.total_plan_value > 0)
			left_bundle_volume = shmp->process_info.rest_if.total_plan_value - shmp->process_info.rest_if.used_plan_value;
		if(left_bundle_volume<=0)
		{
			left_bundle_volume = 0;
			shmp->process_info.bundle_flag = 0;
		}
		else
		{
			shmp->process_info.bundle_flag = 1;
			if(left_bundle_volume>max_quota_bytes)
				left_bundle_volume = max_quota_bytes ;
			shmp->process_info.rest_if.used_plan_value += left_bundle_volume;
			shmp->process_info.rest_if.plan_value_add_this_time = left_bundle_volume;
			pps_send_update_plan_value_req(shmp->process_info.portid);			
		}
#ifndef OCS_USE_REDIS_DB
		pps_send_query_tariff_req(shmp->process_info.portid);//get next tariff
#endif
	}
	else
		left_bundle_volume = pps_get_account_bundle_data(shmp);// remain volume in bundle of user

	if(left_bundle_volume > 0)
	{
		if(left_bundle_volume < max_quota_bytes)// left bundle less than quota for each quest, allocate all left quota
		{
			max_quota_bytes = left_bundle_volume;
		}

		basicFee = 0;// use bundle, do not charge other way
		shmp->process_info.bundle_flag = 1;// bundle identifier

		subscribe_bundle_plan_send_warning_info(shmp);// low bundle reach threshold(<2048K), send SMS notification
	}
	else
	{
		if(shmp->process_info.bundle_flag )// bundle exaust, send SMS notification
		{
			long not_consumed_volume = shmp->process_info.ppsService.gprs_process.vqAllocated.integer - 
										shmp->process_info.ppsService.gprs_process.dqConsumed;
			if( not_consumed_volume > 0)
			{
				gprsProc->vqAllocatedThisTime = not_consumed_volume;
				return 1;
			}
			
			gprsProc->vqAllocatedThisTime = 0;
			shmp->process_info.bundle_flag = 0;				

			subscribe_bundle_plan_send_used_up_info((char *)shmp->caller_info.number, SMS_BUNDLE_PLAN_DATA_USEDUP);

			pps_print_error(shmp->process_info.logMsisdn,"[%05d]Bundle data is used up!",shmp->process_info.portid );
			
			return 0;
		}
		
		if(1)//no need to allocate non-charged small quote
		{
			if(shmp->process_info.ro_if.request_type == RT_INITIAL_REQUEST)
			{
				max_quota_bytes = pps_get_postpaid_quota_for_first_request();
				shmp->process_info.ro_if.post_charge = 1;
			}
			else
				shmp->process_info.ro_if.post_charge = 0;
		}
	
		basicFee = tariff->basicFee;
		
		shmp->process_info.bundle_flag = 0;		
	}
	
	balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+GetOverdraft(&shmp->caller_info);
	if(balance <= 0.001 && shmp->process_info.bundle_flag == 0)
	{
		strcpy(failReason,"low fee");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]low fee with bundle flag=%d",
			shmp->process_info.portid, shmp->process_info.bundle_flag );
		return -5;
	}
	

	chargedFee = &gprsProc->chargedFee;
	memset(chargedFee,0,sizeof(CHARGED_FEE));
	chargedThisTime = 0;
	quotaCharged = 0;
	if(OCS_with_external_CRM())
		discount = shmp->process_info.rest_if.tariff_discount/100;
	else
		discount = (float)GetDiscount(tvnow.tv_sec,tariff->discountID)/100;
	
	//quotaCharged = ((max_quota_bytes+BYTES_PER_BASIC_FEE-1)/BYTES_PER_BASIC_FEE)*basicFee*discount;
	quotaCharged = (max_quota_bytes*basicFee*discount)/BYTES_PER_BASIC_FEE;

	pps_print_event(shmp->process_info.logMsisdn,"[%05d]tariff:need allocate:%lld bytes,bascic fee:%0.4f,discount:%0.4f,bundle data=%lld,quotaCharged:%.4f",
					shmp->process_info.portid ,max_quota_bytes, basicFee,discount, left_bundle_volume,quotaCharged);
	
	if(balance >= quotaCharged)
	{
		gprsProc->vqAllocated.integer += max_quota_bytes;
		chargedThisTime += quotaCharged;
		quotaBytes = max_quota_bytes;
	}
	else
	{

	      if(0)
	      	{
			long long 	extraBytes;
			float		extraCharged;

			extraCharged = balance/discount;
			extraBytes = (extraCharged/tariff->basicFee)*BYTES_PER_BASIC_FEE;
			gprsProc->vqAllocated.integer += extraBytes;
			gprsProc->vqAllocated.exponent = 0;		
			gprsProc->vthAllocated.integer = 0;
			gprsProc->vthAllocated.exponent = 0;
			chargedThisTime = balance;
			quotaBytes = extraBytes;

			shmp->process_info.ro_if.post_charge = 0;
	      	}
		else
		{
			strcpy(failReason,"low fee");
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]low fee for a minu charge",
				shmp->process_info.portid);
			return -5;	
		}
	}
	
	gprsProc->discount = discount;	
	shmp->caller_info.charge += chargedThisTime;
	gprsProc->vqConsumed.integer += quotaBytes;
	gprsProc->vqAllocatedThisTime = quotaBytes;

	if( shmp->process_info.ro_if.post_charge == 0 )
	{
		UpdateBalance(shmp,&shmp->caller_info, -chargedThisTime,chargedFee);	

		if(shmp->process_info.cap_flag)
		{
			UpdateGprsCapInfo(&shmp->caller_info,chargedThisTime,quotaBytes,shmp);
		} 

		if(shmp->process_info.bundle_flag)
		{// pre-charge bundle first, refund when session stop
			UpdateGprsBundleInfo(&shmp->caller_info,chargedThisTime, -quotaBytes, shmp);
		} 

		updateVolumeThisMonth(&shmp->caller_info,quotaBytes);

		instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;
		SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);// dual platform sync
	}

	
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]allocate %lld bytes,charged %0.4f(balance:%0.4f,promotion:%0.4f),vq is %lld,vth is %lld at %s",
					shmp->process_info.portid ,quotaBytes,chargedThisTime,chargedFee->balance,chargedFee->promotion,
					gprsProc->vqAllocated.integer,gprsProc->vthAllocated.integer,__FUNCTION__);

	return 1;
}

int pps_refund_data_volume(_state_data *shmp)
{
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff, gprsTariff;
	long long				refundBytes;
	CHARGED_FEE		*chargedFee;	
	float					discount, refundFee;
	int					instance;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	
	refundBytes = gprsProc->dqRefund;

	if(gprsProc->vqConsumed.integer > refundBytes)
	{
		gprsProc->vqConsumed.integer -= refundBytes;
	}
	else
	{
		//gprsProc->vqConsumed.integer = 0;
		gprsProc->vqConsumed.integer -= refundBytes;// negative for cdr of GPRS refund
	}

	if(OCS_with_external_CRM())
	{
		memset(&gprsTariff, 0x00, sizeof(_gprs_tariff));
		tariff = gprsProc->gprsTariff = &gprsTariff;
		gprsProc->gprsTariff->chargeUnit = shmp->process_info.rest_if.unit/1024;
		gprsProc->gprsTariff->basicFee = shmp->process_info.rest_if.unit_charge;
		discount = shmp->process_info.rest_if.tariff_discount/100.00;
		gprsProc->discount = 	discount;	


                pps_print_event(shmp->process_info.logMsisdn,"[%05d] Refund %lld bytes, basicFee:%.4f, unit:%d, discount:%.f, at %s",
                                        shmp->process_info.portid ,
                                        refundBytes,
                                        tariff->basicFee,
                                        tariff->chargeUnit,
                                        discount,
                                        __FUNCTION__);
		
	}
	else
	{
		tariff = gprsProc->gprsTariff;	
		discount = gprsProc->discount;
	}
	
	chargedFee = &gprsProc->chargedFee;	

	if(shmp->process_info.bundle_flag == 1)
	{
		refundFee = 0;
	}
	else	
	{
		if(tariff->basicFee == 0 || (discount == 0 && shmp->process_info.cap_flag==0))
			return 0;
	
		refundFee = (refundBytes/BYTES_PER_BASIC_FEE)*tariff->basicFee*discount;	
		shmp->caller_info.charge = shmp->caller_info.charge - refundFee;
		refund(&shmp->caller_info, refundFee,chargedFee);
	}
	

	
	if(shmp->process_info.cap_flag)
	{
		UpdateGprsCapInfo(&shmp->caller_info, refundFee, refundBytes,shmp);
	}
	if(shmp->process_info.bundle_flag)
	{
		if(OCS_with_external_CRM())
		{
			if(shmp->process_info.rest_if.used_plan_value>=refundBytes)
			{
				shmp->process_info.rest_if.used_plan_value -= refundBytes;
				shmp->process_info.rest_if.plan_value_add_this_time = (0x8000000000000000) | refundBytes;			
				pps_send_update_plan_value_req(shmp->process_info.portid);			
			}
		}
		else
		{
			UpdateGprsBundleInfo(&shmp->caller_info,refundFee, refundBytes,shmp);
		}
	}

	
	updateVolumeThisMonth(&shmp->caller_info,-refundBytes);
	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;	
	SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]Consumed(%lld bytes,%0.4f fee);refund %lld bytes,fee(balance:%0.4f promotion:%0.4f) at %s",
					shmp->process_info.portid ,gprsProc->vqConsumed.integer,shmp->caller_info.charge,
					refundBytes,chargedFee->balance,chargedFee->promotion,__FUNCTION__);
	return 1;
}

int pps_charge_postpaid_data(_state_data *shmp, long consumed_volume, long *uncharged_volume)
{
	_gprs_process			*gprsProc;
	_gprs_tariff			*tariff, gprsTariff;
	float					balance,chargedThisTime,quotaCharged;
	float					basicFee = 0.0;
	float					discount;
	int					instance;
	CHARGED_FEE		*chargedFee;
	u_long 				quotaBytes,max_quota_bytes;
	long long	 			left_bundle_volume = 0;
	
	gprsProc = &shmp->process_info.ppsService.gprs_process;
	gprsProc->vqAllocatedThisTime = 0;

	if(OCS_with_external_CRM())
	{
		memset(&gprsTariff, 0x00, sizeof(_gprs_tariff));
		tariff = gprsProc->gprsTariff = &gprsTariff;
		gprsProc->gprsTariff->chargeUnit = shmp->process_info.rest_if.unit/1024;
		gprsProc->gprsTariff->basicFee = shmp->process_info.rest_if.unit_charge;
		discount = shmp->process_info.rest_if.tariff_discount/100.00;
		gprsProc->discount = 	discount;	


                pps_print_event(shmp->process_info.logMsisdn,"[%05d] Consumed %lld bytes, basicFee:%.4f, unit:%d, discount:%.f, at %s",
                                        shmp->process_info.portid ,
                                        consumed_volume,
                                        tariff->basicFee,
                                        tariff->chargeUnit,
                                        discount,
                                        __FUNCTION__);
	}
	else
	{
		tariff = getGprsVolumeTariffWithRoamAndCap(shmp);// read Gprs tariff

		if(tariff == NULL)
		{
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]no gprs tariff assigned",shmp->process_info.portid);
			return -4;
		}
		else
		{
			gprsProc->gprsTariff = tariff;
			gprsProc->destIP = tariff->destIp;
			discount = (float)GetDiscount(tvnow.tv_sec,tariff->discountID)/100.00;
			
		}
	}
	
	left_bundle_volume = pps_get_account_bundle_data(shmp);// remain volume in bundle of user

	if(left_bundle_volume > 0)
	{
		if(left_bundle_volume > consumed_volume)// left bundle less than quota for each quest, allocate all left quota
		{
			UpdateGprsBundleInfo(&shmp->caller_info, 0.0, -consumed_volume, shmp);
			basicFee = 0;// use bundle, do not charge other way
			max_quota_bytes = 0;
		}
		else
		{
			UpdateGprsBundleInfo(&shmp->caller_info, 0.0, -left_bundle_volume, shmp);	
			//max_quota_bytes = consumed_volume - left_bundle_volume;	
			//basicFee = tariff->basicFee;
			*uncharged_volume = consumed_volume - left_bundle_volume;
			max_quota_bytes = 0;	
			basicFee = 0.0;
		}
	}
	else
	{	
		//max_quota_bytes = consumed_volume;		
		//basicFee = tariff->basicFee;
		*uncharged_volume = consumed_volume;
		//max_quota_bytes = 0;
		//basicFee = 0.0;
		max_quota_bytes = consumed_volume;		
		basicFee = tariff->basicFee;

	}
	
	balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+GetOverdraft(&shmp->caller_info);
	if(balance <= 0.001)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]low fee to charge postpaid %.02ff",
			shmp->process_info.portid, balance);
		return -5;
	}

	chargedFee = &gprsProc->chargedFee;
	memset(chargedFee,0,sizeof(CHARGED_FEE));
	chargedThisTime = 0;
	quotaCharged = 0;
	
	quotaCharged = ((max_quota_bytes+BYTES_PER_BASIC_FEE-1)/BYTES_PER_BASIC_FEE)*basicFee*discount;

	pps_print_event(shmp->process_info.logMsisdn,"[%05d]tariff:need allocate:%lld bytes,bascic fee:%0.4f,discount:%0.4f @%s",
					shmp->process_info.portid, max_quota_bytes, basicFee, discount,  __FUNCTION__);
	
	if(balance >= quotaCharged)
	{
		gprsProc->vqAllocated.integer = max_quota_bytes;
		chargedThisTime = quotaCharged;
		quotaBytes = max_quota_bytes;
	}
	else
	{
		long long 	extraBytes;
		float		extraCharged;

		extraCharged = balance/discount;
		extraBytes = (extraCharged/tariff->basicFee)*BYTES_PER_BASIC_FEE;
		gprsProc->vqAllocated.integer = extraBytes;
		gprsProc->vqAllocated.exponent = 0;		
		gprsProc->vthAllocated.integer = 0;
		gprsProc->vthAllocated.exponent = 0;
		chargedThisTime = balance;
		quotaBytes = extraBytes;
	}
	
	gprsProc->discount = discount;	
	shmp->caller_info.charge = chargedThisTime;
	//gprsProc->vqConsumed.integer = quotaBytes;
	gprsProc->vqConsumed.integer = consumed_volume;
	gprsProc->vqAllocatedThisTime = quotaBytes;

	UpdateBalance(shmp,&shmp->caller_info, -chargedThisTime,chargedFee);	
	updateVolumeThisMonth(&shmp->caller_info,quotaBytes);

	instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;
	SendAccountRealSync(instance,chargedFee->balance,chargedFee->promotion,OMC_OCODE_EDIT);// dual platform sync

	pps_print_event(shmp->process_info.logMsisdn,"[%05d]Consumed(%lld bytes,%0.4f fee);uncharged %lld bytes,fee(balance:%0.4f promotion:%0.4f) at %s",
					shmp->process_info.portid,consumed_volume,shmp->caller_info.charge,
					*uncharged_volume,chargedFee->balance,chargedFee->promotion,__FUNCTION__);
	
	return 1;
}
