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


#define		VAS_PREAUTHS_PER_TIME			5


#define		VAS_EMAIL_DOT					'@'

#define pps_set_vas_state(val) do { \
	vasProc->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)


extern	_state_machine			*smshm_ptr;
extern	_tariff_private			*blf_shmp;
extern 	struct	timeval	 		tvnow;
extern 	_pps_report 			pps_report;
extern int					csta_now_instance,csta_old_instance;

extern 	float GetSMSTariff(_state_data *shmp,u_char tariff_id);
extern 	int GetMTTariffIDWithFreeServiceControl(_account_info *ptr);
extern 	RADIUS_ATTRIBUTE_NAS_IP_ADDRESS *getNASIP(WORD pid,const RADIUS_ACCESS_REQUEST *accessRequest);
extern 	int sendRadiusAccessAccept(u_short portid,BOOL volumeFlag,BOOL durationFlag);
extern 	int sendRadiusAccessReject(u_short portid,u_char *reason);

extern 	int CutUnknownPrefix(char *number);

typedef enum _VAS_CMD{
	VAS_NULL = 0,
	VAS_SMS_AUTH,
	VAS_SMS_ACCOUNT,
	VAS_MMS_AUTH,
	VAS_MMS_SEND_CHARGING,
	VAS_MMS_RETRIEVE_ACCOUNTING,
	VAS_EMAIL_AUTH,
	VAS_EMAIL_CHARGING
}VAS_CMD;

enum{
	VAS_CALLED_FORMAT_ERROR,
	VAS_CALLED_PHONENUMBER,
	VAS_CALLED_EMAIL,
};

static int vasChargeNow(_state_data 	*shmp,float fee,u_char *peerAddr);
static int checkVasNumber(u_char *number);
static void SendVasCDR(_account_info *userInfo,_state_data *shmp,u_char *peerAddr);

static int getVasMsgType(WORD pid,RADIUS_ACCESS_REQUEST *accessRequest)
{
	if(accessRequest->pre_auth_type.flag != 0)
	{
		return accessRequest->pre_auth_type.pre_auth_type.value;
	}
	return -1;
}

static int getVasCalledNumber(WORD pid,RADIUS_ACCESS_REQUEST *accessRequest)
{		
	if(accessRequest->count_id_size.flag != 0)
	{
		if(accessRequest->count_id_size.msg_count.flag != 0)
			return accessRequest->count_id_size.msg_count.msg_count.value;
	}
	return -1;
}

static int getVasMsgID(WORD pid,RADIUS_ACCESS_REQUEST *accessRequest)
{		
	if(accessRequest->count_id_size.flag != 0)
	{
		if(accessRequest->count_id_size.msg_id.flag != 0)
			return accessRequest->count_id_size.msg_id.msg_id.value;
	}
	return -1;
}

static DWORD getVasVolume(WORD pid,RADIUS_ACCESS_REQUEST *accessRequest)
{		
	if(accessRequest->count_id_size.flag != 0)
	{
		if(accessRequest->count_id_size.size.flag != 0)
			return accessRequest->count_id_size.size.size.value;
	}
	return 0;
}

static VAS_CMD getVasCmd(WORD pid,RADIUS_ACCESS_REQUEST *access_request)
{
	int msgType;

	msgType = getVasMsgType(pid,access_request);
	switch(msgType)
	{
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_AUTH_SMS:		
			pps_print_event(0,"SEND SMS PREAUTH");
			return VAS_SMS_AUTH;
		break;
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_ACCOUNT_SMS:
			pps_print_event(0,"SEND SMS ACCOUNTING");
			return VAS_SMS_ACCOUNT;
		break;
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_AUTH_MMS:
			pps_print_event(0,"SEND MMS AUTH");
			return VAS_MMS_AUTH;
		break;
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_CHARGE_MMS_SEND:
			pps_print_event(0,"SEND MMS CHARGING");
			return VAS_MMS_SEND_CHARGING;
		break;
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_ACCOUNT_MMS_RETRIEVE:
			pps_print_event(0,"RETRIEVE MMS ACCOUNTING");
			return VAS_MMS_RETRIEVE_ACCOUNTING;
		break;
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_AUTH_MMSE:
			pps_print_event(0,"SEND MMSEMAIL AUTH");
			return VAS_EMAIL_AUTH;
		break;
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_CHARGE_MMSE:
			pps_print_event(0,"SEND MMSEMAIL CHARGING");
			return VAS_EMAIL_CHARGING;
		break;
		default:			
			pps_print_error(0,"Unsupported VAS message type");
			return VAS_NULL;
		break;
	}
	return VAS_NULL;
}

static RADIUS_ATTRIBUTE_CALLED_STATION_ID *getCalledList(WORD pid,RADIUS_ACCESS_REQUEST *access_request)
{
//	int						calledNum;
	
	if(access_request->called_station_id.option != 1)
	{
		pps_print_error(0,"No called station id");
		return NULL;
	}
	return &access_request->called_station_id;
}


BOOL isVas(WORD pid,RADIUS_ACCESS_REQUEST *access_request)
{
	int msgType;

	msgType = getVasMsgType(pid,access_request);
	switch(msgType)
	{
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_AUTH_SMS:		
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_ACCOUNT_SMS:
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_AUTH_MMS:
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_CHARGE_MMS_SEND:
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_ACCOUNT_MMS_RETRIEVE:
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_AUTH_MMSE:
		case	RADIUS_ATTRIBUTE_TYPE_NOWSMS_TYPE_VALUE_CHARGE_MMSE:
			return TRUE;
		break;
		default:
			return FALSE;
		break;
	}
	return FALSE;
}

static int allocate_VAS_PORT(WORD pid,u_char *msisdn)
{
	int					vas_port,callerLen;
	_state_data 		*shmp;

	callerLen = strlen((char *)msisdn);
	if(callerLen > MAX_ASCII_NUMBER_LEN)
	{
		pps_print_error(0,"calling station id is too long:%d",callerLen);
		return -1;	
	}
	
	vas_port = AssignProcID(0);
	if(vas_port < 0)
	{
		pps_print_error(0,"System busy ,no idle port");
		return -2;
	}		
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += vas_port;
	shmp->context_info.mtype  = VAS_O_OPEN;
	shmp->context_info.new_flag = YES;
	if(msisdn[0] == '+')
		strcpy((char *)shmp->caller_info.number, (char *)msisdn+1);	
	else
		strcpy((char *)shmp->caller_info.number, (char *)msisdn);
	shmp->process_info.ppsService.gprs_process.radiusPid = pid;
	shmp->process_info.ppsService.gprs_process.qid = INIT_QID;
	if(logMsgFlag & 0x08)
	{
		if(strstr((char *)shmp->caller_info.number, logMsisdn))
			shmp->process_info.logMsisdn = 0x01;
	}
	return vas_port;
}

int vas_process_access_request(WORD pid,RADIUS_ACCESS_REQUEST *access_request)
{
	RADIUS_TLV_TS							*csi;
	u_char									msisdn[RADIUS_MAX_LEN_OF_ATTR];
	RADIUS_ATTRIBUTE_NAS_IP_ADDRESS			*nasAddr;
	int										ppsPort=-1;
	RADIUS_ATTRIBUTE_CALLED_STATION_ID		*calledList;
	VAS_CMD									vasCmd;
	int										rst;
	u_char									action[64];
	
	vasCmd = getVasCmd(pid,access_request);
	if(vasCmd == VAS_NULL)	
		return -1;
	nasAddr = getNASIP(pid,access_request);
	if(nasAddr == NULL)
	{
		return -1;
	}		
	
	csi = &access_request->calling_station_id.calling_station_id;
	if(access_request->calling_station_id.flag != 0)
	{		
		memset(msisdn,0,RADIUS_MAX_LEN_OF_ATTR);		
		memcpy(msisdn,&csi->value[0],csi->length%RADIUS_MAX_LEN_OF_ATTR);					
	}	
	else
	{
		pps_print_error(0,"No calling station id");
		return -1;
	}

	calledList = getCalledList(pid,access_request);
	if(calledList == NULL)
		return -1;	
	
	switch(vasCmd)
	{
		case VAS_NULL:
		break;
		case VAS_SMS_AUTH:				
			sprintf((char *)action, "SMS AUTH");
			rst = checkVasNumber(msisdn);
			if(rst != VAS_CALLED_PHONENUMBER)
			{
				pps_print_error(0,"%s fail,caller:%s is not a phone number",action,msisdn);
				return -1;			
			}			
		break;
		case VAS_SMS_ACCOUNT:
			sprintf((char *)action, "SMS ACCOUNTING");
			rst = checkVasNumber(msisdn);
			if(rst != VAS_CALLED_PHONENUMBER)
			{
				pps_print_error(0,"%s fail,caller:%s is not a phone number",action,msisdn);
				return -1;			
			}	
		break;
		case VAS_MMS_AUTH:
			sprintf((char *)action, "MMS AUTH");
			rst = checkVasNumber(msisdn);
			if(rst != VAS_CALLED_PHONENUMBER)
			{
				pps_print_error(0,"%s fail,caller:%s is not a phone number",action,msisdn);
				return -1;			
			}	
		break;
		case VAS_MMS_SEND_CHARGING:		
			sprintf((char *)action, "MMS CHARGING");
			rst = checkVasNumber(msisdn);
			if(rst != VAS_CALLED_PHONENUMBER)
			{
				pps_print_error(0,"%s fail,caller:%s is not a phone number/email address",action,msisdn);
				return -1;			
			}	
		break;
		case VAS_MMS_RETRIEVE_ACCOUNTING:
			sprintf((char *)action, "SMS RETRIEVE ACCOUNTING");
		break;
		case VAS_EMAIL_AUTH:
			sprintf((char *)action, "EMAIL AUTH");
		break;
		case VAS_EMAIL_CHARGING:
			sprintf((char *)action, "EMAIL CHARGING");
		break;
	}
	pps_print_event(0,"Incoming value added service,caller:%s,%s  at %s",msisdn,action,__FUNCTION__);
	switch(vasCmd)
	{
		case VAS_MMS_RETRIEVE_ACCOUNTING:			
		case VAS_EMAIL_AUTH:			
		case VAS_EMAIL_CHARGING:
			//memset(msisdn,0,RADIUS_MAX_LEN_OF_ATTR);
		break;
		default:
		break;
	}
	
	ppsPort = allocate_VAS_PORT(pid,msisdn);
	
	if(ppsPort >= 0)
	{
		_state_data 					*shmp;
		_gprs_process					*vasProc;
		
		shmp = (_state_data *)&smshm_ptr->state_data;
		shmp += ppsPort;
		shmp->process_info.portid = ppsPort;
		vasProc = &shmp->process_info.ppsService.gprs_process;
		vasProc->gsnIP = nasAddr->nas_ip_addr.value;
		vasProc->radiusPid = pid;
		vasProc->radiusCommand = vasCmd;
		vasProc->calledList = calledList;
		vasProc->calculateCount = 0;		
		
		return ppsPort;
	}
	else
		pps_print_error(0,"%s fail,allocate process port fail",action);
	return -1;
}


static float GetMMSTariff(_state_data *shmp,u_char *calledNumber,u_char tariffid,u_char phoneOrEmail)
{
	int						i,flag=0,len=0,maxlen=0,index=0;
	float					sms_fee = 0;
	int						discount=100;
	VAS_MMS_TARIFF			*senderTariff;
   	u_char					bill_no[RADIUS_TS_VALUE_LEN];


	strcpy((char *)bill_no, (char *)calledNumber);	
		
	for(i=2;i<MAX_VAS_MMS_SEND_TARIFF;i++)
	{		
		senderTariff = &blf_shmp->vas_tariff[tariffid].vasMmsSendTariff[i];	
		if(senderTariff->mmsType != phoneOrEmail)	
			continue;
		len = strlen((char *)senderTariff->prefix);
		if(len == 0)
			continue;
		if(!strncasecmp((char *)bill_no, (char *)senderTariff->prefix, len))
		if(len>maxlen)
		{
			maxlen = len;
			flag = 1;
			index = i;
		}
	}

	if(flag == 0)
	{
		pps_print_event(shmp->process_info.logMsisdn,"[%05d]can't find suitable prefix for %s in tariff:%d ,at %s,use default",
							shmp->process_info.portid,bill_no,tariffid,__FUNCTION__);
		if(phoneOrEmail == VAS_CALLED_PHONENUMBER)
			index = 0;
		else /* VAS_CALLED_EMAIL*/
			index = 1;
	}
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]prefix %s, tariff is is :%d ,index is:%d at %s",
							shmp->process_info.portid,bill_no,tariffid,index,__FUNCTION__);
	senderTariff = &blf_shmp->vas_tariff[tariffid].vasMmsSendTariff[index];
	
	sms_fee = senderTariff->chargeFee;
	discount = GetDiscount(tvnow.tv_sec,senderTariff->discountID);
 
	sms_fee = sms_fee * discount /100;	
	return sms_fee;
}



static float getVasMMSSenderTariffFee(_state_data 	*shmp,u_char *calledNumber,int 	tariffid)
{	
	u_short 			portid;	
	_gprs_process		*vasProc;
	u_char				*pEmailDot;
		
	vasProc = &shmp->process_info.ppsService.gprs_process;	
	portid = shmp->process_info.portid;

	switch(vasProc->calledType)
	{
		case VAS_CALLED_PHONENUMBER:
			CutUnknownPrefix((char *)calledNumber);
			return GetMMSTariff(shmp,calledNumber,tariffid,VAS_CALLED_PHONENUMBER);
		break;
		case VAS_CALLED_EMAIL:
			pEmailDot = (u_char *)strchr((char *)calledNumber,VAS_EMAIL_DOT);
			return GetMMSTariff(shmp,pEmailDot+1,tariffid,VAS_CALLED_EMAIL);
		break;
		default:
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]unsupported called type:%d  at %s",
							portid,vasProc->calledType,__FUNCTION__);
		break;
	}		
	return -1;
}

static float getVasSenderTariffFee(_state_data 	*shmp,u_char *calledNumber)
{
	u_short				portid;
	int					tariffid;
	_gprs_process 		*vasProc;

	
	vasProc = &shmp->process_info.ppsService.gprs_process;	
	portid = shmp->process_info.portid;
	tariffid = GetMOTariffIDWithFreeServiceControl(shmp->caller_info.head,shmp->caller_info.tail);

	switch(vasProc->radiusCommand)
	{
		case VAS_SMS_AUTH:
		case VAS_SMS_ACCOUNT:
			if(vasProc->calledType != VAS_CALLED_PHONENUMBER)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]unsupported call type:%d at %s",
							portid,vasProc->calledType,__FUNCTION__);
				return -1;
			}									
			strcpy((char *)shmp->charge_info.number, (char *)calledNumber);
			CutUnknownPrefix((char *)shmp->charge_info.number);
			return GetSMSTariff(shmp,tariffid);
		break;
		case VAS_MMS_AUTH:
		case VAS_MMS_SEND_CHARGING:
			if(vasProc->calledType != VAS_CALLED_PHONENUMBER && vasProc->calledType != VAS_CALLED_EMAIL)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]unsupported call type:%d at %s",
							portid,vasProc->calledType,__FUNCTION__);
				return -1;
			}	
			return getVasMMSSenderTariffFee(shmp,calledNumber,tariffid);
		break;
		default:			
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]unsupported cmd:%d  at %s",
							portid,vasProc->radiusCommand,__FUNCTION__);
		break;
	}
	return -1;	
}

static float getVasMMSRetrieveTariffFee(_state_data *shmp,int	tariffid)
{
	u_short						portid;	
	VAS_MMS_RETRIEVE_TARIFF		*mmsRetrieveTariff;
	float						fee,discount;
	
	portid = shmp->process_info.portid;
	
	mmsRetrieveTariff = &blf_shmp->vas_tariff[tariffid].vasMmsRetrieveTariff[0];
	fee = mmsRetrieveTariff->chargeFee;
	discount = (float)GetDiscount(tvnow.tv_sec,mmsRetrieveTariff->discountID)/100;
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]fee:%0.4f,disount:%0.4f,at %s",
							portid,fee,discount,__FUNCTION__);
	return fee*discount;
}

static float getVasEmailTariffFee(_state_data 	*shmp,int	tariffid)
{
	u_short								portid;
	VAS_MMSEMAIL_DELIVER_TARIFF			*mmsEmailTariff;
	float								fee,discount;
	
	portid = shmp->process_info.portid;
	
	mmsEmailTariff = &blf_shmp->vas_tariff[tariffid].vasMmsEmailDeliverTariff[0];
	fee = mmsEmailTariff->chargeFee;
	discount = (float)GetDiscount(tvnow.tv_sec,mmsEmailTariff->discountID)/100;
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]fee:%0.4f,disount:%0.4f,at %s",
							portid,fee,discount,__FUNCTION__);
	return fee*discount;
}

static float getVasReceiverTariffFee(_state_data 	*shmp,u_char *calledNumber)
{
	u_short				portid;
	int					tariffid;
	_gprs_process 		*vasProc;

	
	vasProc = &shmp->process_info.ppsService.gprs_process;	
	portid = shmp->process_info.portid;
		
	tariffid = GetMTTariffIDWithFreeServiceControl(&shmp->charge_info);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]%s tariff id is:%d,at %s",
							portid,shmp->charge_info.number,tariffid,__FUNCTION__);
	switch(vasProc->radiusCommand)
	{
		case VAS_MMS_RETRIEVE_ACCOUNTING:
			return getVasMMSRetrieveTariffFee(shmp,tariffid);
		break;
		case VAS_EMAIL_AUTH:
		case VAS_EMAIL_CHARGING:
			return getVasEmailTariffFee(shmp,tariffid);
		break;
		default:			
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]unsupported cmd:%d  at %s",
							portid,vasProc->radiusCommand,__FUNCTION__);
		break;
	}
	return -1;
}

static int checkVasNumber(u_char *number)
{
	int		i,len;
	u_char	*pEmailDot;
	
	pEmailDot = (u_char *)strchr((char *)number,VAS_EMAIL_DOT);
	if(pEmailDot == NULL)
	{
		len = strlen((char *)number);
		if(len >= MAX_ASCII_NUMBER_LEN)
		{
			pps_print_error(0,"too long phone number at %s",__FUNCTION__);
			return VAS_CALLED_FORMAT_ERROR;
		}
		for(i = 0;i < len;i++)
		{		
			if(!isdigit(number[i])&& number[i] != '+')
			{
				pps_print_error(0,"illegal character in phone number at %s",__FUNCTION__);
				return VAS_CALLED_FORMAT_ERROR;				
			}
		}
		return VAS_CALLED_PHONENUMBER;
	}	
	else if(*(pEmailDot+1) == 0)
	{
		pps_print_error(0,"illegal email address at %s",__FUNCTION__);
		return VAS_CALLED_FORMAT_ERROR;
	}
	return VAS_CALLED_EMAIL;
}

extern BOOL isUseLegal(_account_info *userInfo, u_char *reason);
static float getVasSingleFee(_state_data 	*shmp,u_char *calledNumber,u_char *failReason)
{
	u_short				portid;
	_gprs_process 		*vasProc;
	float				chargedFee;
 	
	portid = shmp->process_info.portid;
	vasProc = &shmp->process_info.ppsService.gprs_process;			
	
	switch(vasProc->radiusCommand)
	{
		case VAS_SMS_AUTH:
		case VAS_SMS_ACCOUNT:
		case VAS_MMS_AUTH:
		case VAS_MMS_SEND_CHARGING:			
			chargedFee = getVasSenderTariffFee(shmp,calledNumber);
			if(chargedFee < 0)
				return -1;
		break;
		case VAS_MMS_RETRIEVE_ACCOUNTING:
		case VAS_EMAIL_AUTH:
		case VAS_EMAIL_CHARGING:
			if(vasProc->calledType != VAS_CALLED_PHONENUMBER)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]Wrong called type:%d ,not phone number at %s",
								portid,vasProc->calledType,__FUNCTION__);
				return -1;
			}				
			strcpy((char *)shmp->charge_info.number, (char *)calledNumber);
			CutUnknownPrefix((char *)shmp->charge_info.number);
			if(GetAccountPos(&shmp->charge_info) == TRUE)
			{						
				if(isUseLegal(&shmp->charge_info,failReason) == FALSE)
				{							
					pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s",shmp->process_info.portid ,failReason);
					return -1;
				}
				pps_print_event(shmp->process_info.logMsisdn,"[%05d]prepaid user:%s ask for mms retrieve/email preauth/email charging",portid,calledNumber);					
			}
			else
			{						
				pps_print_event(shmp->process_info.logMsisdn,"[%05d]postpaid user:%s ask for mms retrieve/email preauth/email charging",portid,calledNumber);
				return 0;
			}
			chargedFee = getVasReceiverTariffFee(shmp,calledNumber);
			if(chargedFee < 0)
				return -1;
		break;		
		default:
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]unsupported cmd:%d  at %s",
							portid,vasProc->radiusCommand,__FUNCTION__);
			return -1;
		break;
	}	
	return chargedFee;
}

enum{
	VAS_SELECT_PREAUTH,
	VAS_SELECT_CHARGE,
};
static long getVasTotalFee(_state_data 		*shmp,u_char authOrCharge,u_char *failReason)
{
	int										i,calledListNum,rst;
	_gprs_process 							*vasProc;
	RADIUS_ATTRIBUTE_CALLED_STATION_ID		*calledList;
	RADIUS_TLV_TS							*called;
	u_char									calledNumber[RADIUS_TS_VALUE_LEN],*ptr;
	u_short									portid;
	float									chargedFee;
	
	portid = shmp->process_info.portid;
	vasProc = &shmp->process_info.ppsService.gprs_process;
	
	if(vasProc->calledList == NULL)
	{		
		sprintf((char *)failReason, "no called list");
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]%sfail,%s",portid,shmp->caller_info.number,failReason);
		return -1;
	}
	calledList = vasProc->calledList;
	if(calledList->option != 1)
	{		
		sprintf((char *)failReason,"called list's option is not 1,option is:%d",calledList->option);
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s fail,%s",portid,shmp->caller_info.number,failReason);
		return -1;
	}	
	calledListNum = calledList->called.server.num;
	for(i=0;i<VAS_PREAUTHS_PER_TIME;i++)
	{
		if(vasProc->calculateCount > RADIUS_MAX_NUM_OF_NOWSMS_CALLED_ID)
		{		
			sprintf((char *)failReason,"called list num:%d > Max calledlist Num:%d",
					vasProc->calculateCount,RADIUS_MAX_NUM_OF_NOWSMS_CALLED_ID);
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s fail,%s",portid,shmp->caller_info.number,failReason);
			return -1;
		}
		if(vasProc->calculateCount >= calledListNum)
		{
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]%s,total calledNum:%d,total fee:%0.4f",
							portid,shmp->caller_info.number,calledListNum,vasProc->chargedFee.balance);
 			return (long)vasProc->chargedFee.balance;
		}
		called = &calledList->called.server.called_station_id[vasProc->calculateCount];
		memcpy(calledNumber,called->value,called->length%RADIUS_TS_VALUE_LEN);
		calledNumber[called->length%RADIUS_TS_VALUE_LEN] = 0;
		rst = checkVasNumber(calledNumber);
		if(rst == VAS_CALLED_FORMAT_ERROR)
		{
			sprintf((char *)failReason,"Called Number:%s format error",calledNumber);
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s fail,%s",portid,shmp->caller_info.number,failReason);
			return -1;			
		}
		vasProc->calledType = rst;
		ptr = calledNumber;
		if(rst == VAS_CALLED_PHONENUMBER)
		{
			if(calledNumber[0] == '+')
				ptr = calledNumber+1;				
		}
		chargedFee = getVasSingleFee(shmp,ptr,failReason);
		if(chargedFee < 0)
		{			
			return -1;
		}		
		pps_print_event(shmp->process_info.logMsisdn,"[%05d]caller:%s,called:%s,fee:%0.4f",
						portid,shmp->caller_info.number,calledNumber,chargedFee);
		vasProc->chargedFee.balance += chargedFee;		
		
		if(authOrCharge == VAS_SELECT_CHARGE)
		{
			vasChargeNow(shmp,chargedFee,ptr);
		}
		vasProc->calculateCount++;
		switch(vasProc->radiusCommand)
		{
			case VAS_SMS_AUTH:			
			case VAS_MMS_AUTH:
				/*	multiple recipients	*/
			break;	
			case VAS_MMS_SEND_CHARGING: 		
			case VAS_SMS_ACCOUNT:
			case VAS_MMS_RETRIEVE_ACCOUNTING:
			case VAS_EMAIL_AUTH:
			case VAS_EMAIL_CHARGING:
				/*	only one recipient	*/
				pps_print_event(shmp->process_info.logMsisdn,"[%05d]%s,total calledNum:%d,total fee:%0.4f",
							portid,shmp->caller_info.number,vasProc->calculateCount,vasProc->chargedFee.balance);
 				return (long)vasProc->chargedFee.balance;
			break;
		}
	}		
	return -2;/* continue	*/
}

static int vasPreAuth(_state_data 	*shmp,u_char *failReason)
{
	_gprs_process 							*vasProc;
	//u_short									portid;
	float									balance;
	long									totalChargedFee;
	
	//portid = shmp->process_info.portid;
	vasProc = &shmp->process_info.ppsService.gprs_process;

	totalChargedFee = getVasTotalFee(shmp,VAS_SELECT_PREAUTH,failReason);	 
	switch(totalChargedFee)
	{	
		case -1:
			return -1;
		break;
		case -2:
			return 0;
		break;		 
	}
	
	switch(vasProc->radiusCommand)
	{
		case VAS_SMS_AUTH:
		case VAS_SMS_ACCOUNT:
		case VAS_MMS_AUTH:
		case VAS_MMS_SEND_CHARGING: 		
			balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+GetOverdraft(&shmp->caller_info);	
		break;
		case VAS_MMS_RETRIEVE_ACCOUNTING:
		case VAS_EMAIL_AUTH:
		case VAS_EMAIL_CHARGING:
			balance = GetBalanceWithPromotionControl(shmp,&shmp->charge_info)+GetOverdraft(&shmp->caller_info);
		break;
	}
	if(balance < totalChargedFee)
		return -1;
	return 1;
}

static int vasChargeNow(_state_data 	*shmp,float fee,u_char *peerAddr)
{
	_gprs_process 							*vasProc;
	u_short									portid;
	_account_info							*userInfo;
	DWORD									instance;
	CHARGED_FEE								chargedFee;

	if(fee == 0)
		return 1;
	vasProc = &shmp->process_info.ppsService.gprs_process;
	portid = shmp->process_info.portid;
	switch(vasProc->radiusCommand)
	{
		case VAS_SMS_ACCOUNT:
		case VAS_MMS_SEND_CHARGING: 		
			userInfo = &shmp->caller_info;
		break;		
		case VAS_MMS_RETRIEVE_ACCOUNTING:
		case VAS_EMAIL_CHARGING:
			userInfo = &shmp->charge_info;
		break;
		default:
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]Vas cmd:%d ,no need to charge",
					portid,vasProc->radiusCommand);
			return -1;
		break;
	}
	userInfo->charge = fee;
	UpdateBalance(shmp,userInfo, -fee,&chargedFee);		
	instance = userInfo->head * UNIT_STORE_ACCOUNT + userInfo->tail;
	SendAccountRealSync(instance,chargedFee.balance,chargedFee.promotion,OMC_OCODE_EDIT);	
	SendVasCDR(userInfo,shmp,peerAddr);
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]%s charged fee(balance:%0.4f,promotion:%0.4f)",
					portid,userInfo->number,chargedFee.balance,chargedFee.promotion);
	return 1;
}

static int vasCharge(_state_data 		*shmp,u_char *failReason)
{
	//_gprs_process 							*vasProc;
	//u_short									portid;
	long									totalChargedFee;
	
	//portid = shmp->process_info.portid;
	//vasProc = &shmp->process_info.ppsService.gprs_process;
	
	totalChargedFee = getVasTotalFee(shmp,VAS_SELECT_CHARGE,failReason);
	switch(totalChargedFee)
	{	
		case -1:
			return -1;
		break;
		case -2:
			return 0;
		break;		 
	}
	return 1;
}

extern BOOL checkMiniBalanceForGprs(_state_data *shmp);
int vasProcess(u_short portid)
{
	_gprs_process		*vasProc;
	_state_data 		*shmp;
	int 				result = SERVICE_KEEP_STATE,rst;
	u_char				failReason[128]={0};
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	vasProc = &shmp->process_info.ppsService.gprs_process;

	switch(vasProc->state)
	{
		case VAS_STATE_IDLE:		
			if(vasProc->radiusCommand != VAS_NULL)
				shmp->cdr_info.answertime = tvnow.tv_sec;
			switch(vasProc->radiusCommand)
			{
				case VAS_SMS_AUTH:
				case VAS_MMS_AUTH:
					CutUnknownPrefix((char *)shmp->caller_info.number);
					if(GetAccountPos(&shmp->caller_info) == TRUE)
					{						
						if(isUseLegal(&shmp->caller_info,failReason) == FALSE)
						{
							pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s",shmp->process_info.portid ,failReason);
							sendRadiusAccessReject(portid,failReason);
							pps_set_vas_state(VAS_STATE_TERMINATE);
							break;
						}
						if(checkMiniBalanceForGprs(shmp) == FALSE)
						{
							sendRadiusAccessReject(portid, (u_char *)"Low Balance for vas service");						
							pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s Low Balance for vas service",portid,shmp->caller_info.number);
							pps_set_vas_state(VAS_STATE_TERMINATE);
							break;
						}	
						pps_print_event(shmp->process_info.logMsisdn,"[%05d]prepaid user:%s ask for vas preauth",portid,shmp->caller_info.number);
						pps_set_vas_state(VAS_STATE_AUTH);
					}
					else
					{						
						pps_print_event(shmp->process_info.logMsisdn,"[%05d]postpaid user:%s ask for vas preauth",portid,shmp->caller_info.number);
						pps_set_vas_state(VAS_STATE_ACCEPT);
					}
				break;
				case VAS_EMAIL_AUTH:					
					pps_set_vas_state(VAS_STATE_AUTH);
				break;
				case VAS_SMS_ACCOUNT:
				case VAS_MMS_SEND_CHARGING:
					CutUnknownPrefix((char *)shmp->caller_info.number);
					if(GetAccountPos(&shmp->caller_info) == TRUE)
					{				
						if(isUseLegal(&shmp->caller_info,failReason) == FALSE)
						{
							pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s",shmp->process_info.portid ,failReason);
							sendRadiusAccessReject(portid,failReason);
							pps_set_vas_state(VAS_STATE_TERMINATE);
							break;
						}
						if(checkMiniBalanceForGprs(shmp) == FALSE)
						{
							sendRadiusAccessReject(portid, (u_char *)"Low Balance for vas service");						
							pps_print_error(shmp->process_info.logMsisdn,"[%05d]%s Low Balance for vas service",portid,shmp->caller_info.number);
							pps_set_vas_state(VAS_STATE_TERMINATE);
							break;
						}	
						pps_print_event(shmp->process_info.logMsisdn,"[%05d]prepaid user:%s ask for vas charging",portid,shmp->caller_info.number);
						pps_set_vas_state(VAS_STATE_CHARGE);
					}
					else
					{						
						pps_print_event(shmp->process_info.logMsisdn,"[%05d]postpaid user:%s ask for vas charging",portid,shmp->caller_info.number);
						pps_set_vas_state(VAS_STATE_ACCEPT);
					}
				break;
				case VAS_MMS_RETRIEVE_ACCOUNTING:
				case VAS_EMAIL_CHARGING:
					pps_set_vas_state(VAS_STATE_CHARGE);
				break;
			}
		break;
		case VAS_STATE_AUTH:
			rst = vasPreAuth(shmp,failReason);
			if(rst > 0)
				pps_set_vas_state(VAS_STATE_ACCEPT);
			else if(rst < 0)
				pps_set_vas_state(VAS_STATE_REJECT);
		break;
		case VAS_STATE_CHARGE:
			rst = vasCharge(shmp,failReason);
			if(rst > 0)
				pps_set_vas_state(VAS_STATE_ACCEPT);
			else if(rst < 0)
				pps_set_vas_state(VAS_STATE_REJECT);
		break;
		case VAS_STATE_ACCEPT:
			sendRadiusAccessAccept(portid,FALSE,FALSE);
			pps_set_vas_state(VAS_STATE_TERMINATE);
		break;
		case VAS_STATE_REJECT:
			sendRadiusAccessReject(portid,failReason);
			pps_set_vas_state(VAS_STATE_TERMINATE);
		break;
		default:
		case VAS_STATE_TERMINATE:
			memset(vasProc,0,sizeof(_gprs_process));
			result = SERVICE_EXIT_PPS;
		break;
	}
	return result;
}


static int reportVasCDRIncrement(u8 vasType,float charge)
{
	int 				index=0;
	 _pps_vas_record	*vas_record;
	 
	PutLogFunID("reportVasCDRIncrement");
	
	index = pps_report.ptrTail; 
	vas_record = &pps_report.daily_report[index].call_report.vas_record;
	switch(vasType)
	{
		case VAS_SMS_ACCOUNT:
			vas_record->smsNumber++;
			vas_record->smsCharge += charge;
		break;
		case VAS_MMS_SEND_CHARGING:
			vas_record->mmsSendNumber++;
			vas_record->mmsSendCharge += charge;
		break;
		case VAS_MMS_RETRIEVE_ACCOUNTING:
			vas_record->mmsRetrieveNumber++;
			vas_record->mmsRetrieveCharge += charge;
		break;
		case VAS_EMAIL_CHARGING:
			vas_record->mmsEmailNumber++;
			vas_record->mmsEmailCharge += charge;
		break;
		default:
			return -1;
		break;
	}
	
	pps_report.daily_report[index].balance_summary.today_charge += charge;
	return 1;
}

static int vasCDRStatistics(_tocdr_report* pbuf,u8 vasType)
{
	long      				charge=0;
	//char        			buf[BUFSIZE];
	_pps_vas_csta_items		*vasCsta;
	
	PutLogFunID("vasCDRStatistics");
	
	memcpy(&charge , pbuf->charge, 4);
	vasCsta = &pps_csta_buffer.csta_record[csta_old_instance].vasCsta;

	switch(vasType)
	{
		case VAS_SMS_ACCOUNT:
			vasCsta->smsNumber++;
			vasCsta->smsCharge += charge;
		break;
		case VAS_MMS_SEND_CHARGING:
			vasCsta->mmsSendNumber++;
			vasCsta->mmsSendCharge += charge;
		break;
		case VAS_MMS_RETRIEVE_ACCOUNTING:
			vasCsta->mmsRetrieveNumber++;
			vasCsta->mmsRetrieveCharge += charge;
		break;
		case VAS_EMAIL_CHARGING:
			vasCsta->mmsEmailNumber++;
			vasCsta->mmsEmailCharge += charge;
		break;
		default:
			return -1;
		break;
	}
		
	reportVasCDRIncrement(vasType,charge);
	AddPPSStatistics(MIBPPS_CSTANUMBER,0);
	return 1;	
}

static void SendVasCDR(_account_info *userInfo,_state_data 	*shmp,u_char *peerAddr)
{
	_tocdr_report	pcdr;
	char			buf[BUFSIZE]={""}; 
	int 			balance;
	int 			head, tail;
	long			charge;
	_gprs_process	*vasProc;
	//struct in_addr	nas_addr;
	u16				cdrType;			
		
	PutLogFunID("SendVasCDR");

	memset(&pcdr,0,sizeof(_tocdr_report));
	
	vasProc = &shmp->process_info.ppsService.gprs_process;

	switch(vasProc->radiusCommand)
	{
		case VAS_SMS_ACCOUNT:
			cdrType = CT_VAS_SMS;
			pcdr.mtype = MO_CDR_TYPE;
			memcpy(pcdr.peerAddress,peerAddr,strlen((char *)peerAddr)%128);					
		break;
		case VAS_MMS_SEND_CHARGING:
			cdrType = CT_VAS_MMS_SEND;
			pcdr.mtype = MO_CDR_TYPE;
			memcpy(pcdr.peerAddress,peerAddr,strlen((char *)peerAddr)%128);	
		break;
		case VAS_MMS_RETRIEVE_ACCOUNTING:
			cdrType = CT_VAS_MMS_RETRIEVE;
			pcdr.mtype = MT_CDR_TYPE;
			memcpy(pcdr.peerAddress,shmp->caller_info.number,strlen((char *)shmp->caller_info.number)%128);		
		break;
		case VAS_EMAIL_CHARGING:
			cdrType = CT_VAS_MMS_EMAIL;
			pcdr.mtype = MT_CDR_TYPE;
			memcpy(pcdr.peerAddress,shmp->caller_info.number,strlen((char *)shmp->caller_info.number)%128);	
		break;
		default:
			return;
		break;
	}
			
		
	head = userInfo->head;
	tail = userInfo->tail;		
	
	pcdr.type[0] = cdrType;
	pcdr.type[1] = cdrType>>8;
		
	shmp->cdr_info.releasetime = shmp->cdr_info.answertime;
	AsciiTime(buf, shmp->cdr_info.answertime);
	AsciiToBcd(pcdr.answertime,buf,14);

	AsciiTime(buf, shmp->cdr_info.releasetime);
	AsciiToBcd(pcdr.releasetime,buf,14);
	
	charge = (long)userInfo->charge;
	charge = abs(charge);
	memcpy(pcdr.charge,&charge,4);
	balance = (int) GetAccountBalance(head, tail);
	memcpy(pcdr.balance,&balance,4);
	
	GetAccountMsisdn(head,tail,&pcdr.msisdn[1]);
	ByteRev(pcdr.msisdn,9,0);
	pcdr.msisdn[0]=0x91;
	buf[0] = 0;
		
	pcdr.tcos[0] = NORMAL_CLEARING%256;
	
	vasCDRStatistics(&pcdr,vasProc->radiusCommand);
	StoreCDR(&pcdr);
	pps_real_update_user_file(head, tail);

	pps_print_event(shmp->process_info.logMsisdn,"[%05d]%s send vas cdr,vas type:%d,charge:%ld"
					,shmp->process_info.portid ,userInfo->number,vasProc->radiusCommand,charge);
}


