
#include     "../include/includes.h"
#include     "../include/ramdata.h"
#include     "../include/message.h"
#include     "../include/ppsm.h"
#include     "../include/crypt.h"
#include     "../include/ppsoid.h"
#include     "../include/cdrme.h"
#include     "../include/param.h"
//#include	   "../include/mfunc.h"

#include    "../include/plat_inc.h"

#include    "../../../../plat/scf/src/include/scfmsg.h"
/*------------------------------------------------*/

extern _state_machine		*smshm_ptr;
extern _tariff_private		*blf_shmp;
extern _ram_data			*ramshm_ptr;
extern _debug_info			*debug_shmp;
extern int					csta_now_instance,csta_old_instance;
extern int					logMsgFlag;
extern struct timeval 		tvnow;
/*------------------------------------------------*/
extern	PPSParamStruct	ppsParam;
/*------------------------------------------------*/
extern int	FindPPSSubsStoreIndex();
extern int	AccountLatchFlagCheck();
extern int	GetRechargeCardValidTime();
extern int	GetRechargeCardFaceValue();
extern int	IsPSTNMaitenance();
extern int	IsVMSDeposit();
extern int	IsCallingCardNumber(char *number);
//extern int  des();
extern void SendAccountRealSync_2(_account_info *accountInfo, double balance, double promotion, int ocode);
extern	void increPoolChildCharged(_account_info *ptr,double charged);
extern	void increPoolChildSMS(_account_info *ptr);
extern	void increPoolChildCalls(_account_info *ptr);
extern	BOOL isPromotionCanBeUsedAtThisPoint(_state_data 	*shmp);
extern	float BillingFormula(_state_data *shmp,_bill_tariff *ptar,int startTime);
extern	float TimeToCharge(_state_data *shmp,_bill_tariff *ptar, int duration,int begin_time, int init_bill);
extern	double GetBalanceWithPromotionControl(_state_data 	*stateProc,_account_info *ptr);
extern	float caculateMiniCharge(_state_data 	*shmp,_bill_tariff *ptar);
extern	int preLeftTime(_state_data 	*shmp,float	balance,_bill_tariff *ptar);
extern	void PPSSendUserSMSNotification(int head, int tail,char *msgSend);
extern	u_char getCurrentTier(_state_data 	*shmp,_bill_tariff *ptar);
extern	void ChangeTariffToPoolCallTariff(_state_data *shmp,_bill_tariff 	*ptar);
extern	float getCurrentFee(_state_data 	*shmp);
extern	int isPoolChildReachSMSLimit(_state_data *shmp);
extern char *sendPPSNotificationMessage(int head, int tail, int sms_id);
extern 	u_char *getSmsSentenceNative(SMS_SENTENCE	id,u8 languageType);
extern	BYTE ChangeUserCategory(BYTE *msisdn,BYTE type);
extern int subscribe_bundle_plan_send_used_up_info(char *caller_number, SMS_SENTENCE id);
extern void pps_print_error(u_char logMsisdnFlag,const char *fmt, ...);
extern 	int reportGprsCDRIncrement(int duration, float charge, long gprsVolume);
float		GetAccountBalance(int head, int tail);
int isPoolChildReachFeeLimit(_account_info *ptr);
/*------------------------------------------------*/

int GetMOTariffData(_state_data *shmp,_bill_tariff *ptar, char *call_no, int zone_id)
{
	int		len=0,maxlen=0,msisdn_len;    
	int		loop,index=-1;
	_bill_tariff	*ptr;

	PutLogFunID("GetMOTariffData");

	msisdn_len = strlen(call_no);
	if (zone_id >= MAX_ZONE_NUM || zone_id <0) zone_id = 0;
	if(ppsParam.serviceControl.cos_group_flag)// only use one type of tariff table in specify system
	{// search by tree in this kind of tariff table
              if(zone_id>=MAX_ZONE_NUM_NEW)
                {
                        DebugMsg(debugmib.display.asciiOut, "search prefix failed: zone id = %d, called_no=%s",
                                zone_id,call_no);

                        return 0;
                }
                index = pps_get_charge_prefix(zone_id, call_no);// search tariff table by prefix of called number

                if(index>=0)
                {
                    ptr = &blf_shmp->bill_tariff[index/BILLING_BASE_NUM][index%BILLING_BASE_NUM];
                    memcpy(ptar,ptr,sizeof(_bill_tariff));// cpy content of tariff table
					if(shmp != NULL)// save address of table
					{
						shmp->process_info.tariffMoReadOnly = &blf_shmp->bill_tariff[index/BILLING_BASE_NUM][index%BILLING_BASE_NUM];
					}
					return 1;
                }
                else
                    return 0;
		/*	
		for(loop0=0;loop0<COS_GROUP_NUM;loop++)
		{
			for(loop=0;loop<BILLING_BASE_NUM;loop++)
			{
				ptr = &blf_shmp->bill_tariff[zone_id][loop];
				len = strlen(ptr->bill_id);
				if(len == 0) break;
				if(len>maxlen && len<=msisdn_len)
				{
					if(!strncasecmp(call_no,ptr->bill_id,len))
					{
						maxlen = len;
						index = loop;
					}
				}
			}
		}
		*/
	}
	else
	{// for this kind of tariff table, has 16 sub items; each has 1024 records
		for(loop=0;loop<BILLING_BASE_NUM;loop++)// match prefix one by one
		{
			ptr = &blf_shmp->bill_tariff[zone_id][loop];
			len = strlen(ptr->bill_id);
			if(len == 0) break;
			if(len>maxlen && len<=msisdn_len)
			{
				if(!strncasecmp(call_no,ptr->bill_id,len))
				{
					maxlen = len;
					index = loop;
				}
			}
		}
	}
    if(index>=0)
	{
		ptr = &blf_shmp->bill_tariff[zone_id][index];
		memcpy(ptar,ptr,sizeof(_bill_tariff));		
		if(shmp != NULL)
		{
			shmp->process_info.tariffMoReadOnly =  &blf_shmp->bill_tariff[zone_id][index];
		}
		return 1;
	}
    return FALSE;
}    

int
IsNormalAccount(_account_info *ptr)
{
    if(ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) 
    {
		return TRUE;
    }
    return  FALSE;
}

int GetAccountFavoriteNumberCounter(_account_info *ptr)
{
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

    if(ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) 
    {		
		return	shmp->prepaid_info[ptr->head][ptr->tail].favorite_numbers;
    }else
	{
		return -1;
	}
}

int StoreAccountFavoriteNumber(_account_info *ptr,char *msisdn,int index)
{
	int i,len;
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("StoreAccountFavoriteNumber");

	if(index>0 && index<=MAX_FAVORITE_NUMBERS)
	{
		len = strlen(msisdn);
		shmp->prepaid_info[ptr->head][ptr->tail].favorite_number[index-1][MAX_MSISDN_BLEN-1] = len;
		if(len%2==1)
			len ++;
		AsciiToBcd(shmp->prepaid_info[ptr->head][ptr->tail].favorite_number[index-1],msisdn,len);
		return index;
	}

    if(ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) 
    {
		for(i=0;i<MAX_FAVORITE_NUMBERS;i++)
		{
			if(shmp->prepaid_info[ptr->head][ptr->tail].favorite_number[i][MAX_MSISDN_BLEN-1]<3)
			{
				len = strlen(msisdn);
				shmp->prepaid_info[ptr->head][ptr->tail].favorite_number[i][MAX_MSISDN_BLEN-1] = len;
				if(len%2==1)
					len ++;
				AsciiToBcd(shmp->prepaid_info[ptr->head][ptr->tail].favorite_number[i],msisdn,len);
				shmp->prepaid_info[ptr->head][ptr->tail].favorite_numbers++;
				if(shmp->prepaid_info[ptr->head][ptr->tail].favorite_numbers>MAX_FAVORITE_NUMBERS)
				  shmp->prepaid_info[ptr->head][ptr->tail].favorite_numbers=MAX_FAVORITE_NUMBERS;
				return (i+1);
			}
		}
		return (i+1);
    }else
	{
		return -1;
	}
}

int GetAccountFavoriteNumber(_account_info *ptr,char *msisdn,u_char index)
{
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	int	len;

	PutLogFunID("GetAccountFavoriteNumber");

	if(index<=MAX_FAVORITE_NUMBERS && index>0)
	{
		len = shmp->prepaid_info[ptr->head][ptr->tail].favorite_number[index-1][MAX_MSISDN_BLEN-1];
		if(len<3 || len>16)
		{
			return 0;
		}
		memcpy(msisdn,
				shmp->prepaid_info[ptr->head][ptr->tail].favorite_number[index-1],
				MAX_MSISDN_BLEN);
		return index;
	}
	return 0;
}


int
GetAccountPos(_account_info *ptr)
{
    int              len=0,store_index;
	char             msisdn[32];

	PutLogFunID("GetAccountPos");

	
	len = strlen((char *)ptr->number);
	strcpy(msisdn, (char *)ptr->number);


	if(len<MIN_MSISDN_ALEN || len>MAX_MSISDN_ALEN)
	{
	      ptr->head = MSISDN_HEAD_NUM + 1;
	      ptr->tail = UNIT_STORE_ACCOUNT + 1;
	      return FALSE;
	}

	store_index = FindPPSSubsStoreIndex(atoll((char *)ptr->number));

	if(store_index<0)
	{
		ptr->head = MSISDN_HEAD_NUM + 1;
		ptr->tail = UNIT_STORE_ACCOUNT + 1;
		return FALSE;
	}

	ptr->head = store_index / UNIT_STORE_ACCOUNT;
	ptr->tail = store_index % UNIT_STORE_ACCOUNT;

	return TRUE;
}


int 
GetUserInstance(long long head,int *instance)
{
	int loop;

    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
   
	PutLogFunID("GetUserInstance");

   for(loop=0;loop<MSISDN_HEAD_NUM;loop++)
   {
    if(head == shmp->msisdn_head[loop])
	{
	  *instance = loop ;
	  return 1;
	}
   }
   return 0;
}

int 
GetUserPos(int *head,long long head1)
{
	int loop;

    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
   
	PutLogFunID("GetUserPos");

   for(loop=0;loop<MSISDN_HEAD_NUM;loop++)
   {
	if(shmp->msisdn_head[loop] !=0)
    if(head1 == shmp->msisdn_head[loop])
	{
	  *head = loop;
	  return 1;
	}
   }
   for(loop=0;loop<MSISDN_HEAD_NUM;loop++)
   {
     if(shmp->msisdn_head[loop] == 0)
	 {
	   *head = loop;
	   return 2;
	 }
   }
   return 0;
}


float
GetBalance(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetBalance");

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT){
	    if(shmp->prepaid_info[ptr->head][ptr->tail].balance>=ppsParam.serviceControl.maxBalance)
			shmp->prepaid_info[ptr->head][ptr->tail].status = T_SUSPEND;
		if(shmp->prepaid_info[ptr->head][ptr->tail].msisdn[MAX_MSISDN_BLEN-1]<=2)
			return 0;
        return (shmp->prepaid_info[ptr->head][ptr->tail].balance +
				shmp->prepaid_info[ptr->head][ptr->tail].promo_balance);
	}
    return 0;
}

long
GetExpiration(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetExpiration");

    return (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) ?
            shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date
            : (tvnow.tv_sec+1);
}

long
GetIncomingExpiration(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetIncomingExpiration");

    return (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) ?
            shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date
            : (tvnow.tv_sec+1);
}

int
GetOverdraft(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetOverdraft");

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

int IsPromotionExpired(_account_info *ptr)
{
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("IsPromotionExpired");

	if(ptr == NULL) return 0;

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

	return 1;
}

int
GetCMBTimes(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetCMBTimes");

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

int
UpdateCMBTimes(_account_info *ptr,int times)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateCMBTimes");

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) 
	{
        shmp->prepaid_info[ptr->head][ptr->tail].callMeBackRequestTimes += times;
		SendAccountRealSync_2(ptr,0.0,0.0,OMC_OCODE_EDIT);
		return 1;
    }
    return -1;
}

int
resetCMBTimes(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("resetCMBTimes");

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


void UpdateBalanceForRecharge(_account_info *ptr, int charge)
{
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateBalanceForRecharge");

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{		
		shmp->prepaid_info[ptr->head][ptr->tail].balance = 
				shmp->prepaid_info[ptr->head][ptr->tail].balance + charge;
		if(shmp->prepaid_info[ptr->head][ptr->tail].balance >= 0)
			shmp->prepaid_info[ptr->head][ptr->tail].overdraft = 0; 
		/* clear overdraft for each recharge */
	}	
}

void UpdateBalance(_state_data 	*stateProc,_account_info *ptr, float charge,CHARGED_FEE *chargeFee)
{
    _prepaid_data    	*shmp=&ramshm_ptr->prepaid_data;
	u_char				cos_id;
	COSTable			*cosTableItem;
	double 				promo_balance,balance;
	double				chargedBalance = 0,chargedPromotion = 0;
	
	PutLogFunID("UpdateBalance");


	cos_id = ptr->cos_id%MaxCosTableItem;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		if(charge < 0 			
			&& shmp->prepaid_info[ptr->head][ptr->tail].promo_balance > 0
			&& isPromotionCanBeUsedAtThisPoint(stateProc))
		{
			promo_balance = shmp->prepaid_info[ptr->head][ptr->tail].promo_balance;
			promo_balance = promo_balance + charge;
  		   	shmp->prepaid_info[ptr->head][ptr->tail].promo_balance = promo_balance; 
			chargedPromotion = charge;
		}		
		else
		{
			balance = shmp->prepaid_info[ptr->head][ptr->tail].balance;
		        balance = balance + charge; 
			shmp->prepaid_info[ptr->head][ptr->tail].balance = balance;
			chargedBalance = charge;
		}

		if(shmp->prepaid_info[ptr->head][ptr->tail].promo_balance<0)
		{
			chargedPromotion += shmp->prepaid_info[ptr->head][ptr->tail].promo_balance;
			chargedBalance -= shmp->prepaid_info[ptr->head][ptr->tail].promo_balance;

			shmp->prepaid_info[ptr->head][ptr->tail].balance +=
				shmp->prepaid_info[ptr->head][ptr->tail].promo_balance;
			shmp->prepaid_info[ptr->head][ptr->tail].promo_balance = 0;
		}

		if((shmp->prepaid_info[ptr->head][ptr->tail].balance +
		   shmp->prepaid_info[ptr->head][ptr->tail].overdraft)<=0)
		{
			if(cosTableItem->accountValidity[0].actionAfterBalanceDepleted)
			{
				if(shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date>
						(tvnow.tv_sec + cosTableItem->accountValidity[0].validDaysChangeTo*24*3600))
				{
					shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date =
						 tvnow.tv_sec + cosTableItem->accountValidity[0].validDaysChangeTo*24*3600;
				}
				if(shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date>
						(tvnow.tv_sec + cosTableItem->accountValidity[0].validDaysChangeTo*24*3600))
				{
					shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date =
						 tvnow.tv_sec + cosTableItem->accountValidity[0].validDaysChangeTo*24*3600;
				}
			}
		}
		chargeFee->balance = chargedBalance;
		chargeFee->promotion = chargedPromotion;
		increPoolChildCharged(ptr,-charge);
		debugmib.display.totalCharge +=charge;

		if(shmp->prepaid_info[ptr->head][ptr->tail].balance < 0)
		{
			if(shmp->prepaid_info[ptr->head][ptr->tail].overdraft <= 0)
			{
				shmp->prepaid_info[ptr->head][ptr->tail].balance  = 0;

			}
		}
	
		
	}

}

void UpdateAccountDefaultLanguageType(_account_info *ptr, u_char type)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountDefaultLanguageType");

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) 
	{
        shmp->prepaid_info[ptr->head][ptr->tail].language_type = type;
    }
}

void UpdateAccountPIN(_account_info *ptr, char *pin)
{
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountPIN");

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT) 
	{
        memcpy(shmp->prepaid_info[ptr->head][ptr->tail].pin,pin,6);
    }
}

int GetAccountPINByMsisdn(u_char *msisdn,u_char *pin)
{
	_account_info	info;
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	
	PutLogFunID("GetAccountPINByMsisdn");
	
	memcpy(info.number,msisdn,strlen((char *)msisdn));
	info.number[strlen((char *)msisdn)] = 0;
	if(GetAccountPos(&info) == FALSE)
		return -1;
	
	if (info.head < MSISDN_HEAD_NUM && info.tail < UNIT_STORE_ACCOUNT) 
	{
        memcpy(pin,shmp->prepaid_info[info.head][info.tail].pin,6);
		   
    }
	return 1;
}

int	CheckPINValidity(_account_info *ptr, char *pin)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("CheckPINValidity");

	if(ptr->head<MSISDN_HEAD_NUM && ptr->tail< UNIT_STORE_ACCOUNT)
	{
		if(!(strncmp(shmp->prepaid_info[ptr->head][ptr->tail].pin,
				pin,
				MAX_PIN_LEN)))
			return 1;
		else
			return 0;
	}
	return 0;
}
	
int GetCardSystemParam(u_char	cos_id,u_short faceValue)
{
	int	i;
	COSTable			*cosTableItem;
	
	PutLogFunID("GetCardSystemParam");
	
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	if(faceValue == 0) return -1;
	for(i=0;i<MaxRechargeCardType;i++)
	{
		if(cosTableItem->rechargeCardValidity[i].cardCredit == faceValue)
			return i;
	}
	return -1;
}

int GetCardSystemParamByNearestValue(u_char cos_id, u_short faceValue)
{
	int	i, ret = -1;
	COSTable *cosTableItem;
	u16 maxCredit = 0;

	PutLogFunID("GetCardSystemParamByNearestValue");

	if (faceValue == 0)
	{
		return -1;
	}

	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	for (i=0; i<MaxRechargeCardType; i++)
	{
		if (faceValue < cosTableItem->rechargeCardValidity[i].cardCredit)
		{
			continue;
		}

		if (maxCredit >= cosTableItem->rechargeCardValidity[i].cardCredit)
		{
			continue;
		}

		maxCredit = cosTableItem->rechargeCardValidity[i].cardCredit;
		ret = i;
	}

	return ret;
}

void UpdateAccountCallCDR(_account_info *ptr, 
		char *msisdn,u_long timestamp,int calltype)
{
	int	len;
	char buf[32];
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	
	PutLogFunID("UpdateAccountCallCDR");

	if(ptr->head<MSISDN_HEAD_NUM && ptr->tail<UNIT_STORE_ACCOUNT)
	{
		shmp->prepaid_info[ptr->head][ptr->tail].last_call_type = calltype;
		shmp->prepaid_info[ptr->head][ptr->tail].last_call_date = timestamp;
		len = strlen(msisdn);
		if(len % 2 == 1)
		{
			strcpy(buf,msisdn);
			strcat(buf,"F");
			ppsAsciiToBcdR(shmp->prepaid_info[ptr->head][ptr->tail].last_call_number,buf,len+1);
		}
		else
		{
			ppsAsciiToBcdR(shmp->prepaid_info[ptr->head][ptr->tail].last_call_number,msisdn,len);
		}
		shmp->prepaid_info[ptr->head][ptr->tail].last_call_number[MAX_MSISDN_BLEN-1] = len;
	}
}

void UpdateAccountLastCDR(_state_data *shmp)
{
	int	instance;

	PutLogFunID("UpdateAccountLastCallCDR");
	
	switch(shmp->context_info.callType)
	{
		case CT_MO_CALL:
		UpdateAccountCallCDR(&shmp->caller_info,
								(char *)shmp->charge_info.number,
								shmp->cdr_info.seizuretime,
								LST_MO_CALL);
		AccountLatchFlagCheck(shmp->caller_info.head,shmp->caller_info.tail);
		instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT 
					+ shmp->caller_info.tail;
		SendAccountRealSync(instance,0.0,0.0,OMC_OCODE_EDIT);
		break;
		case CT_MT_CALL:
		UpdateAccountCallCDR(&shmp->called_info,
								(char *)shmp->charge_info.number,
								shmp->cdr_info.seizuretime,
								LST_MT_CALL);
		AccountLatchFlagCheck(shmp->called_info.head,shmp->called_info.tail);
		instance = shmp->called_info.head * UNIT_STORE_ACCOUNT 
					+ shmp->called_info.tail;
		SendAccountRealSync(instance,0.0,0.0,OMC_OCODE_EDIT);
		break;
		case CT_MF_CALL:
		UpdateAccountCallCDR(&shmp->caller_info,
								(char *)shmp->charge_info.number,
								shmp->cdr_info.seizuretime,
								LST_MF_CALL);
		AccountLatchFlagCheck(shmp->caller_info.head,shmp->caller_info.tail);
		instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT 
					+ shmp->caller_info.tail;
		SendAccountRealSync(instance,0.0,0.0,OMC_OCODE_EDIT);
		break;
		case CT_MO_SM:
		UpdateAccountCallCDR(&shmp->caller_info,
								(char *)shmp->charge_info.number,
								shmp->cdr_info.seizuretime,
								LST_MO_SMS);
		AccountLatchFlagCheck(shmp->caller_info.head,shmp->caller_info.tail);
		instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT 
					+ shmp->caller_info.tail;
		SendAccountRealSync(instance,0.0,0.0,OMC_OCODE_EDIT);
		break;
	}
}

void
UpdateExpiration(_state_data *stashmp)
{
    _prepaid_data	*shmp=&ramshm_ptr->prepaid_data;
    long			expiration = 0,i_expiration = 0;
	u_short			valid_time, /* outgoing call */
					i_valid_time, /* incoming call */
					faceValue;
	int				index,cos_id;
	_account_info	*ptr;
	COSTable		*cosTableItem;
		
	PutLogFunID("UpdateExpiration");

	ptr = &stashmp->caller_info;

	if(ptr->head>=MSISDN_HEAD_NUM || ptr->tail>=UNIT_STORE_ACCOUNT)
	  return;
	cos_id = shmp->prepaid_info[ptr->head][ptr->tail].cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	
	valid_time = GetRechargeCardValidTime(stashmp);
	if(valid_time<=0) valid_time = 30;
	i_valid_time = valid_time;

	faceValue  = GetRechargeCardFaceValue(stashmp);
    expiration = shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date;
	i_expiration = shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date;

	if((index=GetCardSystemParam(cos_id,faceValue))>=0){
		valid_time = cosTableItem->rechargeCardValidity[index].moValidity;
		i_valid_time = cosTableItem->rechargeCardValidity[index].mtValidity;

	}

	if(cosTableItem->accountValidity[0].actionAfterRecharge)
	{
		if(expiration<(valid_time*24*3600+tvnow.tv_sec)) 
			shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date = 
					valid_time*24*60*60 + tvnow.tv_sec;
		if(i_expiration<(i_valid_time*24*3600+tvnow.tv_sec)) 
			shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date = 
					i_valid_time*24*60*60 + tvnow.tv_sec;
	}
	else
	{
		if(tvnow.tv_sec > expiration)
			shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date = 
                 valid_time*24*60*60 + tvnow.tv_sec;
		else{
			shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date += 
                 valid_time*24*60*60;
		}
		if(tvnow.tv_sec > i_expiration)
			shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date = 
                 i_valid_time*24*60*60 + tvnow.tv_sec;
		else{
			shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date += 
                 i_valid_time*24*60*60;
		}
	}
	if(cosTableItem->accountValidity[0].maxAccountValidityDays<=0) 
		cosTableItem->accountValidity[0].maxAccountValidityDays = 730;
    if(shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date> tvnow.tv_sec
					+ cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600)
		shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date =
						tvnow.tv_sec + cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600;
    if(shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date> tvnow.tv_sec
					+cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600)
		shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date =
						tvnow.tv_sec + cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600;

}

void UpdateExpirationForRechargeCard(_account_info *ptr, u_short valid_time, u_short faceValue)
{
	_prepaid_data	*shmp=&ramshm_ptr->prepaid_data;
	long			expiration = 0,i_expiration = 0;
	u_short		i_valid_time; /* incoming call */
	int			index, cos_id;
	COSTable		*cosTableItem;
		
	PutLogFunID("UpdateExpirationForRechargeCard");

	if(ptr->head>=MSISDN_HEAD_NUM || ptr->tail>=UNIT_STORE_ACCOUNT)
	{
		return;
	}

	if(OCS_with_external_CRM())
	{
		shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date = 
			 valid_time*24*60*60 + tvnow.tv_sec;
		shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date = 
			 valid_time*24*60*60 + tvnow.tv_sec;
		return;
	}

	cos_id = shmp->prepaid_info[ptr->head][ptr->tail].cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];

	if(valid_time<=0) valid_time = 30;
	i_valid_time = valid_time;

	expiration = shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date;
	i_expiration = shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date;

	if((index=GetCardSystemParamByNearestValue(cos_id, faceValue))>=0){
		valid_time = cosTableItem->rechargeCardValidity[index].moValidity;
		i_valid_time = cosTableItem->rechargeCardValidity[index].mtValidity;
	}

	if(cosTableItem->accountValidity[0].actionAfterRecharge)
	{
		if(expiration<(valid_time*24*3600+tvnow.tv_sec)) 
			shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date = 
					valid_time*24*60*60 + tvnow.tv_sec;
		if(i_expiration<(i_valid_time*24*3600+tvnow.tv_sec)) 
			shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date = 
					i_valid_time*24*60*60 + tvnow.tv_sec;
	}
	else
	{
		if(tvnow.tv_sec > expiration)
			shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date = 
                 valid_time*24*60*60 + tvnow.tv_sec;
		else{
			shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date += 
                 valid_time*24*60*60;
		}
		if(tvnow.tv_sec > i_expiration)
			shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date = 
                 i_valid_time*24*60*60 + tvnow.tv_sec;
		else{
			shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date += 
                 i_valid_time*24*60*60;
		}
	}

	if(cosTableItem->accountValidity[0].maxAccountValidityDays<=0)
	{
		cosTableItem->accountValidity[0].maxAccountValidityDays = 730;
	}
	
	if(shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date> tvnow.tv_sec
		+ cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600)
	{
		shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date =
				tvnow.tv_sec + cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600;
	}
	
	if(shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date> tvnow.tv_sec
		+cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600)
	{
		shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date =
			tvnow.tv_sec + cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600;
	}
}

void
UpdateExpirationFirstCharge(_state_data *stashmp)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	u_short			valid_time, /* outgoing call */
					i_valid_time; /* incoming call */
	int				index,cos_id;
	u_short			faceValue;
	_account_info	*ptr;
	COSTable		*cosTableItem;
	
	PutLogFunID("UpdateExpirationFirstCharge");

	ptr = &stashmp->caller_info;
	if(ptr->head>=MSISDN_HEAD_NUM) {
		return;
	}

	cos_id = shmp->prepaid_info[ptr->head][ptr->tail].cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	valid_time = GetRechargeCardValidTime(stashmp);
	i_valid_time = valid_time;
	faceValue  = GetRechargeCardFaceValue(stashmp);

	if((index=GetCardSystemParam(cos_id,faceValue))>=0){
		valid_time = cosTableItem->rechargeCardValidity[index].moValidity;
		i_valid_time = cosTableItem->rechargeCardValidity[index].mtValidity;
	}

	shmp->prepaid_info[ptr->head][ptr->tail].mo_expiration_date = 
             valid_time*24*60*60 + tvnow.tv_sec ; 
	shmp->prepaid_info[ptr->head][ptr->tail].mt_expiration_date = 
             i_valid_time*24*60*60 + tvnow.tv_sec ; 
}

void
UpdateExpirationFirstInquiry(_account_info *ptr, u_short valid_time)
{
    	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	_prepaid_info	    *user_ptr = NULL;
	COSTable		 *cosTableItem;
	int 	cos_id= 0, bundle_plan_id=0;

	PutLogFunID("UpdateExpirationFirstInquiry");

	if(ptr->head>=MSISDN_HEAD_NUM || ptr->tail>=UNIT_STORE_ACCOUNT) 
		return;

	cos_id = ptr->cos_id%MaxCosTableItem;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];

	user_ptr = &shmp->prepaid_info[ptr->head][ptr->tail];
	user_ptr->mo_expiration_date = valid_time*24*60*60 + tvnow.tv_sec ; 			
	user_ptr->mt_expiration_date = valid_time*24*60*60 + tvnow.tv_sec ; 

	bundle_plan_id = cosTableItem->accountValidity[0].bundlePlanIDWhenActivated;
	if( bundle_plan_id > 0 && bundle_plan_id < MAX_BUNDLE_PLANS)
	{
		user_ptr->bundle_info.validity_date = user_ptr->mo_expiration_date;
		user_ptr->bundle_info.data = ppsParam.ppsTables.bundlePlans[bundle_plan_id].data * 1024 * 1024LL;
		user_ptr->bundle_info.mo_voice = ppsParam.ppsTables.bundlePlans[bundle_plan_id].mo_voice * 60;
		user_ptr->bundle_info.mt_voice = ppsParam.ppsTables.bundlePlans[bundle_plan_id].mt_voice * 60;
		user_ptr->bundle_info.sms = ppsParam.ppsTables.bundlePlans[bundle_plan_id].sms;
	}
	 
}

void
UpdateAccountExpiration(int head, int tail, u_short faceValue,u_short valid_time)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	u_long			 expiration=0,i_expiration=0;
	short			 index, i_valid_time;
	int				 cos_id;
	COSTable		 *cosTableItem;
	
	PutLogFunID("UpdateAccountExpiration");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
    		expiration = shmp->prepaid_info[head][tail].mo_expiration_date;
		i_expiration = shmp->prepaid_info[head][tail].mt_expiration_date;
		i_valid_time = valid_time;
		cos_id = shmp->prepaid_info[head][tail].cos_id;
		cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
		if((index=GetCardSystemParam(cos_id,faceValue))>=0){
			valid_time = cosTableItem->rechargeCardValidity[index].moValidity;
			i_valid_time = cosTableItem->rechargeCardValidity[index].mtValidity;
		}

		if(cosTableItem->accountValidity[0].actionAfterRecharge)
		{
			if(expiration<(valid_time*24*3600+tvnow.tv_sec)) 
				shmp->prepaid_info[head][tail].mo_expiration_date = 
						valid_time*24*60*60 + tvnow.tv_sec;
			if(i_expiration<(i_valid_time*24*3600+tvnow.tv_sec)) 
				shmp->prepaid_info[head][tail].mt_expiration_date = 
						i_valid_time*24*60*60 + tvnow.tv_sec;
		}
		else
		{
			if(tvnow.tv_sec > expiration)
				shmp->prepaid_info[head][tail].mo_expiration_date = 
	                 valid_time*24*60*60 + tvnow.tv_sec;
			else{
				shmp->prepaid_info[head][tail].mo_expiration_date += 
	                 valid_time*24*60*60;
			}
			if(tvnow.tv_sec > i_expiration)
				shmp->prepaid_info[head][tail].mt_expiration_date = 
	                 i_valid_time*24*60*60 + tvnow.tv_sec;
			else{
				shmp->prepaid_info[head][tail].mt_expiration_date += 
	                 i_valid_time*24*60*60;
			}
		}

		if(cosTableItem->accountValidity[0].maxAccountValidityDays<=0)
		{
			cosTableItem->accountValidity[0].maxAccountValidityDays = 730;
		}
		
		if(shmp->prepaid_info[head][tail].mo_expiration_date> tvnow.tv_sec
			+ cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600)
		{
			shmp->prepaid_info[head][tail].mo_expiration_date =
					tvnow.tv_sec + cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600;
		}
		
		if(shmp->prepaid_info[head][tail].mt_expiration_date> tvnow.tv_sec
			+cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600)
		{
			shmp->prepaid_info[head][tail].mt_expiration_date =
				tvnow.tv_sec + cosTableItem->accountValidity[0].maxAccountValidityDays*24*3600;
		}
	}
}

int
UpdateAccountStatusFromNormalToSuspendedforExpiration(_prepaid_info *pUsrInfo, int head, int tail)
{

	if(pUsrInfo == NULL)
		return 0;
	if(pUsrInfo->mo_expiration_date < tvnow.tv_sec)
	{
		if(pUsrInfo->status == T_NORMAL)
		{
			pUsrInfo->status = T_SUSPEND;
			SendAccountRealSync(head*UNIT_STORE_ACCOUNT+tail, 0.00, 0.00, OMC_OCODE_EDIT );
		}
	}

	return 0;
}


int
GetAccountStatus(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetAccountStatus");

   if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
   {
		if(shmp->prepaid_info[ptr->head][ptr->tail].balance>=ppsParam.serviceControl.maxBalance)
				shmp->prepaid_info[ptr->head][ptr->tail].status = T_SUSPEND;
		if(shmp->prepaid_info[ptr->head][ptr->tail].msisdn[MAX_MSISDN_BLEN-1]<=2)
		{
			shmp->prepaid_info[ptr->head][ptr->tail].status = T_FRESH;
			return T_EXTERNAL;
		}
		return shmp->prepaid_info[ptr->head][ptr->tail].status;
    }
    return T_EXTERNAL;
}

int GetAccountCosID(_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetAccountCosID");

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

int GetSerivceNumberFlagPerCos(u8 cos_id, u8 service_type, u8 call_type);
int GetServiceNumberFlag(_state_data *shmp, u8 service_pos)
{
    int match_flag = 0;

    if(shmp->context_info.callType == CT_MO_CALL)
    {
        match_flag = GetSerivceNumberFlagPerCos(shmp->caller_info.cos_id, service_pos, 0);	
    }
    else if(shmp->context_info.callType == CT_MO_SM )
    {
        match_flag = GetSerivceNumberFlagPerCos(shmp->caller_info.cos_id, service_pos, 1);			
    }
    else if(shmp->context_info.callType == CT_USSD)
        match_flag = GetSerivceNumberFlagPerCos(shmp->caller_info.cos_id, service_pos, 2);
	
    return match_flag;		
}


int GetServiceType_0(_state_data *shmp)
{
	int len0,len1;
	char buf[32];
	char destination[32];
	
	strcpy(destination, (char *)shmp->called_info.number);
	
	if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[HotLineNumberPosition].number)==0)
	{
		return SRV_CALL_HOTLINE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[InquiryNumberPosition].number)==0)
	{
		return SRV_INQUIRY;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number)==0)
	{
		return SRV_SCRATCH_CARD_RECHARGE;
		//return SRV_RECHARGE_2TYPES;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[CreditCardRechargeNumberPosition].number)==0)
	{
		return SRV_BUNDLE_PLAN; //SRV_CREDIT_CARD_RECHARGE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[ThirdPartyRechargeNumberPosition].number)==0)
	{
		return SRV_THIRD_PARTY_RECHARGE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[RechargeNumberPosition].number)==0)
	{
		return SRV_RECHARGE_2TYPES;
		//return SRV_SCRATCH_CARD_RECHARGE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[PalimRegisterNumberPosition].number) == 0)
	{
		shmp->process_info.palim_auto_register = 1;
		return SRV_SCRATCH_CARD_RECHARGE;
	}		
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[PoolAccountPosition].number)==0)
	{
		return SRV_POOL_ACCOUNT;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[FeeTransferPosition].number)==0)
	{
		return SRV_FEE_TRANSFER;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[FreeServicePosition].number)==0)
	{
		return SRV_FREE_SMS;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[CallMeBackPosition].number)==0)
	{
		return SRV_CALL_ME_BACK;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[TellMeNumber].number)==0)
	{
	      return SRV_TELL_ME_NUMBER;
	}
	/*
	else if(IsCRDServiceNumber(destination))
	{
		return SRV_CRD;
	}
	else if(IsPSTNMaitenance(destination))
	{
		return SRV_OTHER_VMS_MAITENANCE;
	}
	else if(IsVMSDeposit(destination))
	{
		return SRV_VMS_DEPOSIT;
	}
	*/
	else if(IsCallingCardNumber(destination))
	{/*calling card		*/
		return SRV_CALLINGCARD_MANAGE;
	}
	/*
	else if(IsDirectAccess(destination))
	{
		return SRV_DIRECT_ACCESS;
	}
	else if(IsVMSReservedFunc(destination))
	{
		return SRV_RESERVED_FUN;
	}*/
	else
	{
		len0 = strlen((char *)ppsParam.ppsTables.serviceNumber[CallMeBackPosition].number);
		if(len0 !=0)
		{
			len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination,
					(char *)ppsParam.ppsTables.serviceNumber[CallMeBackPosition].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number, destination);
				shmp->process_info.all_in_one_input_flag = 1;			
				return SRV_CALL_ME_BACK;
			}
		}
		
		len0 = strlen((char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number);
		if(len0 !=0)
		{
	    		len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination, (char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number, len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number, destination);
				return SRV_RECHARGE_ONE_INPUT;
			}
		}

		len0 = strlen((char *)ppsParam.ppsTables.serviceNumber[PalimRegisterNumberPosition].number);
		if(len0 !=0)
		{
	    		len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination,
					(char *)ppsParam.ppsTables.serviceNumber[PalimRegisterNumberPosition].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number,destination);
				shmp->process_info.palim_auto_register = 1;			
				return SRV_RECHARGE_ONE_INPUT;
			}
		}
		return SRV_NORMAL_MO_CALL;
	}
    return SRV_NORMAL_MO_CALL;
}

int GetServiceType(_state_data *shmp)
{
	int len0,len1;
	char buf[32];
	char destination[32];
	
	strcpy(destination, (char *)shmp->called_info.number);
	shmp->process_info.tcos= NORMAL_RELEASE;
	
	if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[HotLineNumberPosition].number)==0)
	{
		if(GetServiceNumberFlag(shmp, HotLineNumberPosition))
		    return SRV_CALL_HOTLINE;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
		   
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[InquiryNumberPosition].number)==0)
	{
		if(GetServiceNumberFlag(shmp, InquiryNumberPosition))
		    return SRV_INQUIRY;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, ScratchCardRechargeNumberPosition))
		    return SRV_SCRATCH_CARD_RECHARGE;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
		//return SRV_RECHARGE_2TYPES;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[CreditCardRechargeNumberPosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, CreditCardRechargeNumberPosition))
		    return SRV_CREDIT_CARD_RECHARGE;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[ThirdPartyRechargeNumberPosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, ThirdPartyRechargeNumberPosition))
		    return SRV_THIRD_PARTY_RECHARGE;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[RechargeNumberPosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, RechargeNumberPosition))
		    return SRV_RECHARGE_2TYPES;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
		//return SRV_SCRATCH_CARD_RECHARGE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[PalimRegisterNumberPosition].number) == 0)
	{
	       if(GetServiceNumberFlag(shmp, PalimRegisterNumberPosition))
	       {
		    shmp->process_info.palim_auto_register = 1;
		    return SRV_SCRATCH_CARD_RECHARGE;
	       }
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}		
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[PoolAccountPosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, PoolAccountPosition))
		    return SRV_POOL_ACCOUNT;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[FeeTransferPosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, FeeTransferPosition))
		    return SRV_FEE_TRANSFER;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[FreeServicePosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, FreeServicePosition))
		    return SRV_FREE_SMS;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[CallMeBackPosition].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, CallMeBackPosition))
		    return SRV_CALL_ME_BACK;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.serviceNumber[TellMeNumber].number)==0)
	{
	      if(GetServiceNumberFlag(shmp, TellMeNumber))  
	          return SRV_TELL_ME_NUMBER;
	      else
	      {
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
	      }
	}/*
	else if(IsCRDServiceNumber(destination))
	{
		return SRV_CRD;
	}
	else if(IsPSTNMaitenance(destination))
	{
		return SRV_OTHER_VMS_MAITENANCE;
	}
	else if(IsVMSDeposit(destination))
	{
		return SRV_VMS_DEPOSIT;
	}
	else if(IsOprNumber(destination))
	{
	       if(GetServiceNumberFlag(shmp, OPRNumberPosition))
		    return SRV_OPR_MANAGE;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}*/
	else if(IsCallingCardNumber(destination))
	{/*calling card		*/
	       if(GetServiceNumberFlag(shmp, VirtualCallingCardNumberPosition))
		    return SRV_CALLINGCARD_MANAGE;
		else
		{
		    shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		    shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		}
	}
	/*
	else if(IsDirectAccess(destination))
	{
		return SRV_DIRECT_ACCESS;
	}
	else if(IsVMSReservedFunc(destination))
	{
		return SRV_RESERVED_FUN;
	}*/
	else
	{
		len0 = strlen((char *)ppsParam.ppsTables.serviceNumber[CallMeBackPosition].number);
		if(len0 !=0)
		{
			len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination,
					(char *)ppsParam.ppsTables.serviceNumber[CallMeBackPosition].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number,destination);
				if(GetServiceNumberFlag(shmp, CallMeBackPosition))
				{
				    shmp->process_info.all_in_one_input_flag = 1;			
				    return SRV_CALL_ME_BACK;
				}
				else
		              {
		                  shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		                  shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		              }
			}
		}
		
		len0 = strlen((char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number);
		if(len0 !=0)
		{
	    		len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination, (char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number, destination);
				if(GetServiceNumberFlag(shmp, ScratchCardRechargeNumberPosition))
				{
				    return SRV_RECHARGE_ONE_INPUT;
				}
				else
		             {
		                 shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		                 shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		             }
			}
		}

		len0 = strlen((char *)ppsParam.ppsTables.serviceNumber[PalimRegisterNumberPosition].number);
		if(len0 !=0)
		{
	    		len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination,
					(char *)ppsParam.ppsTables.serviceNumber[PalimRegisterNumberPosition].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number,destination);
				if(GetServiceNumberFlag(shmp, PalimRegisterNumberPosition))
				{
				    shmp->process_info.palim_auto_register = 1;			
				    return SRV_RECHARGE_ONE_INPUT;
				}
				else
		              {
		                 shmp->process_info.tcos= SRVICE_NOT_SUBSCRIBERED;
		                 shmp->process_info.voiceID = SERIVCE_NOT_APPLIED;
		              }
			}
		}
		return SRV_NORMAL_MO_CALL;
	}
    return SRV_NORMAL_MO_CALL;
}

int GetUssdServiceType(_state_data *shmp)
{
	int len0,len1;
	char buf[128];
	char destination[128];
	
	strcpy(destination, (char *)shmp->called_info.number);
	
	if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_HOTLINE].number)==0)
	{
		return SRV_CALL_HOTLINE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_INQUIRY].number)==0)
	{
		return SRV_INQUIRY;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_RECHARGE].number)==0)
	{
		return SRV_SCRATCH_CARD_RECHARGE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_FEE_TRANSFER].number)==0)
	{
		return SRV_FEE_TRANSFER;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_CALL_ME_BACK].number)==0)
	{
		return SRV_CALL_ME_BACK;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_TELL_ME_NUMBER].number)==0)
	{
	      return SRV_TELL_ME_NUMBER;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_FRIEND_FAMILY_QUERY].number)==0)
	{
	      return SRV_FAVORITE_NUMBER;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_BUNDLE_PLAN].number)==0)
	{
	      return SRV_BUNDLE_PLAN;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_BUNDLE_QUERY].number)==0)
	{
	      return SRV_BUNDLE_QUERY;
	}
	else
	{
		len0 = strlen((char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_CALL_ME_BACK].number);
		if(len0 !=0)
		{
			len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination,
					(char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_CALL_ME_BACK].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number,destination);
				shmp->process_info.all_in_one_input_flag = 1;			
				return SRV_CALL_ME_BACK;
			}
		}
		
		len0 = strlen((char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_RECHARGE].number);
		if(len0 !=0)
		{
	    		len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_RECHARGE].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number, destination);
				return SRV_RECHARGE_ONE_INPUT;
			}
		}
	}
    return SRV_NORMAL_MO_CALL;
}

int GetUssdServiceType_1(_state_data *shmp)
{
	int len0,len1;
	char buf[32];
	char destination[32];
	
	strcpy(destination, (char *)shmp->called_info.number);
	
	if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_HOTLINE].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, HotLineNumberPosition))
		    return SRV_CALL_HOTLINE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_INQUIRY].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, InquiryNumberPosition))
		    return SRV_INQUIRY;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_RECHARGE].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, ScratchCardRechargeNumberPosition))
		    return SRV_SCRATCH_CARD_RECHARGE;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_FEE_TRANSFER].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, FeeTransferPosition))
		    return SRV_FEE_TRANSFER;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_CALL_ME_BACK].number)==0)
	{
	       if(GetServiceNumberFlag(shmp, CallMeBackPosition))
		    return SRV_CALL_ME_BACK;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_TELL_ME_NUMBER].number)==0)
	{
	      if(GetServiceNumberFlag(shmp, TellMeNumber))
	         return SRV_TELL_ME_NUMBER;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_FRIEND_FAMILY_QUERY].number)==0)
	{
	      if(GetServiceNumberFlag(shmp, FriendFamilyPosition))
	          return SRV_FAVORITE_NUMBER;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_BUNDLE_PLAN].number)==0)
	{
	      return SRV_BUNDLE_PLAN;
	}
	else if(strcmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_BUNDLE_QUERY].number)==0)
	{
	      return SRV_BUNDLE_QUERY;
	}	
	else
	{
		len0 = strlen((char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_CALL_ME_BACK].number);
		if(len0 !=0)
		{
			len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination,
					(char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_CALL_ME_BACK].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number,destination);
				if(GetServiceNumberFlag(shmp, CallMeBackPosition))
				{
				    shmp->process_info.all_in_one_input_flag = 1;			
				    return SRV_CALL_ME_BACK;
				}
			}
		}
		
		len0 = strlen((char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_RECHARGE].number);
		if(len0 !=0)
		{
	    		len1 = strlen(destination);
			if(len1>= 6 +len0)
			if(strncmp(destination, (char *)ppsParam.ppsTables.ussdServiceNumber[USSD_OC_RECHARGE].number,len0)==0)
			{
				strcpy(buf,&destination[len0]);
				strcpy(destination,buf);
				destination[len1-len0] = 0;
				strcpy((char *)shmp->called_info.number, destination);
				if(GetServiceNumberFlag(shmp, ScratchCardRechargeNumberPosition))
				    return SRV_RECHARGE_ONE_INPUT;
			}
		}
	}
    return SRV_NORMAL_MO_CALL;
}

int GetUssdServiceKey(u_char serviceKey)
{
	int i;

	PutLogFunID("GetUssdServiceKey");

	for(i=USSD_OC_HOTLINE;i<USSD_OC_MAX;i++)
		if(ppsParam.ppsTables.ussdServiceKey[i].key == serviceKey)
			return i;
	return -1;
}

int CheckWetherFavoriteNumber(_account_info *caller,  char *called_number)
{
	int  i,len;
	char msisdn[32];
	int cos_id;
	CosFriendFamilyControl	 	*serviceControlItem;

       _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("CheckWetherFavoriteNumber");

	if(caller->head < MSISDN_HEAD_NUM && caller->tail <UNIT_STORE_ACCOUNT)
	{
		if(!shmp->prepaid_info[caller->head][caller->tail].favorite_numbers)
			return NOT_FAVORITE_NUMBER;

              cos_id = shmp->prepaid_info[caller->head][caller->tail].cos_id;
              serviceControlItem = &ppsParam.ppsTables.cosTable[cos_id].friendFamilyControl[0];
              if(serviceControlItem->balanceThresholdForFriendFamilyCall>GetAccountBalance(caller->head, caller->tail))
              	return NOT_FAVORITE_NUMBER;
              //if F*F PLAN, check total F&F call duraiton here
              
		for(i=0;i<MAX_FAVORITE_NUMBERS;i++)
		{
			len = shmp->prepaid_info[caller->head][caller->tail].favorite_number[i][MAX_MSISDN_BLEN-1];
			if(len>MAX_MSISDN_ALEN || len<2) 
				continue;
			BcdToAscii(msisdn,
						shmp->prepaid_info[caller->head][caller->tail].favorite_number[i],
						MAX_MSISDN_ALEN);
			msisdn[len] = 0;
			CutUnknownPrefix(msisdn);
			//CheckWetherLocalNumber(msisdn);
			if(strcmp(msisdn,called_number)==0) 
				return FAVORITE_NUMBER;
		}
	}
	return NOT_FAVORITE_NUMBER;
}

int
GetAccountID(_account_info *ptr)
{
    	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	_prepaid_info *user_info=NULL;

	PutLogFunID("GetAccountID");

	if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		user_info = &shmp->prepaid_info[ptr->head][ptr->tail];
		
		if(user_info->msisdn[MAX_MSISDN_BLEN-1]>=MIN_MSISDN_ALEN && user_info->msisdn[MAX_MSISDN_BLEN-1]<=MAX_MSISDN_ALEN)
		{
			return user_info->crm_info.account_id;
		}
		else
			return 0;
	}
	
	return 0;
}

int
GetAccountCUG(_account_info *ptr)
{
    	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	_prepaid_info *user_info=NULL;

	PutLogFunID("GetAccountCUG");

	if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		user_info = &shmp->prepaid_info[ptr->head][ptr->tail];
		
		if(user_info->msisdn[MAX_MSISDN_BLEN-1]>=MIN_MSISDN_ALEN && user_info->msisdn[MAX_MSISDN_BLEN-1]<=MAX_MSISDN_ALEN)
		{
			if(user_info->crm_info.vas_cug_state != 0)//cug service is blocked for rent charge failed
				return NOT_CUG_ACCOUNT;

			return user_info->cug_id;
		}
		else
			return NOT_CUG_ACCOUNT;
	}
	
	return NOT_CUG_ACCOUNT;
}




int GetDiscount(long timestamp,u_char discount_id)
{
    int                holiday_flag;
    _tariff_private    *shmp;
    struct tm          tm, *timep;
    int                discount=0;
    int                wday=0;

	PutLogFunID("GetDiscount");
	
    shmp = blf_shmp;
    timep = (struct tm *) &tm;
    timep = (struct tm *) localtime( (time_t *)&timestamp );
    if(timep == NULL)
    {
	DebugMsg(debugmib.display.asciiOut, "Get Discount time error: %d", timestamp);
    
	return 100;
    }
	
    holiday_flag = shmp->holiday_flag[timep->tm_mon][timep->tm_mday-1];// advanced->Tariff plan->Holiday
    wday = (holiday_flag ?  7 : timep->tm_wday);

#if DEBUG1
    DebugMsg(debugmib.display.asciiOut, "Tm month: %d Tm mday: %d Holiday flag: %d",
                    timep->tm_mon, timep->tm_mday, holiday_flag);
#endif
    discount = shmp->bill_discount[discount_id][timep->tm_hour][wday];// advanced->Tariff plan->Discount
#if DEBUG1
    DebugMsg(debugmib.display.asciiOut, "Type: %d Hour: %d Week day: %d Discount: %d",
                    id, timep->tm_hour, wday, discount);
#endif
	if(discount < 0)
		discount = 0;
    return discount;
}


void set_truncation_flag(int flag)
{
	ppsParam.serviceControl.truncation_flag = flag;
}

int get_truncation_flag()
{
	return ppsParam.serviceControl.truncation_flag;
}


int SetTcpSyncFlag(int flag)
{
	DebugMsg(debugmib.display.asciiOut,"Current tcp sync flag: %d", ppsParam.serviceControl.tcp_sync_flag);

	if(flag)
		ppsParam.serviceControl.tcp_sync_flag = 1;
	else
		ppsParam.serviceControl.tcp_sync_flag = 0;

	DebugMsg(debugmib.display.asciiOut,"New tcp sync flag: %d", ppsParam.serviceControl.tcp_sync_flag);
	return 1;
}

float ChargeGprsAccountBalance(_state_data	*shmp,int real_duration)
{
    int					i=0,j;
    int					discount=0;
	float				charge=0;
    int					itimes=0, unit_time=60;
	int					duration, begin_time;
	_bill_tariff 		*ptar;
	
	PutLogFunID("ChargeAccountBalance");

	begin_time = shmp->cdr_info.answertime,
	ptar = &shmp->process_info.tariff;

	duration = real_duration;
	duration -= ptar->free_time;
	if(duration<=0) return (charge = 0);
    if ((unit_time = ptar->unit_time) <= 0) 
        unit_time = 60;

    itimes = (ceil)((double) duration / unit_time);
	
    discount = GetDiscount(begin_time,ptar->discountID);

	for (i=1; i<=itimes; i++) 
	{
        if (i <= ptar->mini_unit )
		{
			for(j=0;j < ptar->mini_unit;j++)
			{
            	charge += BillingFormula(shmp,ptar,begin_time);
				begin_time += unit_time;
			}
			charge = (charge + ptar->adjustment);
            i = ptar->mini_unit;
        }
		else
		{
            charge  = charge + BillingFormula(shmp,ptar,begin_time);
			begin_time += unit_time;
        }
    }   
	
	charge = (charge * discount)/100;    

	return charge;
}

int GetValidTime(_state_data *shmp,int begin_time, _bill_tariff *ptar, 
        float balance, int init_bill, int overdraft)
{
	int             valid_time = 0;
	float			charge=0, amount=0;
	int				cos_id;
	COSTable		*cosTableItem;
	
	PutLogFunID("GetValidTime");

	cos_id = shmp->called_info.cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	
	if (init_bill == 0)
		valid_time = ptar->unit_time * ptar->mini_unit;
	else
		valid_time = ptar->unit_time;
	if (valid_time <= 0) 
		valid_time = 60;

	charge = TimeToCharge(shmp,ptar, valid_time, begin_time,init_bill);
	amount = balance - charge + overdraft;

#if DEBUG1
    DebugMsg(debugmib.display.asciiOut,
    "GetValid time: balance: %.0f charge: %.0f draft: %d amount: %.0f",
                    balance, charge, overdraft, amount);
#endif
/*How to get left seconds of account??? 
	2003-10-07
	*/
	
	if(charge==0)
	{				
		/* mo call */		
		if(shmp->context_info.callType != CT_MT_CALL)
		{
			//if(amount<=0) /* zero balance can't make free mo call */
			   if(amount<0)
				valid_time = 0;
		}
		else
		{
			
			if(cosTableItem->serviceControl[0].mtChargeFlag !=0 )
			{ // mt call, charged ,no balance 
				if(cosTableItem->serviceControl[0].mtBalanceControl == 1)
				if(balance+overdraft<cosTableItem->serviceControl[0].mtBalanceThreshold)
					valid_time = 0;
			}
			else
			{// mt is free 
				if(cosTableItem->serviceControl[0].mtChargeFlag == 1)
				{	// mt balance threshold used 
					if(balance+overdraft<cosTableItem->serviceControl[0].mtBalanceThreshold)
						valid_time = 0;
				}
			}
		}
	}
	else
	{
		if(amount<0) 
			valid_time = 0;

		if(shmp->context_info.callType == CT_MT_CALL)	
		if(cosTableItem->serviceControl[0].mtChargeFlag == 1)
		{	
			if(balance+overdraft<cosTableItem->serviceControl[0].mtBalanceThreshold)
				valid_time = 0;
		}
	}
	/*
    if (amount < 0 && charge >= 0)
        valid_time = 0;
    */    
    return (valid_time*CYCLE_STATE_INTERVAL);
}

int GetMaxCallDuration(_state_data *shmp)
{
	int			/*init_flag=0 , */discount,	maxDuration=0,free_time=0;
	float			balance;
	_bill_tariff	*ptar, tariff;
	//int			j;
	//float			charge;
	u32			calTime;
	int			init_charge_cycle = 0;

	PutLogFunID("GetMaxCallDuration");

	if(shmp->process_info.serviceType == SRV_CRD
	   ||
	   (shmp->process_info.serviceType == SRV_NORMAL_MO_CALL
	      &&
	    shmp->process_info.callConnected == 1
	      &&
	    !ppsParam.serviceControl.vms_allways_go))
	{
	    maxDuration = 12 * ONE_MINUTE_STATE_TIME ;
	    shmp->process_info.maxCallDuration = maxDuration;
	    return maxDuration;
	}

	ptar = &tariff;
	
	memcpy(ptar, &shmp->process_info.tariff, sizeof(_bill_tariff));
	if(ptar->unit_time<=0) 
		ptar->unit_time = 60;
	if(0)
       DebugMsg(debugmib.display.asciiOut, " %s:%d , tariff: %.2f+%.2f vs %.2f+%.2f",
	   	__FUNCTION__, __LINE__,
	   	shmp->process_info.tariff.tierRating.rating[0].base_fee,
	   	shmp->process_info.tariff.tierRating.rating[0].long_distance_fee,
	   	ptar->tierRating.rating[0].base_fee,
	   	ptar->tierRating.rating[0].long_distance_fee);

	if (shmp->process_info.ro_if.gy_ro_enable)
	{
		init_charge_cycle = ptar->free_time;// Ro: shmp->process_info.duration is in second.
	}
	else
	{
		init_charge_cycle = ptar->free_time*CYCLE_STATE_INTERVAL;
	}

	if(shmp->process_info.duration < init_charge_cycle)
	{
		free_time = ptar->free_time;
		maxDuration = free_time;
		//init_flag = 1;
	}
	else 
	{
		/*
		init_charge_cycle = (ptar->free_time+ptar->mini_unit*ptar->unit_time)*CYCLE_STATE_INTERVAL;
	
		if(shmp->process_info.duration < init_charge_cycle)
		{
			init_flag = 1;
		}
		*/
	}

	discount = GetDiscount(tvnow.tv_sec, ptar->discountID);
	if(discount <= 0)// free call
	{
		maxDuration = MAX_CALL_DURATION;		
	}
	else
	{
		if(shmp->context_info.callType == CT_MT_CALL)
			balance = (int)GetBalanceWithPromotionControl(shmp,&shmp->called_info) + GetOverdraft(&shmp->called_info);
		else
			balance = (int)GetBalanceWithPromotionControl(shmp,&shmp->caller_info) + GetOverdraft(&shmp->caller_info);
		
		if(balance<0)
			return 0;

		calTime = preLeftTime(shmp,balance,ptar);
		
		maxDuration += calTime; // free_time + pre left
	}
	
	shmp->process_info.maxCallDuration = maxDuration;

	return maxDuration;
}

void
EncryptCardHRN(char *card_hrn,u_char *card_pwd)
{
	char	pinkey[32],tmp_hrn[32];
	u_char	u_buf[32];
	int	len;

	//PutLogFunID("EncryptCardHRN");

	len = strlen(card_hrn);
	/*
 	if(len == 10) // add for PNCC 
 	{
		StringCat(card_hrn,"00",12);
		memcpy(pinkey, card_hrn, 6);
		pinkey[6] = 0;
		StringCat(pinkey, "0", 8);
		EncryptPIN(card_pwd, card_hrn, PAN_KEY, pinkey);
	}
	else if(len == 11) // for Solomon, 2006-11-15 
	{
		StringCat(card_hrn,"0",12);
		memcpy(pinkey, card_hrn, 6);
		pinkey[6] = 0;
		StringCat(pinkey, "0", 8);
		EncryptPIN(card_pwd, card_hrn, PAN_KEY, pinkey);
	}
	*/
	if(len<10 || len>16) return;
	if(len<12)
	{
		strcpy(tmp_hrn,card_hrn);
		StringCat(tmp_hrn,"0",16);
		AsciiToBcd(u_buf,tmp_hrn,16);
		des(card_pwd,u_buf, (unsigned char *)PAN_KEY,0);
	}
	else if(len==12)
	{
		memcpy(pinkey, card_hrn, 6);
		pinkey[6] = 0;
		StringCat(pinkey, "0", 8);
		EncryptPIN(card_pwd, card_hrn, PAN_KEY, (unsigned char *)pinkey);
	}
	else if(len == 14)
	{
		memcpy(pinkey, card_hrn, 7);
		pinkey[7] = 0;
		StringCat(pinkey, "0", 8);
		EncryptPIN(card_pwd, card_hrn, PAN_KEY, (unsigned char *)pinkey);
	}
	else if(len==16)
	{
		AsciiToBcd(u_buf,card_hrn,16);
		des(card_pwd,u_buf, (unsigned char *)PAN_KEY,0);
	}
	else
	{
		if(0)
		DebugMsg(debugmib.display.asciiOut,"\33[31mPwd len:%d error!Must be 12,14,16 \33[0m",len);		
	}

#if    DEBUG1
	DebugMsg(debugmib.display.asciiOut, "card hrn: %s pinkey: %s",
		card_hrn, pinkey);
#endif
}

u_char
GetAccountZoneID(int head, int tail)
{
    _prepaid_data   *shmp=&ramshm_ptr->prepaid_data;
	int				cos_id;
	COSTable		*cosTableItem;
	
	PutLogFunID("GetAccountZoneID");
    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		cos_id = shmp->prepaid_info[head][tail].cos_id;
		cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
		return cosTableItem->tariff[0].moTariffID%MAX_ZONE_NUM;

	}
    return 0;
}

u_char GetAccountMTZoneID(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	int		cos_id;
	COSTable			*cosTableItem;
	
	PutLogFunID("GetAccountMTZoneID");
    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		cos_id = shmp->prepaid_info[head][tail].cos_id;
		cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
		return cosTableItem->tariff[0].mtTariffID%MAX_ZONE_NUM;
	}
    return 0;
}

int GetAccountLastCallInfo(_account_info *ptr, _last_call_info *pInfo)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetAccountLastCallInfo");
    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		if(shmp->prepaid_info[ptr->head][ptr->tail].last_call_date>tvnow.tv_sec/2)
		{
			pInfo->last_call_type = 
				shmp->prepaid_info[ptr->head][ptr->tail].last_call_type;
			pInfo->last_call_date = 
				shmp->prepaid_info[ptr->head][ptr->tail].last_call_date;
			memcpy(pInfo->last_call_number,
				shmp->prepaid_info[ptr->head][ptr->tail].last_call_number,
				MAX_MSISDN_BLEN);
			return 1;
		}
	}
    return 0;	
}

int IsSpanCall(_state_data *shmp)
{
	PutLogFunID("IsSpanCall");

    if (shmp->caller_info.location !=
            GetAccountZoneID(shmp->caller_info.head, 
                            shmp->caller_info.tail) )
        return TRUE;
    else
        return FALSE;
}

int IncreaseServiceSM(int head, int tail)
{
	_prepaid_info *ptr;

	PutLogFunID("IncreaseServiceSM");

	if(head<MSISDN_HEAD_NUM){
		ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
		if(ptr->free_sm_counter<0x7F) 
			return (++ptr->free_sm_counter);
		return ptr->free_sm_counter; 
	}
	return 0;
}

int IncreaseFavoriteNumberChangeCounterByOmc(_prepaid_info *ptr)
{
	PutLogFunID("IncreaseFavoriteNumberChangeCounterByOmc");
	if(ptr == NULL)
		return 0;	
	
	if(ptr->fav_change_counter<0x7F) 
		return (++ptr->fav_change_counter);
	return ptr->fav_change_counter; 
}

int IncreaseFavoriteNumberChangeCounter(int head, int tail)
{
	_prepaid_info *ptr;

	PutLogFunID("IncreaseFavoriteNumberChangeCounter");

	if(head<MSISDN_HEAD_NUM)
	{
		ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
		if(ptr->fav_change_counter<0x7F) 
			return (++ptr->fav_change_counter);
		return ptr->fav_change_counter; 
	}
	return 0;
}

int GetFriendFamilyChangeCounter(int head, int tail)
{
   _prepaid_info *ptr;

    PutLogFunID("IncreaseFavoriteNumberChangeCounter");

    if(head<MSISDN_HEAD_NUM)
    {
        ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
        return ptr->fav_change_counter; 
    }
    return 0;
}

int GetFriendFamilyCounter(int head, int tail)
{
    _prepaid_info *ptr;

    PutLogFunID("IncreaseFavoriteNumberChangeCounter");

    if(head<MSISDN_HEAD_NUM)
    {
        ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
        return ptr->favorite_numbers; 
    }
    return 0;
}

int GetAccountFriendFamilyPlanID(int head, int tail)
{
    _prepaid_info *ptr;

    PutLogFunID("GetAccountFriendFamilyPlanID");

    if(head<MSISDN_HEAD_NUM)
    {
        ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
        return ptr->favorite_plan_id-1; 
    }
    return -1;

}

int GetFavoriteNumberChangeCharge(_state_data *shmp)
{
	int				head,tail;
	int				amount,tariff_id=0;
	//_prepaid_info	*ptr;

	PutLogFunID("BillingFavoriteNumberChange");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if(head>=MSISDN_HEAD_NUM) 
		return 0;
	tariff_id = GetAccountZoneID(head,tail);
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

	//ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
	amount = ramshm_ptr->tariff_data.comm_tariff[tariff_id].favEditChargeAmount;
	return amount;
}

int BillingFavoriteNumberChange(_state_data *shmp)
{
	int				head,tail;
	int				amount,tariff_id=0;
	_prepaid_info	*ptr;

	PutLogFunID("BillingFavoriteNumberChange");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if(head>=MSISDN_HEAD_NUM) 
		return 0;
	tariff_id = GetAccountZoneID(head,tail);
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

	ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
	amount = ramshm_ptr->tariff_data.comm_tariff[tariff_id].favEditChargeAmount;
	ptr->balance = ptr->balance - amount;
	shmp->caller_info.charge = amount;
	return amount;
}

int IsEnableToAddEditFavoriteNumber(_state_data *shmp, int flag)
{/*flag = 0/1, add, edit */

    CosFriendFamilyControl	 	*friendFamilyControl;
    int	cos_id,plan_id=0;

    cos_id = shmp->caller_info.cos_id;

    friendFamilyControl = &ppsParam.ppsTables.cosTable[cos_id].friendFamilyControl[0];
       
    plan_id = GetAccountFriendFamilyPlanID(shmp->caller_info.head,shmp->caller_info.tail);
    if(plan_id>MAX_FAVORITE_PALN)
    	return 0;
    
    if(plan_id>=0 && flag == 0)
    {
	    if(GetFriendFamilyCounter(shmp->caller_info.head,shmp->caller_info.tail)>= 
	    	   friendFamilyControl->friendFamilyPlan[plan_id].maxNumbers)
	    	return 0;
    }
    if(flag == 0)
        return 1;
    
    if(friendFamilyControl->friendFamilyChangeChargeFlag)
    {
        if(GetFriendFamilyChangeCounter(shmp->caller_info.head,shmp->caller_info.tail)<=
							friendFamilyControl->friendFamilyFreeChangeTimes)
            return 1;
        
        if(GetFavoriteNumberChangeCharge(shmp) > GetAccountBalance(shmp->caller_info.head,shmp->caller_info.tail))
        {
           return 0;
        }  
    }
    
    return 1;    
}

int IsFavoriteNumberTypeOk(_state_data *shmp)
{
    CosFriendFamilyControl	 	*friendFamilyControl;
    int	cos_id;
    char	tmpChargeNumber[32];
    
    cos_id = shmp->caller_info.cos_id;

    friendFamilyControl = &ppsParam.ppsTables.cosTable[cos_id].friendFamilyControl[0];

    strcpy(tmpChargeNumber,shmp->process_info.tmpDigits);

    CutUnknownPrefix(tmpChargeNumber); //tariff prefix always be E.164 
    
    if(!GetMOTariffData(shmp,&shmp->process_info.tariff, tmpChargeNumber, shmp->caller_info.location))
        return 0; //no tariff
    switch(shmp->process_info.tariff.call_type)
    {
        case 0: //local
            if(friendFamilyControl->friendFamilyNumberTypeLocal)
                return 1;
        break;
        case 1: //NDD
            if(friendFamilyControl->friendFamilyNumberTypeNDD)
                return 1;	    
        break;
        case 2: //IDD
            if(friendFamilyControl->friendFamilyNumberTypeIDD)
                return 1;
        break;        
    }
    
    return 0;
}

int CheckClearFriendFamilyNumber(int head, int tail, int rent_charge,int rent_id)
{
    int cos_id;
    _prepaid_info *ptr;
    CosFriendFamilyControl	 	*friendFamilyControl;

    PutLogFunID("CheckClearFriendFamilyNumber");

    if(head>=MSISDN_HEAD_NUM || tail>=UNIT_STORE_ACCOUNT)
    {
        return 0;
    }

    ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
    //if(ptr->fav_change_counter==0)
    //    return 0;
    ptr->free_favorite_call = 0;
    ptr->free_favorite_sms = 0;
    
    cos_id = ptr->cos_id;
    friendFamilyControl = &ppsParam.ppsTables.cosTable[cos_id].friendFamilyControl[0];
    if(friendFamilyControl->keepOrRemoveFriendFamilyNumber==0) //remove
    {
        ptr->rent_type ^= (0x01<<rent_id);
	 ptr->favorite_plan_id = 0;
	 ptr->favorite_numbers = 0;
	 ptr->favorite_plan_state = 0;
	 ptr->fav_change_counter = 0;
	 memset(ptr->favorite_number,0x00,MAX_FAVORITE_NUMBERS*MAX_MSISDN_BLEN);
	 if(ppsParam.ppsTables.cosTable[cos_id].smsNotificationInquiry[0].smsNotificationWhenFriendFamilyServiceRemoved )
	     sendPPSNotificationMessage(head, tail, SMS_FRIEND_FAMILY_REMOVE); 
        return 1;
    }
    if(friendFamilyControl->balanceThresholdToKeepFriendFamilyNumber!=0 &&
    	    friendFamilyControl->balanceThresholdToKeepFriendFamilyNumber>GetAccountBalance( head, tail))
    {   
        ptr->favorite_plan_state = 1;
        if(ppsParam.ppsTables.cosTable[cos_id].smsNotificationInquiry[0].smsNotificationWhenFriendFamilyServiceSuspend)
	     sendPPSNotificationMessage(head, tail, SMS_FRIEND_FAMILY_SERVICE_SUSPEND);         
    	 return 1;
    }
    if(rent_charge>GetAccountBalance( head, tail))
    {
        ptr->favorite_plan_state = 1;

        if(ppsParam.ppsTables.cosTable[cos_id].smsNotificationInquiry[0].smsNotificationWhenFriendFamilyServiceSuspend)
	     sendPPSNotificationMessage(head, tail, SMS_FRIEND_FAMILY_SERVICE_SUSPEND);     

        return 1;
    }
    return 0;
}

int CheckFriendFamilyPlanFreeCallOrSMS(int head, int tail, int type, int duration) /* type0=call, 1=sms */
{
   /* when type0=call, check if call duration + existing free call duration exceeds threshold */
    int cos_id, plan_id=0;
    _prepaid_info *ptr;
    CosFriendFamilyControl	 	*friendFamilyControl;

    PutLogFunID("CheckFriendFamilyPlanFreeSMS");

    if(head>=MSISDN_HEAD_NUM || tail>=UNIT_STORE_ACCOUNT)
    {
        return 0;
    }

    ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
    if(ptr->favorite_numbers ==0)
        return 0;
    if(ptr->favorite_plan_id<=0 || ptr->favorite_plan_id>MAX_FAVORITE_PALN || ptr->favorite_plan_state !=0)
    	return 0;
    
    plan_id = ptr->favorite_plan_id-1;
    cos_id = ptr->cos_id;
    friendFamilyControl = &ppsParam.ppsTables.cosTable[cos_id].friendFamilyControl[0];
    switch(type)
    {
        case 0:
        	if(friendFamilyControl->friendFamilyPlan[plan_id].freeCallDuration == 0)
        	    return 1;
        	if((ptr->free_favorite_call+duration) >= friendFamilyControl->friendFamilyPlan[plan_id].freeCallDuration*60) //keep
                  return 0;
        break;
        case 1:
        default:
            if(friendFamilyControl->friendFamilyPlan[plan_id].freeSMS == 0)
            	    return 1;
            if(ptr->free_favorite_sms >= friendFamilyControl->friendFamilyPlan[plan_id].freeSMS) //keep
                return 0;
        break;
     }
    
    return 1;
}


int IncreaseFriendFamilyFreeCallDuration(int head, int tail, int duration)
{
    _prepaid_info *ptr;

    PutLogFunID("IncreaseFriendFamilyFreeCallDuration");

    if(head>=MSISDN_HEAD_NUM || tail>=UNIT_STORE_ACCOUNT)
    {
        return 0;
    }

    ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];

    if(1)
    {
        int cos_id, max_free_duration, plan_id;
        cos_id = ptr->cos_id;
        plan_id = ptr->favorite_plan_id - 1;
        if(plan_id>0 && plan_id<MAX_FAVORITE_PALN)
        {
            max_free_duration = ppsParam.ppsTables.cosTable[cos_id].friendFamilyControl[0].friendFamilyPlan[plan_id].freeCallDuration*60;
            if(max_free_duration>0)
	    if((ptr->free_favorite_call + duration/CYCLE_STATE_INTERVAL)>max_free_duration)
	    {
	        sendPPSNotificationMessage(head, tail, SMS_FRIEND_FAMILY_PLAN_EXHAUST);
	    }
        }
    }
    ptr->free_favorite_call += ceil( (double) duration / CYCLE_STATE_INTERVAL);
    
    return 1;
}

	
int IncreaseFriendFamilyFreeSMSCounter(int head, int tail)
{
    _prepaid_info *ptr;

    PutLogFunID("IncreaseFriendFamilyFreeSMSCounter");

    if(head>=MSISDN_HEAD_NUM || tail>=UNIT_STORE_ACCOUNT)
    {
        return 0;
    }

    ptr = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];

    if(1)
    {
        int cos_id, max_free_sms, plan_id;
        cos_id = ptr->cos_id;
        plan_id = ptr->favorite_plan_id - 1;
        if(plan_id>0 && plan_id<MAX_FAVORITE_PALN)
        {
            max_free_sms = ppsParam.ppsTables.cosTable[cos_id].friendFamilyControl[0].friendFamilyPlan[plan_id].freeSMS;
            if(max_free_sms>0)
	    if((ptr->free_favorite_sms +1 )>max_free_sms)
	    {
	        sendPPSNotificationMessage(head, tail, SMS_FRIEND_FAMILY_PLAN_EXHAUST);
	    }
        }
    }
    ptr->free_favorite_sms++;
    
    return 1;
}


float GetSMSTariff(_state_data *shmp,u_char tariff_id)
{
	int			i,flag=0,len=0,maxlen=0,index=0;
	float		sms_fee = 0;
	int			discount=100;
	_bill_sms	*ptr;
   	char		bill_no[64];

	//tariff_id = shmp->caller_info.location; 
	//GetAccountZoneID(shmp->caller_info.head,shmp->caller_info.tail);
	if(tariff_id>=MAX_ZONE_NUM ) tariff_id = 0;	
	
	if(shmp->process_info.mnp_status )
		sprintf(bill_no,"%s%s",shmp->process_info.routing_number,
			shmp->called_info.number);	
	else
		strcpy(bill_no, (char *)shmp->charge_info.number);	
		
	for(i=1;i<MAX_SMS_PREFIX;i++)
	{
		ptr = &blf_shmp->bill_sms[tariff_id][i];
		if(ptr->id !=0)
		{
			len = strlen(ptr->bill_id);
			if(!strncasecmp(bill_no,ptr->bill_id,len))
			if(len>maxlen)
			{
				maxlen = len;
				flag = 1;
				index = i;
			}
		}
	}

	if(flag == 0)
	{
		if(shmp->process_info.mnp_status )
		{
			if((logMsgFlag & 0x01) || shmp->process_info.logMsisdn)
			{
				DebugMsg(debugmib.display.asciiOut,
				"\33[33m SMS to ported number, but no RN+MSISDN(%s) billing prefix\33[0m",
					bill_no);
				DebugMsg(debugmib.display.asciiOut,
				"\33[33m Use dialing prefix %s as billing prefix\33[0m",
				shmp->charge_info.number);
			}			
			strcpy(bill_no, (char *)shmp->charge_info.number);	
			for(i=1;i<MAX_SMS_PREFIX;i++)
			{
				ptr = &blf_shmp->bill_sms[tariff_id][i];
				if(ptr->id !=0)
				{
					len = strlen(ptr->bill_id);
					if(!strncasecmp(bill_no,ptr->bill_id,len))
					if(len>maxlen)
					{
						maxlen = len;
						flag = 1;
						index = i;
					}
				}
			}			
		}
		if(flag == 0)
			ptr = &blf_shmp->bill_sms[tariff_id][DEFAULT_SMS_BILLING];
		else
			ptr = &blf_shmp->bill_sms[tariff_id][index];
	}
	else
	{
		ptr = &blf_shmp->bill_sms[tariff_id][index];
	}
	memcpy(&shmp->process_info.smsTariff,ptr,sizeof(_bill_sms));
	sms_fee = ptr->base_fee;
	discount = GetDiscount(tvnow.tv_sec,ptr->discountID);
  //  DebugMsg(debugmib.display.asciiOut, 
  //		"sms tariff : fee:%0.4f discountID:%d discount: %d ",
  //	        sms_fee,ptr->discountID,discount);
	sms_fee = sms_fee * discount /100;
//	if(!shmp->process_info.cap_flag)
	shmp->process_info.cap_flag = ptr->cap_flag;

	if(shmp->process_info.favoriteCall)
	{
           //COSTable    *cosTableItem;

	    //cosTableItem = &ppsParam.ppsTables.cosTable[shmp->caller_info.cos_id];
	    if(CheckFriendFamilyPlanFreeCallOrSMS(shmp->caller_info.head, shmp->caller_info.tail, 1,0))
	    {
	        sms_fee = 0; /* free friend family SMS */
	        shmp->process_info.cap_flag = 0;
	        shmp->process_info.favoriteCall = FRIEND_FAMILY_FREE_SMS; /* free friend family call */
	    }
	}	
	return sms_fee;
}


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

	COSTable		*cosTableItem;	
	CosCapPlan		*capPlanPtr;	

	
	PutLogFunID("CheckCAPSMSNotification");

	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;
	type = staPtr->process_info.cap_flag;
	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->capPlan[cap_plan];
	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;	

	switch(cap_type)
	{
		case 0:
			if(capPlanPtr->type == 0) //counter
			{
				if(type == 1)	/* in net call */
				{
					if((subPtr->in_net_cap_call_duration  + amount)>=c_threshold)
						notifyFlag = 1;
				}
				else if(type == 2) /* out net call */
				{
					if((subPtr->out_net_cap_call_duration + amount)>=c_threshold)
						notifyFlag = 1;
					
				}	
			}
			else	 if(charge>0) //balance
			{
				if(type == 1)
				{
					if((subPtr->in_net_cap_call_balance  + charge)>=c_threshold)
						notifyFlag = 1;
				}
				else if(type == 2)
				{
					if((subPtr->out_net_cap_call_balance + charge)>=c_threshold)
						notifyFlag = 1;
					
				}	
			}
		break;
		case 1:
			if(capPlanPtr->type == 0) //counter
			{
				if(type == 1)
				{
					if((subPtr->in_net_cap_sms_counter  + 1)==c_threshold)
						notifyFlag = 1;
				}
				else if(type == 2)
				{
					if((subPtr->out_net_cap_sms_counter + 1)==c_threshold)
						notifyFlag = 1;
					
				}	
			}
			else	 if(charge>0) //balance
			{
				if(type == 1)
				{
					if((subPtr->in_net_cap_sms_balance  + charge)>=c_threshold)
						notifyFlag = 1;
				}
				else if(type == 2)
				{
					if((subPtr->out_net_cap_sms_balance + charge)>=c_threshold)
						notifyFlag = 1;
					
				}	
			}			
		break;
	}
	
	if(notifyFlag)
	{
	    if(cap_seg == 0)	    
		PPSSendUserSMSNotification(accountPtr->head, accountPtr->tail, (char *)getSmsSentenceNative(0,SMS_CAPSMS_NOTIFY));
	   else if(cap_seg == 1)
	   	PPSSendUserSMSNotification(accountPtr->head, accountPtr->tail, (char *)getSmsSentenceNative(0,SMS_CAPSMS_1_NOTIFY));
	   else
	   {PPSSendUserSMSNotification(accountPtr->head, accountPtr->tail, (char *)getSmsSentenceNative(0,SMS_CAPSMS_2_NOTIFY));}
	   	
		staPtr->process_info.cap_notify_flag = 1;
	}
			
}

void UpdateSubsCapInfo(_account_info *ptr, int cap_type,float charge, int amount, _state_data *sta_ptr)
{
/* cap type: 0=call, 1=sm */

 	_prepaid_data		*shmp=&ramshm_ptr->prepaid_data;
	_prepaid_info		*subPtr;
	u_char			cos_id;
	int			type=sta_ptr->process_info.cap_flag;
	COSTable		*cosTableItem;	

	
	PutLogFunID("UpdateSubsCapBalance");
		
    	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)
		{
			CheckCAPSMSNotification(sta_ptr,ptr,cos_id,cap_type,charge, amount);
		}
		
		if(cap_type==0)
		{
			if(type == 1)
			{
				subPtr->in_net_cap_call_balance = 
					subPtr->in_net_cap_call_balance + charge;
				subPtr->in_net_cap_call_duration  += amount;
				subPtr->in_net_cap_balance = 
					subPtr->in_net_cap_balance + charge;
			}
			else if(type == 2)
			{
				subPtr->out_net_cap_call_balance = 
					subPtr->out_net_cap_call_balance + charge;
				subPtr->out_net_cap_call_duration += amount;
				subPtr->out_net_cap_balance = 
					subPtr->out_net_cap_balance + charge;
			}
		}
		else
		{
			if(type == 1)
			{
				subPtr->in_net_cap_sms_balance = 
					subPtr->in_net_cap_sms_balance + charge;
				subPtr->in_net_cap_sms_counter ++;
				subPtr->in_net_cap_balance = 
					subPtr->in_net_cap_balance + charge;
			}
			else if(type == 2)
			{
				subPtr->out_net_cap_sms_balance = 
					subPtr->out_net_cap_sms_balance + charge;
				subPtr->out_net_cap_sms_counter ++;
				subPtr->out_net_cap_balance = 
					subPtr->out_net_cap_balance + charge;
			}
		}		

	}    		
}

void GetSubsCapInfo(_account_info *ptr,int cap_type, float *balance, int *counter, int type, int cap_flag)
{
 	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetSubsCapInfo");
		
    	if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		if(cap_flag == CAP_TYPE_BOTH)
		{
				if(type == 0)
				{
					*balance = shmp->prepaid_info[ptr->head][ptr->tail].in_net_cap_balance;
					*counter = 0;
				}
				else
				{
					*balance = shmp->prepaid_info[ptr->head][ptr->tail].out_net_cap_balance;
					*counter = 0;
				}
				return;
		}
		
		if(cap_type==0)
		{
			if(type==0) /* int net */
			{
				*balance = shmp->prepaid_info[ptr->head][ptr->tail].in_net_cap_call_balance;
				*counter = shmp->prepaid_info[ptr->head][ptr->tail].in_net_cap_call_duration;			
			}
			else /* out net */
			{
				*balance = shmp->prepaid_info[ptr->head][ptr->tail].out_net_cap_call_balance;
				*counter = shmp->prepaid_info[ptr->head][ptr->tail].out_net_cap_call_duration;
			}
		}
		else
		{
			if(type==0) /* int net */
			{
				*balance = shmp->prepaid_info[ptr->head][ptr->tail].in_net_cap_sms_balance;
				*counter = shmp->prepaid_info[ptr->head][ptr->tail].in_net_cap_sms_counter;			
			}
			else /* out net */
			{
				*balance = shmp->prepaid_info[ptr->head][ptr->tail].out_net_cap_sms_balance;
				*counter = shmp->prepaid_info[ptr->head][ptr->tail].out_net_cap_sms_counter;
			}
		}
	}
	else
	{
		*balance = 0;
		*counter = 0;	
	}    		
}

int GetSubsCapTariffID(_state_data *shmp, int call_type)
{
	int	cos_id=0,tariff_id=-1,cap_plan=0,loop=0;
	float	cap_balance;
	int	cap_counter;
	float	c_threshold0=0,c_threshold1=0,c_threshold2=0;
	int	cap_flag,cap_type;
	COSTable	*cosTableItem;	
	CosCapPlan	*capPlanPtr;
	
	PutLogFunID("GetSubsCapTariffID");
	
	cos_id = shmp->caller_info.cos_id%MaxCosTableItem;

	if(call_type == 0) /* call */
	{
		cap_type = 1;	/* call */
		cap_flag = 1;	
	}
	else	/* sm , please refer param.h cap_plan for the assignment*/
	{
		cap_type = 2;
		cap_flag = 2;
	}
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	for(loop=0;loop<MaxcapPlanTableItem;loop++)
	{
		if((cosTableItem->capPlan[loop].flag & cap_flag )== cap_type && //call
			shmp->process_info.cap_flag == (loop%2+1) )
		{
			cap_flag = cosTableItem->capPlan[loop].flag; /* 0=disable, 1/2/3=cap by SM/CALL/BOTH */
			cap_plan = loop;
			break;
		}
			
	}
	
	shmp->process_info.cap_plan = cap_plan;	
	GetSubsCapInfo(&shmp->caller_info,
				call_type,  // call=0 or sm=1 
				&cap_balance,
				&cap_counter,
				shmp->process_info.cap_flag-1, //innet=0, outnet=1
				cap_flag
				);
	

	capPlanPtr = &cosTableItem->capPlan[cap_plan];
	c_threshold0 = (float)capPlanPtr->threshold0;
	c_threshold1 = (float)capPlanPtr->threshold1;
	c_threshold2 = (float)capPlanPtr->threshold2;		
			
	if(capPlanPtr->type == 0)
	{
		if(c_threshold0>0)
		{
			if(cap_counter<c_threshold0)
			{
				tariff_id = capPlanPtr->tariffID0;		
				shmp->process_info.cap_seg = 0;
			}
			else if(c_threshold1>0)
			{
				if(cap_counter<c_threshold1)
				{
					tariff_id = capPlanPtr->tariffID1;		
					shmp->process_info.cap_seg = 1;
				}
				else if(c_threshold2>0)
				{
					if(cap_counter<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
	{
		if(c_threshold0>0)
		{
			if(cap_balance<c_threshold0)
			{
				tariff_id = capPlanPtr->tariffID0;
				shmp->process_info.cap_seg = 0;
			}
			else if(c_threshold1>0)
			{
				if(cap_balance<c_threshold1)
				{
					tariff_id = capPlanPtr->tariffID1;		
					shmp->process_info.cap_seg = 1;
				}
				else if(c_threshold2>0)
				{
					if(cap_balance<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;
			}
		}				
		
	}		
	return tariff_id;
}


int	UpdateAccountBundlePlanInfoWarningFlag(_state_data *shmp, int flag)
{
	_prepaid_data    *ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("UpdateAccountBundlePlanInfoWarningFlag");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &ptr->prepaid_info[head][tail];
		sub_ptr->bundle_info.warning_sms_flag = flag;
	}	
	
	return 1;
}

int	UpdateAccountBundlePlanInfo(_account_info *shmp, long set_value[7])
{
	_prepaid_data    *ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("UpdateAccountBundlePlanInfo");

	head = shmp->head;
	tail = shmp->tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &ptr->prepaid_info[head][tail];

		if(sub_ptr->bundle_info.data == 0)// set voice/sms to 0, if data==0 when subscribe bundle
		{
			sub_ptr->bundle_info.validity_date = tvnow.tv_sec - 1000;
			sub_ptr->bundle_info.mo_voice = 0;
			sub_ptr->bundle_info.mt_voice = 0;
			sub_ptr->bundle_info.sms = 0;
		}
		
		//sub_ptr->balance = sub_ptr->balance + set_value[0];
		sub_ptr->bundle_info.data +=  ( set_value[1] * 1024 * 1024);
		sub_ptr->bundle_info.mo_voice += ( set_value[2] * 60 );
		sub_ptr->bundle_info.mt_voice += ( set_value[3] * 60 );
		sub_ptr->bundle_info.sms += set_value[4];
		sub_ptr->bundle_info.warning_sms_flag = set_value[6];
		
		if(sub_ptr->bundle_info.validity_date < tvnow.tv_sec)
		{
			sub_ptr->bundle_info.validity_date = tvnow.tv_sec + set_value[5] * 24 * 3600;
		}
		else
		{
			sub_ptr->bundle_info.validity_date += set_value[5] * 24 * 3600;
		}
	}
	
	return 1;
}

int GetAccountBundlePlanInfo(_account_info *ptr, long ret_value[6])
{
	_prepaid_data    *data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	int	retval = 0;
	int	bundle_info_updated_flag = 1;
	
	PutLogFunID("GetAccountBundlePlanInfo");

	head = ptr->head;
	tail = ptr->tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];

		// see if we have to set bundle to 0===================================
		if ((sub_ptr->bundle_info.validity_date > 0) && (sub_ptr->bundle_info.validity_date < tvnow.tv_sec))// 1) set bundle to 0, if expired when querying.
		{
			memset(&sub_ptr->bundle_info, 0x00, sizeof(_bundle_info));
		}
		else if (((sub_ptr->bundle_info.validity_date >= tvnow.tv_sec) && (sub_ptr->bundle_info.data <= 0))\
				|| ((sub_ptr->bundle_info.validity_date == 0) && (sub_ptr->bundle_info.data > 0)))// 2) set bundle to 0, if data == 0 and not expired
			//	&& sub_ptr->bundle_info.mo_voice == 0 
			//	&& sub_ptr->bundle_info.mt_voice == 0 
			//	&& sub_ptr->bundle_info.sms == 0)
		{
			memset(&sub_ptr->bundle_info, 0x00, sizeof(_bundle_info));
		}
		else
		{
			bundle_info_updated_flag = 0;// no change, so do not have to sync.
		}
		//=====================================================

		// return bundle when data > 0 and not expired.
		if ((sub_ptr->bundle_info.data > 0) && (sub_ptr->bundle_info.validity_date >= tvnow.tv_sec))
		{
			retval = 1;
		}
		//
		
		ret_value[0]	= (sub_ptr->bundle_info.data /( 1024*1024));
		ret_value[1] = (sub_ptr->bundle_info.mo_voice / 60 );
		ret_value[2] = (sub_ptr->bundle_info.mt_voice / 60 );
		ret_value[3] = ( sub_ptr->bundle_info.sms );
		ret_value[4] = sub_ptr->bundle_info.validity_date;
		ret_value[5] = sub_ptr->bundle_info.warning_sms_flag;
	}

	if(bundle_info_updated_flag )
	{
		SendAccountRealSync_2(ptr,0.0,0.0,OMC_OCODE_EDIT);	
	}
	
	return retval;
}

extern void pps_print_event(u_char logMsisdnFlag,const char *fmt, ...);
long long pps_get_account_bundle_data(_state_data *shmp)// get remained Bytes from user bundle info
{
	_prepaid_data		*data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("pps_get_account_bundle_data");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];
		/*
		pps_print_event(shmp->process_info.logMsisdn,"[%04d] %s, bundle data=%lld, validity=%ld, now time=%ld",
							shmp->process_info.portid,
							__FUNCTION__,
							sub_ptr->bundle_info.data,
							sub_ptr->bundle_info.validity_date,
							tvnow.tv_sec);
		*/
		if(sub_ptr->bundle_info.validity_date > tvnow.tv_sec)
		{
			return sub_ptr->bundle_info.data;
		}
		else
		{
			return 0;
		}
	}

	return 0;
}

int pps_get_account_bundle_mo_voice(_state_data *shmp)// get remained Bytes from user bundle info
{
	_prepaid_data		*data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("pps_get_account_bundle_mo_voice");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];

		if(sub_ptr->bundle_info.validity_date > tvnow.tv_sec)
		{
			return sub_ptr->bundle_info.mo_voice;
		}
		else
		{
			return 0;
		}
	}

	return 0;
}
	
int pps_update_account_bundle_mo_voice(_state_data *shmp, int seconds)//
{
	_prepaid_data		*data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("pps_update_account_bundle_mo_voice");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];

		if(sub_ptr->bundle_info.validity_date > tvnow.tv_sec)
		{
			sub_ptr->bundle_info.mo_voice += seconds;
			if(sub_ptr->bundle_info.mo_voice <= 0)
			{
				sub_ptr->bundle_info.mo_voice = 0;
				subscribe_bundle_plan_send_used_up_info((char *)shmp->caller_info.number, SMS_BUNDLE_PLAN_VOICE_USEDUP); /* should only send MO voice is used up */
			}
		}
		else
		{
			return 0;
		}
	}

	return 1;
}

int pps_get_account_bundle_mt_voice(_state_data *shmp)//
{
	_prepaid_data		*data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("pps_get_account_bundle_mt_voice");

	head = shmp->called_info.head;
	tail = shmp->called_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];

		if(sub_ptr->bundle_info.validity_date > tvnow.tv_sec)
		{
			return sub_ptr->bundle_info.mo_voice;// mt use the same bundle as mo
		}
		else
		{
			return 0;
		}
	}

	return 0;
}
	
int pps_update_account_bundle_mt_voice(_state_data *shmp, int seconds)// 
{
	_prepaid_data		*data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("pps_update_account_bundle_mt_voice");

	head = shmp->called_info.head;
	tail = shmp->called_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];

		if(sub_ptr->bundle_info.validity_date > tvnow.tv_sec)
		{
			sub_ptr->bundle_info.mo_voice += seconds;// mt use the same bundle as mo
			if(sub_ptr->bundle_info.mo_voice <= 0)
			{
				sub_ptr->bundle_info.mo_voice = 0;
				subscribe_bundle_plan_send_used_up_info((char *)shmp->called_info.number, SMS_BUNDLE_PLAN_VOICE_USEDUP); /* should only send MT voice is used up */
			}
		}
		else
		{
			return 0;
		}
	}

	return 1;
}


int pps_get_account_bundle_mo_sms(_state_data *shmp)// get remained Bytes from user bundle info
{
	_prepaid_data		*data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("pps_get_account_bundle_mo_sms");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];

		if(sub_ptr->bundle_info.validity_date > tvnow.tv_sec)
		{
			return sub_ptr->bundle_info.sms;
		}
		else
		{
			return 0;
		}
	}

	return 0;
}

int pps_update_account_bundle_mo_sms(_state_data *shmp, int count)// get remained Bytes from user bundle info
{
	_prepaid_data		*data_ptr =&ramshm_ptr->prepaid_data;
	_prepaid_info		*sub_ptr = NULL;
	int	head, tail;
	
	PutLogFunID("pps_get_account_bundle_mo_voice");

	head = shmp->caller_info.head;
	tail = shmp->caller_info.tail;
	
	if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		sub_ptr = &data_ptr->prepaid_info[head][tail];

	//	if(sub_ptr->bundle_info.validity_date > tvnow.tv_sec)
		{
			sub_ptr->bundle_info.sms += count;
			if(sub_ptr->bundle_info.sms <= 0)
			{	
				sub_ptr->bundle_info.sms = 0;
				subscribe_bundle_plan_send_used_up_info((char *)shmp->caller_info.number, SMS_BUNDLE_PLAN_SMS_USEDUP); /* should only send MO SMS is used up */
			}
		}
		/*
		else
		{
			return 0;
		}
		*/
	}

	return 1;
}

extern int  ocs_get_charge_prefix(char *dialed_number, char *prefix, char *area_name, int *call_type);
extern int crm_check_if_cug_call(_account_info *caller, _account_info *called);
int BillingSMSProc(_state_data *shmp, int sms_num)
{
	float 			balance = 0,  discount=100.0;
	float 			mosms_unit_charge ;
	int				cos_id,tariff_id;
	COSTable		*cosTableItem = NULL;
	int charge_num = sms_num, bundle_used_num = 0;
	CosServiceControl	 	*serviceControlItem = NULL;
	
	PutLogFunID("BillingSMSProc");

	if(OCS_with_external_CRM())
	{
		if(crm_check_if_cug_call(&shmp->caller_info, &shmp->called_info))
		{
			mosms_unit_charge = 0;
			shmp->process_info.rest_if.total_plan_value = 0;
			shmp->process_info.rest_if.used_plan_value = 0;			
		}
		else
			mosms_unit_charge = shmp->process_info.rest_if.unit_charge * shmp->process_info.rest_if.tariff_discount / discount;
	}
	else
		mosms_unit_charge = GetSMSTariff(shmp,shmp->caller_info.location);

	// Check Cap or Bundle is used================================
	cos_id = shmp->caller_info.cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	serviceControlItem = &cosTableItem->serviceControl[0];
	if(serviceControlItem->cap_or_bundle_in_tariff)// bundle is used, so we can set bundle flag and clear cap flag
	{
		shmp->process_info.bundle_flag = shmp->process_info.cap_flag;
		shmp->process_info.cap_flag = 0;
	}
	//==================================================
	
	if(shmp->process_info.bundle_flag)
	{
		int left_mo_sms = 0;

		left_mo_sms = pps_get_account_bundle_mo_sms(shmp);
		if(left_mo_sms > 0)
		{
			if (left_mo_sms >= charge_num)
			{
				shmp->caller_info.fee = 0;
				shmp->caller_info.valid_time = 10*ONE_MINUTE_STATE_TIME;
				shmp->called_info.fee = 0;
				shmp->called_info.valid_time = shmp->caller_info.valid_time;
				bundle_used_num = charge_num;
				return bundle_used_num;// bundle charge num
			}
			else
			{
				charge_num -= left_mo_sms;
				bundle_used_num = left_mo_sms;
			}
		}
		else
		{
			shmp->process_info.bundle_flag = 0;			
		}
	}
	
	if(shmp->process_info.cap_flag)
	{
	    tariff_id=GetSubsCapTariffID(shmp,CAP_SMS);
	    if(tariff_id>=0)
	    {
		mosms_unit_charge = GetSMSTariff(shmp,tariff_id); 
		/* there should always be DEFAULT tariff prefix */
		shmp->caller_info.location = tariff_id;
	    }
	}
	//no roaming fee charged for ROAMING SMS, 2005-05-09

	switch(shmp->process_info.call_type)
	{
		case CT_MO_SM:	   
		if(shmp->process_info.serviceType == SRV_SCRATCH_CARD_RECHARGE ||
			shmp->process_info.serviceType == SRV_RECHARGE_2TYPES ||
			shmp->process_info.serviceType == SRV_FEE_TRANSFER ||
			shmp->process_info.serviceType == SRV_FREE_SMS ||
			shmp->process_info.serviceType == SRV_CALL_ME_BACK ||
			shmp->process_info.serviceType == SRV_BUNDLE_PLAN ||
			shmp->process_info.serviceType == USSD_OC_BUNDLE_QUERY)		
		{
			shmp->process_info.cap_flag = 0;
   			shmp->caller_info.fee = 0;
			shmp->caller_info.valid_time = 10*ONE_MINUTE_STATE_TIME;
			shmp->called_info.fee = 0;
			shmp->called_info.valid_time = shmp->caller_info.valid_time;
			return bundle_used_num;
		}
		
		if(cosTableItem->serviceControl[0].moThresholdSet)
		{
		     balance = (int)GetBalanceWithPromotionControl(shmp,&shmp->caller_info)
				+ GetOverdraft(&shmp->caller_info)
				- (cosTableItem->serviceControl[0].moMinimumBalance);
		
			if(balance<0)
			{
				shmp->caller_info.fee = 0;
				shmp->caller_info.valid_time = 0;
				shmp->called_info.fee = 0;
				shmp->called_info.valid_time = 0;
				return bundle_used_num;
			}
		}

        balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info) + GetOverdraft(&shmp->caller_info);

		if(shmp->process_info.serviceType == SRV_INQUIRY )
		{	     		
			shmp->process_info.cap_flag = 0;
			if(!(cosTableItem->smsNotificationInquiry[0].chargeWhenInquiryBySMS))
			{ /* not charging on SM inquring */
	     			shmp->caller_info.fee = 0;
       				shmp->caller_info.valid_time = 10*ONE_MINUTE_STATE_TIME;
						/* should be a parameter */
					shmp->called_info.fee = 0;
					shmp->called_info.valid_time = shmp->caller_info.valid_time;
					return bundle_used_num;
			}
			else
			{
				/* inquiry charged, recharge should be always free,not yet,2004-01-06 */
				if(IncreaseServiceSM(shmp->caller_info.head,
	     						shmp->caller_info.tail)<=
									(cosTableItem->smsNotificationInquiry[0].freeInquiryPerDay))
				{
	     				shmp->caller_info.fee = 0;
						shmp->caller_info.valid_time = 10*ONE_MINUTE_STATE_TIME;
						shmp->called_info.fee = 0;
						shmp->called_info.valid_time = shmp->caller_info.valid_time;
						return bundle_used_num;
	     			}
	    	 	}
		}
		/* zero balance can't send free sms,06-02-27 */
		/*
		if(balance == 0 && mosms_unit_charge==0)
		{
         	shmp->caller_info.valid_time = 0; // should be a parameter 
	     	shmp->called_info.valid_time = 0;
	     	shmp->process_info.tcos = CALLER_CREDIT_OUT; // no enough balance 
		}
		else if(balance>=mosms_unit_charge)
			*/
		 if(balance>=(mosms_unit_charge * charge_num))
		{
       		shmp->caller_info.valid_time = 10*ONE_MINUTE_STATE_TIME; /* should be a parameter */
	     		shmp->called_info.valid_time = 10*ONE_MINUTE_STATE_TIME;
	        	shmp->caller_info.fee = mosms_unit_charge * charge_num;
	     		shmp->called_info.fee = 0x00;
		}
		else
		{
	         	shmp->caller_info.valid_time = 0; /* should be a parameter */
		     	shmp->called_info.valid_time = 0;
		     	shmp->process_info.tcos = CALLER_CREDIT_OUT; /* no enough balance */
		}
		break;
		default:
			shmp->caller_info.valid_time = 0; 
			shmp->called_info.valid_time = 0;
			shmp->process_info.tcos = CALLER_CREDIT_OUT;	    
		break;
	}
	return bundle_used_num;
}

int 
ConferenceCallBillingProc(_state_data *shmp,int duration)
{
   float charge,balance;
   int   unit_time=60,unit_amount,tariff_id=0;
   int	cos_id;
   COSTable			*cosTableItem;
	
	PutLogFunID("ConferenceCallBillingProc");
	tariff_id = GetAccountZoneID(shmp->caller_info.head,shmp->caller_info.tail);
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

   	if(shmp->caller_info.status != T_NORMAL ||
      tvnow.tv_sec>= GetExpiration(&shmp->caller_info))
	  return FALSE;
	cos_id = shmp->caller_info.cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	
   	unit_time = blf_shmp->comm_tariff[tariff_id].ccChargeUnit;
   	if(unit_time<=0) unit_time = 60;
   	unit_amount = blf_shmp->comm_tariff[tariff_id].ccChargeAmount;
   	if(unit_amount<=0) unit_amount = 20;

   	if(cosTableItem->serviceControl[0].moThresholdSet)
	   balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info) + GetOverdraft(&shmp->caller_info)
				- cosTableItem->serviceControl[0].moMinimumBalance;
   	if(balance<0)
   	{
		return FALSE;
  	 }
  
	balance = GetBalanceWithPromotionControl(shmp,&shmp->caller_info) + GetOverdraft(&shmp->caller_info);
		
   	charge = (duration * unit_amount*1.0) / unit_time;
   	
   	shmp->caller_info.charge = charge;
  	shmp->caller_info.fee = charge;
   	if(balance>=charge) return TRUE;
   	return FALSE;
}

void ChangeTariffToCugTariff(_state_data *shmp,_bill_tariff 	*ptar){
	u_short 		rUnit=60;
	float			rAmount=0;
	u_short 		Unit=0;
	u_char 			tier;
	int 			tariff_id;
	
	PutLogFunID("ChangeTariffToCugTariff");
	tariff_id = shmp->caller_info.location;
    Unit = ptar->unit_time;
    if(Unit == 0) Unit = 60;
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;
		
    rUnit = blf_shmp->comm_tariff[tariff_id].cugChargeUnit;
	rAmount = blf_shmp->comm_tariff[tariff_id].cugChargeAmount;

	if(rUnit == 0) rUnit = 60;
    tier = getCurrentTier(shmp,ptar);
	ptar->tierRating.rating[tier].base_fee =  Unit * rAmount /rUnit;
}

void ChangeTariffToFavoriteCallTariff(_state_data *shmp,_bill_tariff 	*ptar)
{
	u_short 		rUnit=60;
	float			rAmount=0;
	u_short 		Unit=0;
	u_char 		tier;
	int 			tariff_id;
	
	PutLogFunID("ChangeTariffToFavoriteCallTariff");

	if(shmp->process_info.favoriteCall == FRIEND_FAMILY_FREE_CALL)
		return;
	tariff_id = shmp->caller_info.location;

       Unit = ptar->unit_time;
       if(Unit == 0) Unit = 60;

	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

       rUnit = blf_shmp->comm_tariff[tariff_id].favChargeUnit;
	rAmount = blf_shmp->comm_tariff[tariff_id].favChargeAmount;

	if(rUnit ==0) rUnit = 60;
       tier = getCurrentTier(shmp,ptar);
	
	ptar->tierRating.rating[tier].base_fee =  Unit * rAmount /rUnit;
}


void ChangeTariffToMORoamingTariff(_state_data *shmp,_bill_tariff 	*ptar){
	u_short 		rUnit,Unit=0;
	float			rAmount,Amount=0;
	u_char 			tier;
	int 			tariff_id;
	_bill_tariff  const *moPtr;
	
	PutLogFunID("ChangeTariffToMORoamingTariff");
	moPtr = shmp->process_info.tariffMoReadOnly;
	if(moPtr == NULL)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]ChangeTariffToMORoamingTariff,no mo tariff",
							shmp->process_info.portid);
		return;
	}
	tariff_id = shmp->caller_info.location;
    Unit = moPtr->unit_time;
    if(Unit == 0) Unit = 60;
	
	tier = getCurrentTier(shmp, (_bill_tariff *)moPtr);
	Amount = moPtr->tierRating.rating[tier].base_fee;
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

    rUnit = blf_shmp->comm_tariff[tariff_id].roamingChargeUnit;
	rAmount = blf_shmp->comm_tariff[tariff_id].roamingChargeAmount;
	if(rUnit ==0) rUnit = 60;
    	
	ptar->tierRating.rating[tier].base_fee =  Unit * rAmount /rUnit + Amount;
}

void ChangeTariffToMTRoamingTariff(_state_data *shmp,_bill_tariff 	*ptar)
{
	u_short 		rUnit,Unit;
	float			rAmount,base_fee;
	int     		location;
	char			destination[128],inter_prefix[32];
	u_char 			tier,tierDest;
	
	_bill_tariff 	*ptrTemp,btar;
	_bill_tariff  const *mtPtr;
	
	PutLogFunID("ChangeTariffToMTRoamingTariff");

	memset(&btar,0,sizeof(_bill_tariff));
	ptrTemp = &btar;
	mtPtr = shmp->process_info.tariffMtReadOnly;
	if(mtPtr == NULL)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]ChangeTariffToMTRoamingTariff,no mt tariff",
							shmp->process_info.portid);
		return;
	}
	
    location = GetAccountZoneID(shmp->called_info.head,
								shmp->called_info.tail);
	
	xap_get_interprefix((u8 *)inter_prefix);

	BcdToAscii(destination, (BYTE *)inter_prefix, 4);
	StringCut(destination,'E');
	strcat(destination,shmp->cdr_info.locationNumber);

	shmp->process_info.tariff_flag = GetMOTariffData(shmp,ptrTemp, destination, location);

	if(shmp->process_info.tariff_flag <= 0)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]ChangeTariffToMTRoamingTariff,can't find prefix by:%s,assume basic fee and long fee as 0",
							shmp->process_info.portid,destination);
	}
	
	location = shmp->called_info.location;
    rUnit = blf_shmp->comm_tariff[location].roamingChargeUnit;
	rAmount = blf_shmp->comm_tariff[location].roamingChargeAmount;
	if(rUnit ==0) rUnit = 60;

	Unit = mtPtr->unit_time;	
    if(Unit == 0) Unit = 60;
	
	tier = getCurrentTier(shmp, (_bill_tariff *)mtPtr);	
	tierDest = getCurrentTier(shmp,ptrTemp);
		
	base_fee = mtPtr->tierRating.rating[tier].base_fee;

	ptar->tierRating.rating[tier].base_fee =  base_fee + Unit * rAmount /rUnit;

	Unit = ptrTemp->unit_time;
    if(Unit == 0) Unit = 60;
	rUnit = mtPtr->unit_time;
	if(rUnit == 0) rUnit = 60;

	if(Unit<rUnit)
	{
		ptar->tierRating.rating[tier].long_distance_fee = 
			mtPtr->tierRating.rating[tier].long_distance_fee + ptrTemp->tierRating.rating[tierDest].long_distance_fee*rUnit/Unit;
	}
	else
	{
		ptar->tierRating.rating[tier].long_distance_fee = 
			ptrTemp->tierRating.rating[tierDest].long_distance_fee + mtPtr->tierRating.rating[tier].long_distance_fee * Unit / rUnit;
		ptar->tierRating.rating[tier].base_fee = ptar->tierRating.rating[tier].base_fee * Unit / rUnit;
		ptar->adjustment = mtPtr->adjustment * Unit / rUnit;
		ptar->unit_time = mtPtr->unit_time * Unit / rUnit;
		ptar->mini_unit = ptrTemp->mini_unit;
	}
}	

void ChangeTariffToCfTariff(_state_data *shmp,_bill_tariff 	*ptar){
	u_short 		cfUnit;
	float			cfAmount;
	u_short 		Unit=0;
	u_char 			tier;
	int 			tariff_id;
	
	PutLogFunID("ChangeTariffToCfTariff");
	tariff_id = shmp->caller_info.location;
    Unit = ptar->unit_time;
    if(Unit == 0) Unit = 60;
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

    cfUnit = blf_shmp->comm_tariff[tariff_id].cfChargeUnit;
	cfAmount = blf_shmp->comm_tariff[tariff_id].cfChargeAmount;

	if(cfUnit ==0) cfUnit = 60;
    tier = getCurrentTier(shmp,ptar);
	ptar->tierRating.rating[tier].base_fee =  Unit * cfAmount /cfUnit;
}

void ChangeTariffToCfCugTariff(_state_data *shmp,_bill_tariff 	*ptar){
	u_short 		cfUnit;
	float			cfAmount;
	u_short 		Unit=0;
	u_char 			tier;
	int 			tariff_id;
												
	PutLogFunID("ChangeTariffToCfCugTariff");
	tariff_id = shmp->caller_info.location;
    Unit = ptar->unit_time;
    if(Unit == 0) Unit = 60;
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

    cfUnit = blf_shmp->comm_tariff[tariff_id].cf_cugChargeUnit;
	cfAmount = blf_shmp->comm_tariff[tariff_id].cf_cugChargeAmount;

	if(cfUnit ==0) cfUnit = 60;
    tier = getCurrentTier(shmp,ptar);
	ptar->tierRating.rating[tier].base_fee =  Unit * cfAmount /cfUnit;
}

void ChangeTariffToCfFavTariff(_state_data *shmp,_bill_tariff 	*ptar){
	u_short 		cfUnit;
	float			cfAmount;
	u_short 		Unit=0;
	u_char 			tier;
	int 			tariff_id;
	
	PutLogFunID("ChangeTariffToCfFavTariff");
	tariff_id = shmp->caller_info.location;
    Unit = ptar->unit_time;
    if(Unit == 0) Unit = 60;
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

    cfUnit = blf_shmp->comm_tariff[tariff_id].cf_favChargeUnit;
	cfAmount = blf_shmp->comm_tariff[tariff_id].cf_favChargeAmount;

	if(cfUnit ==0) cfUnit = 60;
    tier = getCurrentTier(shmp,ptar);
	ptar->tierRating.rating[tier].base_fee =  Unit * cfAmount /cfUnit;
}

void ChangeTariffToCugFavTariff(_state_data *shmp,_bill_tariff 	*ptar){
	u_short			cfUnit;
	float			cfAmount;
	u_short			Unit=0;
	u_char 			tier;
	int 			tariff_id;
	
	PutLogFunID("ChangeTariffToCugFavTariff");
	tariff_id = shmp->caller_info.location;
    Unit = ptar->unit_time;
    if(Unit == 0) Unit = 60;
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

    cfUnit = blf_shmp->comm_tariff[tariff_id].cug_favChargeUnit;
	cfAmount = blf_shmp->comm_tariff[tariff_id].cug_favChargeAmount;

	if(cfUnit ==0) cfUnit = 60;
    tier = getCurrentTier(shmp,ptar);
	ptar->tierRating.rating[tier].base_fee =  Unit * cfAmount /cfUnit;
}

void ChangeTariffToCfCugFavTariff(_state_data *shmp,_bill_tariff 	*ptar){
	u_short 		cfUnit;
	float			cfAmount;
	u_short 		Unit=0;
	u_char 			tier;
	int 			tariff_id;
	
	PutLogFunID("ChangeTariffToCfCugFavTariff");
	tariff_id = shmp->caller_info.location;
    Unit = ptar->unit_time;
    if(Unit == 0) Unit = 60;
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;

    cfUnit = blf_shmp->comm_tariff[tariff_id].cf_cug_favChargeUnit;
	cfAmount = blf_shmp->comm_tariff[tariff_id].cf_cug_favChargeAmount;

	if(cfUnit ==0) cfUnit = 60;
    tier = getCurrentTier(shmp,ptar);
	ptar->tierRating.rating[tier].base_fee =  Unit * cfAmount /cfUnit;
}

int GetMTTariffData(_state_data *shmp,
					u_char tariffID,
					_bill_tariff *ptar, 
					_account_info *pinfo,
					_account_info *pcaller)
{
	int						prefixIndex = DEFAULT_MT_BILLING,i;
	int						len=0,maxlen=0;
	_bill_tariff 			*ptr;
	int						cos_id;
	COSTable				*cosTableItem;
	
	PutLogFunID("GetMTTariffData");


	cos_id = pinfo->cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	if(cosTableItem->serviceControl[0].mtChargeFlag==0)
	{
		ptar->call_type = 4;
		ptar->free_time = 0;
		ptar->unit_time = 60;
		ptar->mini_unit = 1;		
		ptar->adjustment = 0;		
		memset(&ptar->tierRating,0,sizeof(TIER_RATING));
		return 1;
	}

	if(tariffID>=MAX_ZONE_NUM ) 
		tariffID = 0;

	memset(ptar,0,sizeof(_bill_tariff));

	for(i=1;i<CALLED_BASE_NUM;i++)
	{
		ptr = &blf_shmp->called_tariff[tariffID][i];
		if(ptr->bill_id[0] == 0) continue;
		len = strlen(ptr->bill_id);

		if(len>maxlen)
		if(!strncasecmp((char *)pcaller->number,ptr->bill_id,len))
		{
			maxlen = len;
			prefixIndex = i;
		}
	}
	memcpy(ptar,&blf_shmp->called_tariff[tariffID][prefixIndex],sizeof(_bill_tariff));	
	shmp->process_info.tariffMtReadOnly = &blf_shmp->called_tariff[tariffID][prefixIndex];
	return 1;
}

int cfLargeThancug(int tariff_id)
{
	_comm_tariff *commTariff;

	PutLogFunID("cfLargeThancug");
	if(tariff_id>=MAX_ZONE_NUM || tariff_id<0) tariff_id = 0;
	commTariff = &ramshm_ptr->tariff_data.comm_tariff[tariff_id];
	if((commTariff->cfChargeAmount*commTariff->cugChargeUnit)>
		(commTariff->cugChargeAmount*commTariff->cfChargeUnit))
			return 1;
	else
			return 0;
}

int BillingProc(_state_data *shmp, int sub_flag0)
{
	int          	duration=0,overdraft=0,sub_flag;
	int				cos_id;
	COSTable		*cosTableItem;
	CosServiceControl	 	*serviceControlItem = NULL, *mtServiceCtrItem = NULL;
	 
	PutLogFunID("BillingProc");
	
	sub_flag = sub_flag0;
	cos_id = shmp->caller_info.cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	serviceControlItem = &cosTableItem->serviceControl[0];
		
	if (sub_flag || shmp->process_info.favoriteCall == FRIEND_FAMILY_FREE_CALL)
	{
		if(shmp->context_info.callType == CT_MT_CALL)
		{
			shmp->process_info.tariff_flag =
					GetMTTariffData(shmp,
									shmp->called_info.location,
									&shmp->process_info.tariff, 
									&shmp->called_info,
									&shmp->charge_info); 
		}
		else
		{
			if(shmp->process_info.serviceType == SRV_CRD 
				||
				(shmp->process_info.serviceType == SRV_NORMAL_MO_CALL 
				 &&
				 shmp->process_info.callConnected == 1
				 &&
				 !ppsParam.serviceControl.vms_allways_go))
			{
				GetMOTariffData(shmp, &shmp->process_info.tariff, (char *)shmp->charge_info.number, shmp->caller_info.location);
				shmp->process_info.tariff_flag = 1;
				shmp->caller_info.valid_time = 12*ONE_MINUTE_STATE_TIME;
				shmp->caller_info.fee = 0;
				shmp->called_info.valid_time = 12*ONE_MINUTE_STATE_TIME;
				shmp->called_info.fee = 0;
				return 1;
			}
			if(shmp->context_info.callType == CT_MO_CALL &&
				cosTableItem->smsNotificationInquiry[0].smsNotificationWhenBalanceInsufficient)
			if(GetBalanceWithPromotionControl(shmp,&shmp->caller_info)<
				cosTableItem->smsNotificationInquiry[0].insufficientBalanceThreshold)
			{
				shmp->process_info.pre_warning = 1;
				shmp->process_info.voiceID = PRE_WARNING_LOW_BALANCE;
			}
			if(shmp->process_info.mnp_status)
			{
				char	billing_prefix[64];
				sprintf(billing_prefix,"%s%s",shmp->process_info.routing_number,
						shmp->called_info.number);
						
				shmp->process_info.tariff_flag = 
				GetMOTariffData(shmp,&shmp->process_info.tariff, billing_prefix, shmp->caller_info.location);
				/* parameter: continue or release the call */
				if(shmp->process_info.tariff_flag==0)
				{
					if((logMsgFlag & 0x01) || shmp->process_info.logMsisdn)
					{
						DebugMsg(debugmib.display.asciiOut,
							"\33[33m Calling ported number, but no RN+MSISDN(%s) billing prefix\33[0m",
							billing_prefix);
						DebugMsg(debugmib.display.asciiOut,
							"\33[33m Use dialing prefix %s as billing prefix\33[0m",
							shmp->charge_info.number);
					}
					shmp->process_info.tariff_flag = 
						GetMOTariffData(shmp, &shmp->process_info.tariff, 
							(char *)shmp->charge_info.number, 
							shmp->caller_info.location);
				}
				else
				{
					strcpy((char *)shmp->charge_info.number, billing_prefix);
				}
			}			
			else
			{
				shmp->process_info.tariff_flag = 
					GetMOTariffData(shmp,&shmp->process_info.tariff, 
							(char *)shmp->charge_info.number, 
							shmp->caller_info.location);
				
			}			
			/* check MO tariff cap flag here */
			/* added for cap call, 2007-10-25 */
			shmp->process_info.cap_flag = shmp->process_info.tariff.cap_flag;
			// Check Cap or Bundle is used================================
			if(serviceControlItem->cap_or_bundle_in_tariff)// bundle is used, so we can set bundle flag and clear cap flag
			{
				shmp->process_info.bundle_flag = shmp->process_info.cap_flag; /* cap flag is reused as bundle call */
				shmp->process_info.cap_flag = 0;
			}
			//==================================================
	
			if(shmp->process_info.cap_flag>0)
			{
//				DebugMsg(debugmib.display.asciiOut,"CAP call, flag: %d",
	//				shmp->process_info.cap_flag);
				shmp->caller_info.location = GetSubsCapTariffID(shmp,CAP_CALL);
				GetMOTariffData(shmp,&shmp->process_info.tariff, 
							(char *)shmp->charge_info.number, 
							shmp->caller_info.location);
				
			}
			
			if(shmp->process_info.favoriteCall)
			{				    
				    if(CheckFriendFamilyPlanFreeCallOrSMS(shmp->caller_info.head, shmp->caller_info.tail, 0, shmp->process_info.duration/CYCLE_STATE_INTERVAL))
				    {
				        shmp->process_info.favoriteCall = FRIEND_FAMILY_FREE_CALL;	

				        memset(&shmp->process_info.tariff.tierRating, 0x00, sizeof(TIER_RATING));
				        shmp->process_info.tariff.adjustment = 0;
				        shmp->process_info.tariff.cap_flag = 0;
				    }
				    else
				    {
				        IncreaseFriendFamilyFreeCallDuration(shmp->caller_info.head, 
                        		 	shmp->caller_info.tail, 
		                            shmp->process_info.duration);
				        shmp->process_info.favoriteCall = FRIEND_FAMILY_CALL;
				    }
			}
			if(serviceControlItem->moThresholdSet && shmp->process_info.favoriteCall != FRIEND_FAMILY_FREE_CALL)
			{
				if((GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+GetOverdraft(&shmp->caller_info))
					< (serviceControlItem->moMinimumBalance))
				{
					shmp->caller_info.valid_time = 0;
					shmp->caller_info.fee = 0;
					shmp->called_info.valid_time = 0;
					shmp->called_info.fee = 0;
					return 0;
				}
			}

		}			
	}// if sub_flag

    switch(shmp->context_info.callType){
		case CT_MT_CALL:
		   switch(shmp->called_info.status)
		   { 
		      case T_FRESH:
			  case T_SUSPEND:
			  case T_BLACKLIST:
			  case T_EXTERNAL:
			  case T_RELEASED:
				shmp->caller_info.valid_time = 0;
				shmp->called_info.valid_time = 0;
			    shmp->process_info.tcos = CALLED_STATUS_ERROR;
  				shmp->process_info.voiceID = MT_ERROR_ACCOUNT_STATUS;
				return FALSE;
			  break;
			  default:
			  break;
			}

			// Check Cap or Bundle is used================================
			mtServiceCtrItem = &ppsParam.ppsTables.cosTable[shmp->called_info.cos_id].serviceControl[0];
			if(mtServiceCtrItem->cap_or_bundle_in_tariff)// bundle is used, so we can set bundle flag
			{
			   	shmp->process_info.bundle_flag = shmp->process_info.tariff.cap_flag;
			}
			//==================================================

			if(shmp->process_info.bundle_flag)
			{
				int left_mt_voice_seconds = 0;
				
				left_mt_voice_seconds = pps_get_account_bundle_mt_voice(shmp);
				if(left_mt_voice_seconds > 0)
				{
					if(left_mt_voice_seconds > shmp->process_info.tariff.unit_time)
						left_mt_voice_seconds = shmp->process_info.tariff.unit_time;
					shmp->caller_info.valid_time = left_mt_voice_seconds*CYCLE_STATE_INTERVAL;

					shmp->caller_info.fee = 0;
					shmp->called_info.fee = 0;
					shmp->called_info.valid_time = shmp->caller_info.valid_time;
					//DebugMsg(debugmib.display.asciiOut, "BillingProc()->%s allocate bundle mt voice[%d]",
					//				shmp->caller_info.number, left_mt_voice_seconds);
					
					return 1;
				}
				else
				{
					/*
					if(shmp->process_info.duration > 1)
					{
						subscribe_bundle_plan_send_used_up_info(shmp->caller_info.number, SMS_BUNDLE_PLAN_VOICE_USEDUP);	
					}
					*/
					
					shmp->process_info.bundle_flag = 0;
				}
			}
			
			if(shmp->process_info.chargedTime==0 && shmp->process_info.tariff.free_time)
			{
				shmp->called_info.valid_time = 
					shmp->process_info.tariff.free_time*CYCLE_STATE_INTERVAL;
				shmp->called_info.fee = 0;
		
			}
			else
			{
				
				sub_flag = shmp->process_info.chargedTime;
				if(shmp->process_info.tariff.free_time)
					sub_flag = shmp->process_info.chargedTime - 1;

				overdraft = GetOverdraft(&shmp->called_info);
				if(!sub_flag && overdraft<0){
					overdraft = 0;
				}
				shmp->called_info.valid_time = 
						   GetValidTime( shmp,
										tvnow.tv_sec,
										//shmp->cdr_info.answertime,
										&shmp->process_info.tariff,
										GetBalanceWithPromotionControl(shmp,&shmp->called_info),
										sub_flag,
										overdraft);
				
				duration = (int)( shmp->called_info.valid_time / CYCLE_STATE_INTERVAL);
				shmp->called_info.fee = TimeToCharge(shmp,&shmp->process_info.tariff, duration, tvnow.tv_sec,sub_flag);							
				if(isPoolChildReachFeeLimit(&shmp->called_info) == 2)
				{
					pps_print_error(shmp->process_info.logMsisdn,"%s child account,reach limited fee",shmp->called_info.number);
					shmp->caller_info.valid_time = 0;
					shmp->caller_info.fee = 0;
					shmp->called_info.valid_time = 0;		
					shmp->called_info.fee = 0;			
					shmp->process_info.tcos = CALLER_REACH_CHARGE_LIMIT;
					shmp->process_info.pre_warning = 1;
					shmp->process_info.voiceID = PRE_WARNING_LOW_BALANCE;
					return -1;
				}	
			}
			shmp->caller_info.fee = 0;
			shmp->caller_info.valid_time = shmp->called_info.valid_time;
		break;
		case CT_MF_CALL:
		case CT_MO_CALL:			
			if(shmp->process_info.serviceType == SRV_CRD 
				||
				(shmp->process_info.serviceType == SRV_NORMAL_MO_CALL 
				 &&
				 shmp->process_info.callConnected == 1
				 &&
				 !ppsParam.serviceControl.vms_allways_go))
			{
				shmp->process_info.tariff_flag = 1;
				shmp->caller_info.valid_time = 6*ONE_MINUTE_STATE_TIME;
				shmp->caller_info.fee = 0;
				shmp->called_info.valid_time = 6*ONE_MINUTE_STATE_TIME;
				shmp->called_info.fee = 0;
				return 1;
			}
		   switch(shmp->caller_info.status)
		   { 
		      case T_FRESH:
			  case T_SUSPEND:
			  case T_BLACKLIST:
			  case T_EXTERNAL:
			  case T_RELEASED:
				shmp->caller_info.valid_time = 0;
				shmp->called_info.valid_time = 0;
			    shmp->process_info.tcos = CALLER_STATUS_ERROR;
			    shmp->process_info.voiceID = ERROR_ACCOUNT_STATUS;
				return FALSE;
			  break;
			  default:
			  break;
			}
		   	overdraft = GetOverdraft(&shmp->caller_info);
			if(shmp->process_info.bundle_flag )/* bundle mo call charging */
			{
				int left_mo_voice_seconds = 0;
				
				left_mo_voice_seconds = pps_get_account_bundle_mo_voice(shmp);
				if(left_mo_voice_seconds > 0)
				{
					if(left_mo_voice_seconds > shmp->process_info.tariff.unit_time)
						left_mo_voice_seconds = shmp->process_info.tariff.unit_time;
					shmp->caller_info.valid_time = left_mo_voice_seconds*CYCLE_STATE_INTERVAL;

					shmp->caller_info.fee = 0;
					shmp->called_info.fee = 0;
					shmp->called_info.valid_time = shmp->caller_info.valid_time;
					//DebugMsg(debugmib.display.asciiOut, "BillingProc()->%s allocate bundle mo voice[%d]",
					//				shmp->caller_info.number, left_mo_voice_seconds);
					
					return 1;
				}
				else
				{
					/*
					if(shmp->process_info.duration > 1)
					{
						subscribe_bundle_plan_send_used_up_info(shmp->caller_info.number, SMS_BUNDLE_PLAN_VOICE_USEDUP);	
					}
					*/
					
					shmp->process_info.bundle_flag = 0;
				}
			}

			if(shmp->process_info.chargedTime==0 && shmp->process_info.tariff.free_time)
			{
				shmp->caller_info.valid_time = 
					shmp->process_info.tariff.free_time*CYCLE_STATE_INTERVAL;
				shmp->caller_info.fee = 0;
				if(GetBalanceWithPromotionControl(shmp,&shmp->caller_info)+overdraft< getCurrentFee(shmp))
				{
					shmp->caller_info.valid_time = 0;
				}
			}
			else
			{
				if(shmp->process_info.cap_flag>0 && sub_flag0 == 0) /* re-get tariff for cap call during the call */
				{
 					shmp->caller_info.location = GetSubsCapTariffID(shmp,CAP_CALL);
					GetMOTariffData(shmp,&shmp->process_info.tariff, 
							(char *)shmp->charge_info.number, 
							shmp->caller_info.location);
				
				}
			
				sub_flag = shmp->process_info.chargedTime;
				if(shmp->process_info.tariff.free_time)
					sub_flag = shmp->process_info.chargedTime - 1;
			
				if(!sub_flag && overdraft<0){
					overdraft = 0;
				}
				shmp->caller_info.valid_time = 
						GetValidTime( shmp,
									tvnow.tv_sec,
									//shmp->cdr_info.answertime,
									&shmp->process_info.tariff,
									GetBalanceWithPromotionControl(shmp,&shmp->caller_info),
									sub_flag,
									overdraft);

				duration = (int)( shmp->caller_info.valid_time
								/ CYCLE_STATE_INTERVAL);
				shmp->caller_info.fee = TimeToCharge(shmp,&shmp->process_info.tariff, duration,tvnow.tv_sec,sub_flag);
				if(isPoolChildReachFeeLimit(&shmp->caller_info) == 2)
				{
					pps_print_error(shmp->process_info.logMsisdn,"%s child account,reach limited fee",shmp->caller_info.number);
					shmp->caller_info.valid_time = 0;
					shmp->caller_info.fee = 0;
					shmp->called_info.valid_time = 0;		
					shmp->called_info.fee = 0;			
					shmp->process_info.tcos = CALLER_REACH_CHARGE_LIMIT;
					shmp->process_info.pre_warning = 1;
					shmp->process_info.voiceID = PRE_WARNING_LOW_BALANCE;
					return -1;
				}	
			}
			shmp->called_info.fee = 0;
		    shmp->called_info.valid_time = shmp->caller_info.valid_time;
		break;										
        default:
            shmp->caller_info.valid_time = 0;
            shmp->called_info.valid_time = 0;
        break;
    }
	shmp->process_info.chargedTime ++;
    return SUCCESS;
}

int crm_check_if_cug_call(_account_info *caller, _account_info *called)
{
	if(caller == NULL || called == NULL)
		return 0;
	if(caller->cug_id == 0 || called->cug_id == 0)
		return 0;
	if(caller->cug_id != called->cug_id)
		return 0;

	return 1;	
}

extern int ocs_get_crm_free_seconds();
int BillingProc_crm(_state_data *shmp, int sub_flag0)
{
	int          	duration=0,overdraft=0,sub_flag;
	int				cos_id;
	COSTable		*cosTableItem;
	//CosServiceControl	 	*mtServiceCtrItem = NULL;//*serviceControlItem = NULL, 
	char prefix[128], areaName[32]="";
	
	PutLogFunID("BillingProc_crm");
	
	sub_flag = sub_flag0;
	cos_id = shmp->caller_info.cos_id;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	//serviceControlItem = &cosTableItem->serviceControl[0];

	if(shmp->context_info.callType == CT_MT_CALL)
	{
		switch(shmp->called_info.status)
		{ 
			case T_FRESH:
			//case T_SUSPEND:
			case T_BLACKLIST:
			case T_EXTERNAL:
			case T_DISABLE:
			case T_RELEASED:
				shmp->caller_info.valid_time = 0;
				shmp->called_info.valid_time = 0;
				shmp->process_info.tcos = CALLER_STATUS_ERROR;
				shmp->process_info.voiceID = ERROR_ACCOUNT_STATUS;
				return FALSE;
			break;
			default:
			break;
		}
	
		shmp->process_info.tariff_flag = 1;
		shmp->caller_info.valid_time = 12*ONE_MINUTE_STATE_TIME;
		shmp->caller_info.fee = 0;
		shmp->called_info.valid_time = 12*ONE_MINUTE_STATE_TIME;
		shmp->called_info.fee = 0;
		shmp->process_info.call_type = CT_LOCAL_CALL_IN;

		if(ocs_get_charge_prefix( (char *)shmp->caller_info.number, prefix, areaName, &shmp->process_info.tariff.call_type) == 0)
			return 1;
		if(shmp->process_info.tariff.call_type == CT_LOCAL_CALL_OUT)	//to get incoming call type
		{
			shmp->process_info.tariff.call_type = CT_LOCAL_CALL_IN; 
			shmp->process_info.call_type = CT_LOCAL_CALL_IN;
		}
		else if(shmp->process_info.tariff.call_type == CT_TRUNK_CALL_OUT)	
		{
			shmp->process_info.tariff.call_type = CT_TRUNK_CALL_IN; 		
			shmp->process_info.call_type = CT_TRUNK_CALL_IN;
		}
		else if(shmp->process_info.tariff.call_type == CT_IDD_CALL_OUT)	
		{
			shmp->process_info.tariff.call_type = CT_IDD_CALL_IN; 		
			shmp->process_info.call_type = CT_IDD_CALL_IN;
		}
		
		return 1;	

	}
	
	if(shmp->process_info.rest_if.query_tariff_status == 2)
	{
		shmp->process_info.tariff_flag = 1;

		shmp->process_info.tariff.unit_time = shmp->process_info.rest_if.unit;
		shmp->process_info.tariff.tierRating.tierNumber = 0;
		shmp->process_info.tariff.tierRating.rating[0].base_fee = shmp->process_info.rest_if.unit_charge;
		shmp->process_info.tariff.tierRating.rating[0].units = 1;
		shmp->process_info.tariff.tierRating.rating[0].long_distance_fee = 0;
		shmp->process_info.tariff.mini_unit = 1;

	}
	else
	{
		shmp->process_info.tariff_flag = 0;
	}

	switch(shmp->caller_info.status)
	{ 
		case T_FRESH:
		case T_SUSPEND:
		case T_BLACKLIST:
		case T_EXTERNAL:
		case T_DISABLE:
		case T_RELEASED:
			shmp->process_info.tariff_flag = 1;
			
			shmp->caller_info.valid_time = 0;
			shmp->called_info.valid_time = 0;
			shmp->process_info.tcos = CALLER_STATUS_ERROR;
			shmp->process_info.voiceID = ERROR_ACCOUNT_STATUS;
			shmp->process_info.pre_warning = 1;			
			return FALSE;
		break;
		default:
		break;
	}

	if(crm_check_if_cug_call(&shmp->caller_info, &shmp->called_info))
	//if(shmp->caller_info.cug_id>0 && (shmp->caller_info.cug_id == shmp->called_info.cug_id ))
	{
		shmp->process_info.tariff_flag = 1;
		shmp->caller_info.valid_time = 120*ONE_MINUTE_STATE_TIME;
		shmp->caller_info.fee = 0;
		shmp->called_info.valid_time = 120*ONE_MINUTE_STATE_TIME;
		shmp->called_info.fee = 0;
		return 1;	
	}
	
	shmp->caller_info.free_flag = 0;
	
	if(shmp->context_info.callType == CT_MO_CALL &&
		cosTableItem->smsNotificationInquiry[0].smsNotificationWhenBalanceInsufficient)
	{
		if(GetBalanceWithPromotionControl(shmp,&shmp->caller_info)<
			cosTableItem->smsNotificationInquiry[0].insufficientBalanceThreshold)
		{
			shmp->process_info.pre_warning = 1;
			shmp->process_info.voiceID = PRE_WARNING_LOW_BALANCE;
		}
	}
	else
	{
		if ((shmp->process_info.rest_if.total_plan_value> 0) && 
			(shmp->process_info.rest_if.used_plan_value < shmp->process_info.rest_if.total_plan_value))
		{
			if(sub_flag)
			{
				if(shmp->process_info.tariff.call_type != CT_IDD_CALL_OUT)
				{
					int free_secs = ocs_get_crm_free_seconds();
					if(free_secs>0)
					{
						shmp->caller_info.free_flag = 1;
						shmp->caller_info.valid_time = free_secs*CYCLE_STATE_INTERVAL;
						shmp->caller_info.fee = 0;
						shmp->called_info.valid_time = shmp->caller_info.valid_time;
						shmp->called_info.fee = 0;
						return 1;
					}
				}
			}
			
			int left_plan_value = 0;

			left_plan_value = (shmp->process_info.rest_if.total_plan_value - shmp->process_info.rest_if.used_plan_value);
			if(left_plan_value > shmp->process_info.tariff.unit_time)
				left_plan_value = shmp->process_info.tariff.unit_time;
			shmp->caller_info.valid_time = (left_plan_value) * CYCLE_STATE_INTERVAL;
			shmp->caller_info.fee = 0;
			goto billing_crm_ret;
		}

		{
			shmp->caller_info.valid_time = 
					GetValidTime( shmp,
								tvnow.tv_sec,
								&shmp->process_info.tariff,
								GetBalanceWithPromotionControl(shmp,&shmp->caller_info),
								sub_flag,
								overdraft);
			duration = (int)( shmp->caller_info.valid_time/CYCLE_STATE_INTERVAL);
			shmp->caller_info.fee = TimeToCharge(shmp,&shmp->process_info.tariff, duration,tvnow.tv_sec,sub_flag);
		}			

		if(sub_flag && (shmp->caller_info.valid_time) > CYCLE_STATE_INTERVAL)
		{
			if(shmp->process_info.tariff.call_type != CT_IDD_CALL_OUT)
			{
				int free_secs = ocs_get_crm_free_seconds();
				if(free_secs>0)
				{
					shmp->caller_info.free_flag = 1;
					shmp->caller_info.valid_time = free_secs*CYCLE_STATE_INTERVAL;
					shmp->caller_info.fee = 0;
					shmp->called_info.valid_time = shmp->caller_info.valid_time;
					shmp->called_info.fee = 0;
					return 1;
				}
			}
		}
	}

billing_crm_ret:		
	shmp->called_info.fee = 0;
	shmp->called_info.valid_time = shmp->caller_info.valid_time;

	shmp->process_info.chargedTime ++;
	return SUCCESS;
}

extern float ocs_get_calling_card_left_balance(int head, int tail);
int BillingProc_ccc(_state_data *shmp, int sub_flag0)
{
	int          	duration=0,overdraft=0,sub_flag;
	//int				cos_id;
	//COSTable		*cosTableItem;
	//CosServiceControl	 	*serviceControlItem = NULL, *mtServiceCtrItem = NULL;
	int				balance = 0;
	
	PutLogFunID("BillingProc_ccc");
	
	sub_flag = sub_flag0;
	//cos_id = shmp->caller_info.cos_id;
	//cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	//serviceControlItem = &cosTableItem->serviceControl[0];
	shmp->caller_info.location = shmp->caller_info.cos_id;	

	if (sub_flag )
	{
		shmp->process_info.tariff_flag = 
					GetMOTariffData(shmp,&shmp->process_info.tariff, 
							(char *)shmp->charge_info.number, 
							shmp->caller_info.location);
		balance = (int)ocs_get_calling_card_left_balance(shmp->card_info.cardhead, shmp->card_info.cardtail);
		shmp->card_info.balance = balance;
				
	}// if sub_flag
	else
	{
		if(shmp->card_info.balance > shmp->card_info.cc_call_charge)
			balance = shmp->card_info.balance;
		else
			balance = 0;
	}
	
    switch(shmp->context_info.callType){
		case CT_MO_CC_CALL:			
			if( shmp->process_info.callConnected == 1 )
			{
				shmp->process_info.tariff_flag = 1;
				shmp->caller_info.valid_time = 6*ONE_MINUTE_STATE_TIME;
				shmp->caller_info.fee = 0;
				shmp->called_info.valid_time = 6*ONE_MINUTE_STATE_TIME;
				shmp->called_info.fee = 0;
				return 1;
			}
			

			if(shmp->process_info.chargedTime==0 && shmp->process_info.tariff.free_time)
			{
				shmp->caller_info.valid_time = 
					shmp->process_info.tariff.free_time*CYCLE_STATE_INTERVAL;
				shmp->caller_info.fee = 0;
			}
			else
			{
				if(shmp->process_info.cap_flag>0 && sub_flag0 == 0) /* re-get tariff for cap call during the call */
				{
						GetMOTariffData(shmp,&shmp->process_info.tariff, 
								(char *)shmp->charge_info.number, 
								shmp->caller_info.location);
					
				}
			
				sub_flag = shmp->process_info.chargedTime;
				if(shmp->process_info.tariff.free_time)
					sub_flag = shmp->process_info.chargedTime - 1;
			
				shmp->caller_info.valid_time = 
						GetValidTime( shmp,
									tvnow.tv_sec,
									&shmp->process_info.tariff,
									balance,
									sub_flag,
									overdraft);

				duration = (int)( shmp->caller_info.valid_time 	/ CYCLE_STATE_INTERVAL);
				shmp->caller_info.fee = TimeToCharge(shmp,&shmp->process_info.tariff, duration,tvnow.tv_sec,sub_flag);
			}
			shmp->called_info.fee = 0;
		      shmp->called_info.valid_time = shmp->caller_info.valid_time;
		break;										
        default:
            shmp->caller_info.valid_time = 0;
            shmp->called_info.valid_time = 0;
        break;
    }
	shmp->process_info.chargedTime ++;
    return SUCCESS;
}

int IsEnableToCall_ccc(int proc_id)
{
	_state_data		*shmp=(_state_data *) &smshm_ptr->state_data;
	//int				call_type=0,rst;
	//COSTable		*cosTableItem;

	PutLogFunID("IsEnableToCall");

	shmp += proc_id;

	//call_type = shmp->context_info.callType;

   	 if(shmp->process_info.tcos == SRVICE_NOT_SUBSCRIBERED)
        return FALSE;



	if(shmp->process_info.tariff_flag ==0)
	{
		shmp->process_info.tcos = TARIFF_NOT_SET;
		shmp->process_info.voiceID = CALLED_TARIFF_NOT_SET;
		return FALSE;
	}

	if(shmp->caller_info.valid_time <= 0)
	{
		shmp->process_info.tcos = CALLER_CREDIT_OUT;
		shmp->process_info.voiceID = LOW_ACCOUNT_BALANCE;
		return FALSE;
	}	
	return TRUE;
}

void
SetCallType(_state_data *shmp, int type)
{
	switch(type)
	{
		case 0:
			if(shmp->context_info.callType == CT_MT_CALL)
				shmp->process_info.call_type = CT_LOCAL_CALL_IN;
			else
				shmp->process_info.call_type = CT_LOCAL_CALL_OUT;			
		break;
		case 1:
			shmp->process_info.call_type = CT_NDD_CALL_OUT;
		break;
		case 2:
			shmp->process_info.call_type = CT_IDD_CALL_OUT;
		break;
		case 4:
			shmp->process_info.call_type = CT_LOCAL_CALL_IN;
		break;
		case 5:
			shmp->process_info.call_type = CT_TRUNK_CALL_IN;
		break;
		default:
			shmp->process_info.call_type = type;
		break;
	}
}

const int
IsNormalRechargeCard(_state_data *shmp)
{
    u_char       bcd[CONTEXT_BLOCK_SIZE];
    u_long       expiration=0;

	PutLogFunID("IsNormalRechargeCard");

    memcpy(bcd, &shmp->context_info.context[1], sizeof(_RCI_));
    expiration = ((_RCI_ *)bcd)->expiry_date; 
    if (((_RCI_ *) bcd)->card_status == T_FRESH  
                    && (u_long) tvnow.tv_sec < expiration) 
    {
        return TRUE;
    }
    else
	{
        return FALSE;
	}
}
            
int GetRechargeCardFaceValue(_state_data *shmp)
{
    u_char       bcd[CONTEXT_BLOCK_SIZE];

	PutLogFunID("GetRechargeCardFaceValue");

    memcpy(bcd, shmp->context_info.context,//+1 , //sizeof(_RSIR_), 
                    sizeof(_RCI_));
    return  ((_RCI_ *) bcd)->face_value;
}

int GetRechargeCardValidTime(_state_data *shmp)
{
    u_char       bcd[CONTEXT_BLOCK_SIZE];

	PutLogFunID("GetRechargeCardValidTime");

    memcpy(bcd, shmp->context_info.context,//+1, //+sizeof(_RSIR_), 
                    sizeof(_RCI_));
    return  ((_RCI_ *) bcd)->valid_time;
}

static void PureDigitAscii(u_char *string)
{
	int loop,len;
	
	len = strlen((char *)string);
	for(loop=0;loop<len;loop++)
	{
		if(string[loop]<'0' || string[loop]>'9')
		{
			string[loop] = 0;
			break;
		}
	}
	string[loop] = 0;
}

char
GetTariffLacInfo(u_char *cgi)
{
	int loc = -1, loop, matchFlag=0,len;
	u_short cell, lac;
	u_char  MCC[32]={0},MNC[32]={0};
	BasicTariffZone	*pBTZ;
	
	PutLogFunID("GetTariffLacInfo");

	ppsBcdToAsciiR((char *)MCC,cgi,4);	
	PureDigitAscii(MCC);
	
	ppsBcdToAsciiR((char *)MNC,&cgi[2],2);
	PureDigitAscii(MNC);
	
	lac  = cgi[3] * 256 + cgi[4];
	cell = cgi[5] * 256 + cgi[6];

	if(cgi[0]==0 && lac==0 && cell==0) //CF /MT call
		return loc;

	for(loop=0;loop<MaxBasicTariffZoneItem;loop++)
	{
		pBTZ = &ppsParam.ppsTables.basicTariffZone[loop];
		if(pBTZ->existFlag == FALSE)
			continue;
		matchFlag = 0; 
		len = strlen((char *)pBTZ->mcc);
		if(len == 0)
			continue;
		if(memcmp(pBTZ->mcc,MCC,len) == 0)
		{			
			len = strlen((char *)pBTZ->mnc);
			if(len == 0)
			{
				matchFlag = 1;
			}
			else if(memcmp(pBTZ->mnc,MNC,len) ==0 )
			{
			    if(ppsParam.serviceControl.charge_by_lac_flag ==  1)
			    {
			        if(pBTZ->cellId == cell)
			        	matchFlag = 1;
			    }
			    else
			    {
			        if(pBTZ->lac != 0xFFFF)
				{
					if(pBTZ->cellId != 0xFFFF)
					{
						if(pBTZ->cellId == cell 
							&& pBTZ->lac == lac)
						{
							matchFlag = 1;
						}
					}
					else if(pBTZ->lac == lac)
					{
						matchFlag = 1;
					}
				 }
				 else
				 {
					matchFlag = 1;
				 }/* lac */
			    }
			}/* mcc&mnc */

			if(matchFlag)
			{
				return loop;
			}
		}//if mcc
	}/* for */
    return loc;
}


int GetAccountZoneIDByLocationInfo(int index)
{
	return 	ppsParam.ppsTables.basicTariffZone[index].moTariffId%MAX_ZONE_NUM;
}

int GetAccountMTZoneIDByLocationInfo(int index)
{
	return 	ppsParam.ppsTables.basicTariffZone[index].mtTariffId%MAX_ZONE_NUM;
}

void CheckWetherRoamingUser(_state_data *shmp)
{
    _prepaid_data	*user_shmp=&ramshm_ptr->prepaid_data;
	_account_info	*ptr;
	int				loop;

	PutLogFunID("CheckWetherRoamingUser");
	ptr = &shmp->caller_info;

	shmp->process_info.roamingFlag = 1;

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		for(loop=0;loop<MAX_HOME_LOCATION;loop++)
		{
			if(ptr->location == 
				user_shmp->prepaid_info[ptr->head][ptr->tail].home_location[loop])
			{
				shmp->process_info.roamingFlag = 0;
				return;
			}
		}
	}
	pps_print_event(shmp->process_info.logMsisdn,"%s is a MO roaming user",shmp->caller_info.number);
}

void CheckWetherRoamingMTUser(_state_data *shmp)
{
    _prepaid_data	*user_shmp=&ramshm_ptr->prepaid_data;
	_account_info	*ptr;
	int				loop;

	PutLogFunID("CheckWetherRoamingMTUser");
	ptr = &shmp->called_info;

	shmp->process_info.roamingFlag = 1;

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
		for(loop=0;loop<MAX_HOME_LOCATION;loop++)
		{
			if(ptr->location == 
				user_shmp->prepaid_info[ptr->head][ptr->tail].home_location[loop])
			{
				shmp->process_info.roamingFlag = 0;
				return;
			}
		}
	}
	pps_print_event(shmp->process_info.logMsisdn,"%is a MT roaming user",shmp->called_info.number);
}

/* get HPLMN from xapp , 2003-10-23 */
void SetRoamingFlag(_state_data *shmp)
{
	char plmn[32]={""},buf[32]={""};
    int  plmnlen;
	
    u_char CC[32],NDC[32];
    
	PutLogFunID("SetRoamingFlag");

	xap_get_cc(CC);
	xap_get_ndc(NDC);

    BcdToAscii(plmn,CC,4);
	StringCut(plmn,'E');
	BcdToAscii(buf,NDC,6);
	StringCut(buf,'E');
    strcat(plmn,buf);
	plmnlen = strlen(plmn);
	
	
	if(!strncasecmp(shmp->cdr_info.locationNumber,plmn,plmnlen))
	{
		shmp->process_info.roamingFlag = 0;
	}else{
		shmp->process_info.roamingFlag = 1;
	}

}

void
SetLocationInfo(u_char *cgi, u_short proc_id)
{
    _state_data    *shmp=(_state_data *) &smshm_ptr->state_data;

	PutLogFunID("SetLocationInfo");

    shmp += proc_id;
	
	memcpy(shmp->cdr_info.cgi,cgi,7);
	
}

u_long
GetTimeFunnel(u_short hour, u_short minute, u_short second)
{
    int       funnel=0;

	PutLogFunID("GetTimeFunnel");

    funnel = ((hour-GetTmHour()) * 60 * 60 )
            + ((minute - GetTmMinute() ) * 60) 
            + (second - GetTmSecond());
    return (funnel <= 0) ? (funnel+24*60*60)*CYCLE_STATE_INTERVAL 
            : funnel*CYCLE_STATE_INTERVAL;
}

int
IsEnablePeriodicCharge(int head, int tail)
{
    _prepaid_info     *shmp=NULL;

	PutLogFunID("IsEnablePeriodicCharge");

    shmp = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
    if ( shmp->msisdn[MAX_MSISDN_BLEN-1] != 0 
          && ((shmp->status == T_NORMAL )
              || (shmp->status == T_BLACKLIST)
              || (shmp->status == T_SUSPEND ))
           && shmp->mt_expiration_date>=tvnow.tv_sec
           && shmp->rent_type != 0)
        return TRUE;
    else 
        return FALSE;
}

int
IsEnablePeriodicAction(int head, int tail)
{
    _prepaid_info     *shmp=NULL;

	PutLogFunID("IsEnablePeriodicAction");

    shmp = &ramshm_ptr->prepaid_data.prepaid_info[head][tail];
    if ( shmp->msisdn[MAX_MSISDN_BLEN-1] != 0 
         && ((shmp->status == T_NORMAL )
            || (shmp->status == T_BLACKLIST)
            || (shmp->status == T_SUSPEND ))
         && shmp->mt_expiration_date>=tvnow.tv_sec)
        return TRUE;
    else 
        return FALSE;
}

float GetAccountBalance(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetAccountBalance");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
        return (shmp->prepaid_info[head][tail].balance + 
				shmp->prepaid_info[head][tail].promo_balance);
    return FALSE;
}

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

	PutLogFunID("GetAccountVoiceID");

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

}

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

	PutLogFunID("GetUserType");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
        return shmp->prepaid_info[head][tail].rent_type;
    return FAILURE;
}
    
int
GetPeriodicChargeDate(int tariff_id,int id)
{
    _rent_tariff        *shmp=NULL;

	PutLogFunID("GetPeriodicChargeDate");

    if (id < MAX_RENT_TARIFF) {
        shmp = &ramshm_ptr->tariff_data.rent_tariff[tariff_id][id];
        return shmp->charge_date;
    } else
        return 32;
}

int
GetPeriodicTariffFee(int tariff_id,int id)
{
    _rent_tariff        *shmp=NULL;
	int	rent_amount = 0;
	
	PutLogFunID("GetPeriodicTariffFee");

    if (id < MAX_RENT_TARIFF) {
        shmp = &ramshm_ptr->tariff_data.rent_tariff[tariff_id][id];
        rent_amount = (int)shmp->rent_amount;
    } 
    return rent_amount;
}

short
GetPeriodicTariffDate(int tariff_id,int id)
{
    _rent_tariff        *shmp=NULL;

	PutLogFunID("GetPeriodicTariffDate");

    if (id < MAX_RENT_TARIFF) {
        shmp = &ramshm_ptr->tariff_data.rent_tariff[tariff_id][id];
        return shmp->charge_date;
    } else
        return FAILURE;
}

short 
GetPeriodicTariffType(int tariff_id,int id)
{
    _rent_tariff        *shmp=NULL;

	PutLogFunID("GetPeriodicTariffType");

    if (id < MAX_RENT_TARIFF) {
        shmp = &ramshm_ptr->tariff_data.rent_tariff[tariff_id][id];
        return shmp->rent_type;
    } else
        return FAILURE;
}

void
UpdateAccountBalance(int head, int tail, float  charge)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountBalance");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
    {
        shmp->prepaid_info[head][tail].balance = shmp->prepaid_info[head][tail].balance + charge;
	if(shmp->prepaid_info[head][tail].balance<0)
	{
		shmp->prepaid_info[head][tail].balance = 0;
	}
		
    }
}

void UpdateAccountOverdraft(int head, int tail, long overdraft)
{
	_prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountOverdraft");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
        shmp->prepaid_info[head][tail].overdraft = overdraft;
}

void UpdateAccountPromotionBalance(int head, int tail, float charge)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountPromotionBalance");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
        shmp->prepaid_info[head][tail].promo_balance =  shmp->prepaid_info[head][tail].promo_balance+charge;

}

void UpdateAccountPromotionExpiryDay(int head, int tail, int days)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountPromotionExpiryDay");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		if(days>0 )
		{
		    if(shmp->prepaid_info[head][tail].promo_date< tvnow.tv_sec + days*24*3600)
		        shmp->prepaid_info[head][tail].promo_date = tvnow.tv_sec + days*24*3600;
		}
		else
			shmp->prepaid_info[head][tail].promo_date = 
				shmp->prepaid_info[head][tail].mo_expiration_date;
	}
}

void UpdateAccountCUGStatus(_prepaid_info *user_info_ptr, u8 cug_status)
{
	if(user_info_ptr == NULL)
		return;
	user_info_ptr->crm_info.vas_cug_state = cug_status;
}

u_long
GetAccountFirstUsedDate(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetAccountFirstUsedDate");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
        return shmp->prepaid_info[head][tail].first_used_date;
    return FALSE;
}

int MsisdnToLong(u_long *ulmsisdn, char *cmsisdn)
{
    int   len,len1;
	char  tmpbuf[64],buf[64];

	PutLogFunID("MsisdnToLong");

	len = cmsisdn[9];
	if(len>18) len = 18;
	len1 = len;
	if(len %2 !=0) len1 = len + 1;

    BcdToAscii(tmpbuf, (BYTE *)cmsisdn,len1);
    memcpy(buf,tmpbuf,len);
	buf[len] = 0;	
	*ulmsisdn = atol(buf);
	
    return 1;
}

int
GetAccountMsisdnStr(int head, int tail,u_char *msisdn)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
    int len;

	PutLogFunID("GetAccountMsisdn");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
		len = shmp->prepaid_info[head][tail].msisdn[MAX_MSISDN_BLEN-1];
		BcdToAscii((char *)msisdn, shmp->prepaid_info[head][tail].msisdn, (len+1)/2*2);
		msisdn[len]=0;
        	return TRUE; 
	}
    return FALSE;
}

int
GetAccountMsisdn(int head, int tail,u_char *msisdn)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
    int len;

	PutLogFunID("GetAccountMsisdn");

    if (head < MSISDN_HEAD_NUM && tail < UNIT_STORE_ACCOUNT)
	{
	    // MsisdnToLong(&msisdn,shmp->prepaid_info[head][tail].msisdn);
		len = shmp->prepaid_info[head][tail].msisdn[MAX_MSISDN_BLEN-1];
		memcpy(msisdn,shmp->prepaid_info[head][tail].msisdn,MAX_MSISDN_BLEN-1);

		if(len%2==0){
		  msisdn[len/2]=0xFF;
		}else{
		  msisdn[(len)/2] |= 0x0F;
		}
        return TRUE; 
	}
    return FALSE;
}


void
UpdateAccountFirstUsedDate(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
    char             date_buf[32];

	PutLogFunID("UpdateAccountFirstUsedDate");

    GetTmDate(date_buf);
    strcat(date_buf, "000000");
    shmp->prepaid_info[head][tail].first_used_date = 
            MakeTime(NULL, date_buf);
}

void
UpdateAccountStatus(int head, int tail, int status)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountStatus");

    shmp->prepaid_info[head][tail].status = status;
}

void
UpdateAccountRechargeFail(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("UpdateAccountRechargeFail");
    shmp->prepaid_info[head][tail].recharge_fail ++;
}

u_short
GetAccountRechargeFail(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("GetAccountRechargeFail");

    return shmp->prepaid_info[head][tail].recharge_fail;
}

void
ResetAccountRechargeFail(int head, int tail)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;

	PutLogFunID("ResetAccountRechargeFail");

    shmp->prepaid_info[head][tail].recharge_fail = 0;
}

int isAccountExpired(_account_info *ptr)
{
	if(GetExpiration(ptr) < tvnow.tv_sec)
		return 1;
	return 0;

}
/* If the call is valid or invalid 
 0: invalid, 1: valid
 */
int
IsEnableToCall(int proc_id)
{
	_state_data		*shmp=(_state_data *) &smshm_ptr->state_data;
	int				call_type=0,rst;
	COSTable		*cosTableItem;

	PutLogFunID("IsEnableToCall");

	shmp += proc_id;

	call_type = shmp->context_info.callType;

   	 if(shmp->process_info.tcos == SRVICE_NOT_SUBSCRIBERED)
        return FALSE;

	if(call_type == CT_MO_SM)
	{
		rst = isPoolChildReachSMSLimit(shmp);
		if(rst)
		{
			switch(rst)
			{
			case 1:
				shmp->process_info.tcos= CALLER_REACH_SMS_LIMIT;
				break;
			case 2:
				shmp->process_info.tcos= CALLER_REACH_CHARGE_LIMIT;
				break;
			}
			return FALSE;
		}
		
		if ((shmp->caller_info.valid_time > 0 
			&& tvnow.tv_sec <= GetExpiration(&shmp->caller_info)))
		{
			return TRUE;
		}

		if (tvnow.tv_sec > GetExpiration(&shmp->caller_info) ) 
		{
			shmp->process_info.tcos= CALLER_EXPIRIED;
			return FALSE;
		} 
		else if (shmp->caller_info.valid_time <= 0 ) 
		{
			shmp->process_info.tcos= CALLER_CREDIT_OUT;
			return FALSE;
		}
	}

	switch(call_type)
	{
	case CT_MT_CALL:
		if(shmp->called_info.valid_time >0)
		{
			SetCallType(shmp,shmp->process_info.tariff.call_type);

			if(tvnow.tv_sec>=GetIncomingExpiration(&shmp->called_info))
			{
				shmp->process_info.tcos= CALLED_EXPIRIED;
				shmp->process_info.voiceID = MT_EXPIRIED_ACCOUNT;
				return FALSE;
			}
			else
			{
				SetCallType(shmp,shmp->process_info.tariff.call_type);
			}
		}
		else
		{
			if(shmp->called_info.status == T_NORMAL)
			{
				shmp->process_info.tcos = CALLED_CREDIT_OUT;
				shmp->process_info.voiceID = MT_LOW_ACCOUNT_BALANCE;
			}
			else
			{
				shmp->process_info.tcos = CALLED_STATUS_ERROR;
				shmp->process_info.voiceID = MT_ERROR_ACCOUNT_STATUS;
			}
			return FALSE;
		}
		
		if(shmp->process_info.tariff_flag ==0)
		{
			shmp->process_info.tcos = TARIFF_NOT_SET;
			shmp->process_info.voiceID = CALLED_TARIFF_NOT_SET;
			return FALSE;
		}
		break;
		
	case CT_MO_CALL:
	case CT_MF_CALL:
		if(shmp->process_info.tariff_flag ==0)
		{
			shmp->process_info.tcos = TARIFF_NOT_SET;
			shmp->process_info.voiceID = CALLED_TARIFF_NOT_SET;
			return FALSE;
		}

		if(shmp->caller_info.valid_time >0)
		{
			cosTableItem = &ppsParam.ppsTables.cosTable[shmp->caller_info.cos_id];
			if(tvnow.tv_sec>=GetExpiration(&shmp->caller_info))
			{
				if(call_type == CT_MO_CALL)
				{
					shmp->process_info.tcos = CALLER_EXPIRIED;
					shmp->process_info.voiceID = EXPIRIED_ACCOUNT;
				}
				else
				{
					shmp->process_info.tcos= CALLED_EXPIRIED;
					shmp->process_info.voiceID = MT_EXPIRIED_ACCOUNT;
				}
				return FALSE;
			}
			else if((call_type == CT_MO_CALL) &&
					((tvnow.tv_sec+
					(cosTableItem->smsNotificationInquiry[0].theDaysSendingSMSBeforeExpiredDay)*24*3600)
					>=GetExpiration(&shmp->caller_info)))
			{
				shmp->process_info.pre_warning = 1;
				shmp->process_info.voiceID = PRE_WARNING_EXPIRATION_DAY;
			}
			SetCallType(shmp,shmp->process_info.tariff.call_type);
		}
		else
		{
			if(shmp->caller_info.status == T_NORMAL)
			{
				if(call_type == CT_MO_CALL)
				{
					shmp->process_info.tcos = CALLER_CREDIT_OUT;
					shmp->process_info.voiceID = LOW_ACCOUNT_BALANCE;
				}
				else
				{
					shmp->process_info.tcos = CALLED_CREDIT_OUT;
					shmp->process_info.voiceID = MT_LOW_ACCOUNT_BALANCE;
				}
			}
			return FALSE;
		}
		break;
	default:
		shmp->process_info.tcos = 255;
		return FALSE;
		break;
	}
	
	return TRUE;
}

void
SetCSTATimestamp(int instance, u_long timestamp)
{
	PutLogFunID("SetCSTATimestamp");

	pps_csta_buffer.csta_record[instance].timestamp = timestamp;
}

static int callTypeToCstaType(u_short    callType)
{
	switch(callType)
	{
		case CT_LOCAL_CALL_OUT:
		case CT_TRUNK_CALL_OUT:
		case CT_NDD_CALL_OUT:
		case CT_IDD_CALL_OUT:
		case CT_LOCAL_CALL_IN:
		case CT_TRUNK_CALL_IN:
		case CT_PERIODIC_CHARGE:
		case CT_ASSISTED_UP:
		case CT_ASSISTED_DOWN:
		case CT_RECHARGE_SUCCESS:
		case CT_RECHARGE_FAIL:
		case CT_MO_SM:
		case CT_DIRECT_DEBITING:
		case CT_REFUND_ACCOUNT:
		case CT_CONFERENCE_CALL:
		case CT_THIRD_PARTY_CHARGE:
		case CT_EMERGENCY_CALL:
			return callType - PPS_CSTA_TYPE_BASE;
		break;
		case CT_BUNDLE_PLAN_CHARGE:
			return CT_PERIODIC_CHARGE - PPS_CSTA_TYPE_BASE;
		break;
		case CT_GPRS:
		case CT_GPRS_REFUND:			
			return SERVICE_GPRS;
		break;	
		case CT_MO_CC_CALL:
			return SERVICE_CCC;
		default:
			return -1;
		break;
	}
	return -1;
}

int CDRStatistics(_tocdr_report* pbuf,float f_charge,ulong 	gprsVolume)
{
	int		charge=0, duration=0;
	u_short   type;
	char        buf[BUFSIZE];
	int		cstaType;
	
	PutLogFunID("CDRStatistics");

	type =  pbuf->type[0] + pbuf->type[1] * 256;
	memcpy(&charge , pbuf->charge, 4);
	memcpy(&duration , pbuf->duration, 4 );
		
	BcdToAscii(buf, pbuf->releasetime, 14);

	if (type == CT_REFUND_ACCOUNT)
	{
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.number ++;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.charge -= charge;
		reportGprsCDRIncrement(0, -charge, 0);
	}
	else if (type == CT_DIRECT_DEBITING)
	{
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.number ++;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.charge += charge;
		reportGprsCDRIncrement(0, charge, 0);
	}
	
	cstaType = callTypeToCstaType(type);
	if(cstaType<0) 
		return -1;
		
	if(cstaType == SERVICE_GPRS)
	{	
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.gprsVolume += gprsVolume;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.number ++;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.charge += charge;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.duration += duration ;
	}
	else
	{
		pps_csta_buffer.csta_record[csta_old_instance].csta_type[cstaType].number ++;
		pps_csta_buffer.csta_record[csta_old_instance].csta_type[cstaType].charge += charge;
		pps_csta_buffer.csta_record[csta_old_instance].csta_type[cstaType].duration += duration ;
	}
	
	AddPPSStatistics(MIBPPS_CSTANUMBER,0);
#if DEBUG
   if(logMsgFlag & 0x04)
	DebugMsg(debugmib.display.asciiOut, "type:%d charge:%d duration:%d", 
				cstaType, charge, duration);
#endif
	if(cstaType != SERVICE_GPRS)
    		reportCDRIncrement(type, duration, f_charge,gprsVolume);
	else		
		reportGprsCDRIncrement(duration,f_charge,gprsVolume);
	return 1;
}

int GprsCDRStatistics(_tocdr_report* pbuf,float f_charge,ulong 	gprsVolume)
{
	u_long      charge=0, duration=0;
	u_short     type,cstaType;
	char        buf[BUFSIZE];
	
	PutLogFunID("GprsCDRStatistics");

	type =  pbuf->type[0] + pbuf->type[1] * 256;
	memcpy(&charge , pbuf->charge, 4);
	memcpy(&duration , pbuf->duration, 4 );
		
	BcdToAscii(buf, pbuf->releasetime, 14);
	
	cstaType = callTypeToCstaType(type);
	if(cstaType<0) 
		return -1;
	
	if( type == CT_GPRS_REFUND)
	{
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.gprsVolume -= gprsVolume;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.number --;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.charge -= charge;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.duration -= duration ;
		reportGprsCDRIncrement(duration, -f_charge, -gprsVolume);
	}
	else if(cstaType == SERVICE_GPRS)
	{	
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.gprsVolume += gprsVolume;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.number ++;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.charge += charge;
		pps_csta_buffer.csta_record[csta_old_instance].gprsCsta.duration += duration ;
		reportGprsCDRIncrement(duration,f_charge,gprsVolume);
		AddPPSStatistics(MIBPPS_CSTANUMBER,0);
	}	

#if DEBUG
	   if(logMsgFlag & 0x04)
		DebugMsg(debugmib.display.asciiOut, "type:%d charge:%d duration:%d volume:%ld", 
					cstaType, charge, duration,gprsVolume);
#endif

	return 1;	
}

int MsisdnToAscii(char *ascii_in, BYTE  *cmsisdn)
{
	int  len,len1;
	char tmpbuf[64];

	PutLogFunID("MsisdnToAscii");

	len = cmsisdn[MAX_MSISDN_BLEN-1];
	len = len % 19;
       len1 = len;
	if(len%2 !=0) len1 = len + 1;
	BcdToAscii(tmpbuf,cmsisdn,len1);
	memcpy(ascii_in,tmpbuf,len);
	ascii_in[len] = 0;

	return 1;
}

int ppsInitCSTA()
{
	time_t nowSec,nowSec1;

	PutLogFunID("ppsInitCSTA");

	nowSec = nowSec1 = tvnow.tv_sec;
    nowSec = nowSec % (24 * 3600);
	csta_now_instance = \
	csta_old_instance = \
	nowSec / (15 * 60);

	SetCSTATimestamp(csta_old_instance, nowSec1);
	return 1;
}

void 
cstaInstanceCheck()
{
	time_t nowSec,nowSec1;

	PutLogFunID("cstaInstanceCheck");

	nowSec = nowSec1 = tvnow.tv_sec;
    nowSec = nowSec % (24 * 3600);
	csta_now_instance = \
	nowSec / (15 * 60);
    if(csta_old_instance != csta_now_instance)
	{
	   csta_old_instance = csta_now_instance;
	   memset(&pps_csta_buffer.csta_record[csta_now_instance],
				0,
				sizeof(_pps_csta_record));
  	   SetCSTATimestamp(csta_old_instance, nowSec1);
	}
}

int CheckWetherLocalNumber(char *number)
/*return value is the number property,
  0=unknwon, 1=local, 2=national, 3=internationl
*/
{
	char plmn[32]={""},buf[BUFSIZE]={""};
	char ndc[32]={""},cc[32]={""};
    int  start,/*len,*/plmnlen,ndclen,cclen;
    int  retval=3;
	
    u_char CC[32],NDC[32];
    
	PutLogFunID("CheckWetherLocalNumber");

	xap_get_cc(CC);
	xap_get_ndc(NDC);

	//len = strlen(number);
    BcdToAscii(plmn,CC,4);
	StringCut(plmn,'E');
	cclen = strlen(plmn);
	strcpy(cc,plmn);
	BcdToAscii(buf,NDC,6);
	StringCut(buf,'E');
	strcpy(ndc,buf);
	ndclen = strlen(buf);
    strcat(plmn,buf);
	plmnlen = strlen(plmn);

	start = 0;
	memcpy(buf,number,plmnlen);
	buf[plmnlen] = 0;
	
	if(!strncasecmp(buf,cc,cclen))
	{
		if(!strncasecmp(buf,plmn,plmnlen))
		{
	 	 	start += plmnlen;     
	  		retval = 1;
		}
		else
		{
			start += cclen;
			retval = 2;
		}		
	}
	else if(ndclen>0 && !strncasecmp(buf,ndc,ndclen))
	{
	  start += ndclen;
	  retval = 2;
	}

//	if((len-start)<0) start = len;
//	memcpy(buf,&number[start],len-start);
//	buf[len-start] = 0;

//	strcpy(number,buf);
	return retval;
}

int transferIddBillingPrefix(char *number) /* to check if idd/00idd billing prefix need to be tranferred by XAPP */
{
	char	inter_prefix[32];
	char	bufi[32];
	int 	len;
	
	PutLogFunID("transferIddBillingPrefix");

	xap_get_interprefix((u8 *)inter_prefix);

	BcdToAscii(bufi, (BYTE *)inter_prefix,4);
	StringCut(bufi,'E');
	len = strlen(bufi);
	
	if(memcmp(number,bufi,len)) /* no international prefix */
	{
		return 1;
	}
	
	if(ppsParam.serviceControl.transfer_idd_prefix == 0) /* contains internaltion prefix, e.g 00, but not need to transfer it */
	{
		return 0;
	}
	return 1;
}

int  AddNDC(char *number)
{
   char bufndc[32],buf[BUFSIZE];
   char NDC[32];

	PutLogFunID("AddNDC");

   xap_get_ndc((u8 *)NDC);
  
   BcdToAscii(bufndc, (BYTE *)NDC,6);
   StringCut(bufndc,'E');
   strcpy(buf,bufndc);
   strcat(buf,number);
   strcpy(number,buf);

   return 1;
}

int AddCC(char *number)
{
   char bufcc[32],buf[BUFSIZE];
   char CC[32];
   
	PutLogFunID("AddCC");

   xap_get_cc((u8 *)CC);
   BcdToAscii(bufcc, (BYTE *)CC,4);
   StringCut(bufcc,'E');
   strcpy(buf,bufcc);
   strcat(buf,number);
   strcpy(number,buf);

   return 1;
}

int AddInterPrefix(char *number)
{
    char	inter_prefix[32];
	char	bufi[32];
	char	buf[32];

	PutLogFunID("AddInterPrefix");
	xap_get_interprefix((u8 *)inter_prefix);


	BcdToAscii(bufi, (BYTE *)inter_prefix,4);
	StringCut(bufi,'E');
	strcpy(buf,number);
	sprintf(number,"%s%s",bufi,buf);
	return 1;
}

int	AddNationalPrefix(char *number)
{
    char	national_prefix[32];
	char	bufn[32];
	char	buf[32];

	PutLogFunID("AddNationalPrefix");

	xap_get_nationalprefix((u8 *)national_prefix);
	BcdToAscii(bufn, (BYTE *)national_prefix,4);
	StringCut(bufn,'E');
	strcpy(buf,number);
	sprintf(number,"%s%s",bufn,buf);
	return 1;
}

int CutUnknownPrefix(char *number)
{	
	char	buf[BUFSIZE],buf1[BUFSIZE];
	//char idd_prefix[4], bufi[8];
	//char *offset = NULL;
	
	PutLogFunID("CutUnknownPrefix");

	if(number == NULL) 
		return 0;
	
	if(strlen(number)>MAX_ASCII_NUMBER_LEN) 
	{
		pps_print_error(0,
			"CutUnknowPrefix src number %s is too long",number);
		return 0;
	}
	
	strcpy(buf,number);
	if(msisdnUnkToInt(buf,buf1)<=0)
		return 0;
/*
	xap_get_interprefix(idd_prefix);

	BcdToAscii(bufi,idd_prefix,4);
	StringCut(bufi,'E');
	if((offset = strstr(bufi, buf1)) != NULL)
		strcpy(buf, offset);
	else
		strcpy(buf, buf1);
		*/
		strcpy(buf, buf1);
	if(strlen(buf)>MAX_ASCII_NUMBER_LEN) 
	{	
		pps_print_error(0,
			"CutUnknowPrefix manimpulated number %s is too long",buf);
		return 0;
	}
	strcpy(number, buf);
	return 1;
}

int CheckWetherHPLMNUser(char *number)
{
	char bufcc[32],buf[BUFSIZE],bufndc[32];
	char CC[32],NDC[32];
	int		len;

	PutLogFunID("CheckWetherHPLMNUser");

	xap_get_cc((u8 *)CC);
	BcdToAscii(bufcc, (BYTE *)CC,4);
	StringCut(bufcc,'E');

	xap_get_ndc((u8 *)NDC);
	BcdToAscii(bufndc, (BYTE *)NDC,6);
	StringCut(bufndc,'E');

	strcpy(buf,bufcc);
	strcat(buf,bufndc);
	len = strlen(buf);
	if(strncasecmp(buf,number,len)==0)
		return 1;
	return 0;
}

int    CheckSeriveIdentification(char *serviceID,char *servicePWD,u_long srcip,
								int *result,char *cause)
{
	int loop;

	PutLogFunID("CheckSeriveIdentification");

	*result = 0;
	sprintf(cause,"error service ID");
	for(loop=0;loop<MaxThirdPartyInfoItem;loop++)
	{
		if(memcmp(serviceID,
				  ppsParam.ppsTables.thirdPartyInfo[loop].serviceID,
				  strlen(serviceID))==0)
		{
			if(memcmp(servicePWD,
					ppsParam.ppsTables.thirdPartyInfo[loop].servicePassword,
					strlen(servicePWD))==0)
			{
				*result = 1;
				sprintf(cause," ");
				break;
			}
			else
				sprintf(cause,"error password");
		}
	}
	return *result;
}

int  cliManipulationFunction(char *caller, char *called) /* only support IDD string, no prop 91 digits */
	/* caller=8675590123, called=86755234 */
{
	char tmpCaller[64],tmpCalled[64];
	
	if(caller == NULL || called == NULL) 
		return 0;
	if(strlen(caller)>MAX_MSISDN_ALEN || strlen(called)>MAX_MSISDN_ALEN) 
		return 0;
	
	sprintf(tmpCaller,"91%s",caller);
	sprintf(tmpCalled,"91%s",called);	

	cliUptoCalled(tmpCaller,tmpCalled);

	if(strlen(tmpCaller)>MAX_MSISDN_ALEN)
		return 0;

	strcpy(caller,&tmpCaller[2]);
	return strlen(caller);

}

int ppsCheckMTSMSCharging(char *msisdn)
{
    _account_info account_info;
    
    if(msisdn == NULL) return 1;
    if(strlen(msisdn)>=23) return 1;
    
    strcpy((char *)account_info.number, msisdn);
    if( !GetAccountPos(&account_info)) return 1;
    
    account_info.cos_id = GetAccountCosID(&account_info);
    if(ppsParam.ppsTables.cosTable[account_info.cos_id].serviceControl[0].mtSMSCharge)
    {
        if(GetAccountBalance(account_info.head, account_info.tail)>=
        	ppsParam.ppsTables.cosTable[account_info.cos_id].serviceControl[0].mtSMSCharge)
        	return 1;
        else
        	return 0;
    }
    
    return 1;
}

int pps_get_ro_voice_quota(_state_data *shmp)
{
	int	retval = 0;
	
	if(shmp->process_info.tariff_flag == 0)
		return 0;
	if(shmp->caller_info.status != T_NORMAL)
		return 0;
	if(GetExpiration(&shmp->caller_info) < tvnow.tv_sec)
		return 0;
	
	retval = GetMaxCallDuration(shmp);

	return retval;
}


static int pps_update_last_ro_quota_type(_state_data *shmp, u_char new_type, u32 new_quota)// for Ro refund
{
	if (shmp->process_info.ro_if.last_ro_quota_type == 0)// bundle
	{
		if (new_type == 0)// bundle
		{
			shmp->process_info.ro_if.last_bundle_duration += new_quota;
		}
		else
		{
			shmp->process_info.ro_if.last_cap_duration = new_quota;
			shmp->process_info.ro_if.last_ro_quota_type = 1;
		}
	}
	else
	{
		if (new_type == 0)// bundle
		{
			shmp->process_info.ro_if.last_bundle_duration = new_quota;
			shmp->process_info.ro_if.last_ro_quota_type = 0;
		}
		else
		{
			shmp->process_info.ro_if.last_cap_duration += new_quota;
		}
	}

	return 0;
}


// need_assigned_duration: Param IN and OUT; new_assigned_bundle, Param OUT;
float pps_billing_ro_voice(_state_data *shmp, u32 *assigned_duration, u32 *new_assigned_bundle)
{
	float charge = 0.0, balance = 0.0;
	_bill_tariff *bt_ptr;
	int	discount, left_bundle, left_duration_to_assign = 0;

	bt_ptr = &shmp->process_info.tariff;
	discount = GetDiscount(tvnow.tv_sec, bt_ptr->discountID);

	if(shmp->process_info.serviceType == SRV_NORMAL_MO_CALL ||
		shmp->process_info.serviceType == SRV_NORMAL_MF_CALL)	// Get left bundle
	{
		left_bundle = pps_get_account_bundle_mo_voice(shmp);
		balance = GetAccountBalance(shmp->caller_info.head, shmp->caller_info.tail);
	}
	else
	{
		left_bundle = pps_get_account_bundle_mt_voice(shmp);
		balance = GetAccountBalance(shmp->called_info.head, shmp->called_info.tail);
	}

	// allocation of the first time
	if (shmp->process_info.chargedTime == 0)
	{
		if (left_bundle > 0)
		{
			// allocate bunle first, and then balance
			if (left_bundle >= *assigned_duration)// enough bundle for first quota
			{
				*new_assigned_bundle = *assigned_duration;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				goto out;
			}
			else
			{
				*new_assigned_bundle = left_bundle;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				left_duration_to_assign = *assigned_duration - left_bundle;// have to allocate by balance to meet this quota
				*assigned_duration = left_bundle;
			}
		}
		else
		{
			// check if balance enough for the min charge unit
			if(bt_ptr->unit_time == 0 || bt_ptr->mini_unit == 0)// error param, can't allocate duratioon
			{
				*assigned_duration = 0;
				pps_update_last_ro_quota_type(shmp, 1, 0);
				goto out;
			}
	
			charge = (bt_ptr->mini_unit * 1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0;
			charge += bt_ptr->adjustment;
			if (charge <= balance)// have enough balance for the first basic charge
			{
				int basic_time = (bt_ptr->mini_unit * bt_ptr->unit_time) + bt_ptr->free_time;
				if (basic_time >= *assigned_duration)// basic time is enough for this quota
				{
					*assigned_duration = basic_time;
					pps_update_last_ro_quota_type(shmp, 1, 0);// basic time don't have to refund
					goto out;
				}
				else
				{
					left_duration_to_assign = *assigned_duration - basic_time;// have to allocate more by balance to meet this quota
					*assigned_duration = basic_time;
					pps_update_last_ro_quota_type(shmp, 1, 0);// basic time don't have to refund
				}
			}
			else
			{
				*assigned_duration = 0;
				pps_update_last_ro_quota_type(shmp, 1, 0);
				goto out;
			}
		}
	}
	else
	{
		if (left_bundle > 0)
		{
			// allocate bunle first, and then balance
			if (left_bundle >= *assigned_duration)// enough bundle for first quota
			{
				*new_assigned_bundle = *assigned_duration;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				goto out;
			}
			else
			{
				*new_assigned_bundle = left_bundle;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				left_duration_to_assign = *assigned_duration - left_bundle;// have to allocate by balance to meet this quota
				*assigned_duration = left_bundle;
			}
		}
		else
		{
			int units = 0;
			
			// calculate how many unit have to allocate
			units = (*assigned_duration + bt_ptr->unit_time -1) / bt_ptr->unit_time;
			
			// check if balance enough for the min charge unit
			charge = (units * 1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0;
			if (charge <= balance)// have enough balance for the first basic charge
			{
				*assigned_duration = units * bt_ptr->unit_time;
				pps_update_last_ro_quota_type(shmp, 1, *assigned_duration);
				goto out;
			}
			else// have no enough balance, so that assign as much unit as it could
			{
				units = balance / ((1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0);
				if (units > 0)
				{
					charge = (units * 1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0;
					*assigned_duration = units * bt_ptr->unit_time;
					pps_update_last_ro_quota_type(shmp, 1, *assigned_duration);
				}
				else
				{
					*assigned_duration = 0;
				}
				goto out;
			}
		}
	}
	//

	// have to allocate more quota by balance
	if (left_duration_to_assign > 0)
	{
		int units = 0;
		float temp_charge = 0.0;
		
		// calculate how many unit have to allocate
		units = (left_duration_to_assign + bt_ptr->unit_time -1) / bt_ptr->unit_time;
		
		// check if balance enough for the min charge unit
		temp_charge = (units * 1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0;
		if ((temp_charge + charge) <= balance)// have enough balance for the first basic charge
		{
			charge += temp_charge;
			*assigned_duration += units * bt_ptr->unit_time;
			pps_update_last_ro_quota_type(shmp, 1, units * bt_ptr->unit_time);
			goto out;
		}
		else// have no enough balance, so that assign as much unit as it could
		{
			units = (balance - charge) / ((1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0);
			if (units > 0)
			{
				charge += (units * 1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0;
				*assigned_duration += units * bt_ptr->unit_time;
				pps_update_last_ro_quota_type(shmp, 1, units * bt_ptr->unit_time);
			}
			goto out;
		}
	}
	//
	
	if(0)
       DebugMsg(debugmib.display.asciiOut, " %s:%d , tariff: unit=%d, %.2f+%.2f, assigned_duration=%d, , discount=%d ,charge=%.2f",
	   	__FUNCTION__, __LINE__,
	   	bt_ptr->unit_time ,
	   	bt_ptr->tierRating.rating[0].base_fee,
	   	bt_ptr->tierRating.rating[0].long_distance_fee,
	   	*assigned_duration,
	   	discount,
	   	charge
	   	);

out:	
	shmp->process_info.chargedTime ++;
	
	return charge;
}


float pps_billing_ro_voice_with_crm(_state_data *shmp, u32 *assigned_duration, u32 *new_assigned_bundle)
{
	float charge = 0.0, balance = 0.0;
	int	discount, left_bundle, left_duration_to_assign = 0;

	discount = shmp->process_info.rest_if.tariff_discount;

	left_bundle = shmp->process_info.rest_if.total_plan_value - shmp->process_info.rest_if.used_plan_value;
	if(shmp->process_info.serviceType == SRV_NORMAL_MO_CALL ||
		shmp->process_info.serviceType == SRV_NORMAL_MF_CALL)
	{
		balance = GetAccountBalance(shmp->caller_info.head, shmp->caller_info.tail);
	}
	else
	{
		balance = GetAccountBalance(shmp->called_info.head, shmp->called_info.tail);
	}

	// allocation of the first time
	if (0)//(shmp->process_info.chargedTime == 0)
	{
		if (left_bundle > 0)
		{
			// allocate bunle first, and then balance
			if (left_bundle >= *assigned_duration)// enough bundle for first quota
			{
				*new_assigned_bundle = *assigned_duration;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				goto out;
			}
			else
			{
				*new_assigned_bundle = left_bundle;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				left_duration_to_assign = *assigned_duration - left_bundle;// have to allocate by balance to meet this quota
				*assigned_duration = left_bundle;
			}
		}
		else
		{
			// check if balance enough for the min charge unit
			if(shmp->process_info.rest_if.unit == 0)// error param, can't allocate duratioon
			{
				*assigned_duration = 0;
				pps_update_last_ro_quota_type(shmp, 1, 0);
				goto out;
			}
	
			charge = ((*assigned_duration / shmp->process_info.rest_if.unit) * 1.0 * ( shmp->process_info.rest_if.unit_charge)) * discount / 100.0;
			if (charge <= balance)// have enough balance for the first basic charge
			{
				pps_update_last_ro_quota_type(shmp, 1, 0);// basic time don't have to refund
				goto out;
			}
			else
			{
				if (balance >= shmp->process_info.rest_if.unit_charge)// last unit?
				{
					charge = balance;
					*assigned_duration = shmp->process_info.rest_if.unit;
					pps_update_last_ro_quota_type(shmp, 1, 0);
					goto out;
				}
				else
				{
					*assigned_duration = 0;
					pps_update_last_ro_quota_type(shmp, 1, 0);
					goto out;
				}
			}
		}
	}
	else
	{
		if (left_bundle > 0)
		{
			// allocate bunle first, and then balance
			if (left_bundle >= *assigned_duration)// enough bundle for first quota
			{
				*new_assigned_bundle = *assigned_duration;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				goto out;
			}
			else
			{
				*new_assigned_bundle = left_bundle;
				pps_update_last_ro_quota_type(shmp, 0, *new_assigned_bundle);
				left_duration_to_assign = *assigned_duration - left_bundle;// have to allocate by balance to meet this quota
				*assigned_duration = left_bundle;
			}
			
			if((logMsgFlag & 0x04) || (logMsgFlag & 0x02) || shmp->process_info.logMsisdn)
			   DebugMsg(debugmib.display.asciiOut, " %s:%d, left bundle[%d], assigned=[%d], need assigned by balance=[%d]",
				__FUNCTION__, __LINE__,
				left_bundle,
				*assigned_duration, 
				left_duration_to_assign
				);
		}
		else
		{
			int units = 0;
			
			// calculate how many unit have to allocate
			units = (*assigned_duration + shmp->process_info.rest_if.unit -1) / shmp->process_info.rest_if.unit;
			
			// check if balance enough for the min charge unit
			charge = (units * 1.0 * ( shmp->process_info.rest_if.unit_charge)) * discount / 100.0;
			if (charge <= balance)// have enough balance for the first basic charge
			{
				*assigned_duration = units * shmp->process_info.rest_if.unit;
				pps_update_last_ro_quota_type(shmp, 1, *assigned_duration);
				goto out;
			}
			else// have no enough balance, so that assign as much unit as it could
			{
				units = balance / ((1.0 * ( shmp->process_info.rest_if.unit_charge)) * discount / 100.0);
				if (units > 0)
				{
					charge = (units * 1.0 * (shmp->process_info.rest_if.unit_charge)) * discount / 100.0;
					*assigned_duration = units * shmp->process_info.rest_if.unit;
					pps_update_last_ro_quota_type(shmp, 1, *assigned_duration);
				}
				else
				{
					*assigned_duration = 0;
					charge = 0.0;
				}
				if((logMsgFlag & 0x04) || (logMsgFlag & 0x02) || shmp->process_info.logMsisdn)
				   DebugMsg(debugmib.display.asciiOut, " %s:%d, no enough balance: units[%d], assigned=[%d], balance=%.2f, charge=%.2f",
					__FUNCTION__, __LINE__,
					units,
					*assigned_duration, 
					balance, charge
					);
				
				goto out;
			}
		}
	}
	//

	// have to allocate more quota by balance
	if (left_duration_to_assign > 0)
	{
		int units = 0;
		float temp_charge = 0.0;
		
		// calculate how many unit have to allocate
		units = (left_duration_to_assign + shmp->process_info.rest_if.unit -1) / shmp->process_info.rest_if.unit;
		
		// check if balance enough for the min charge unit
		temp_charge = (units * 1.0 * ( shmp->process_info.rest_if.unit_charge)) * discount / 100.0;
		if ((temp_charge + charge) <= balance)// have enough balance for the first basic charge
		{
			charge += temp_charge;
			*assigned_duration += units * shmp->process_info.rest_if.unit;
			pps_update_last_ro_quota_type(shmp, 1, units * shmp->process_info.rest_if.unit);
			goto out;
		}
		else// have no enough balance, so that assign as much unit as it could
		{
			units = (balance - charge) / ((1.0 * ( shmp->process_info.rest_if.unit_charge)) * discount / 100.0);
			if (units > 0)
			{
				charge += (units * 1.0 * ( shmp->process_info.rest_if.unit_charge)) * discount / 100.0;
				*assigned_duration += units * shmp->process_info.rest_if.unit;
				pps_update_last_ro_quota_type(shmp, 1, units * shmp->process_info.rest_if.unit);
			}
			goto out;
		}
	}
	//
	
out:
	if((logMsgFlag & 0x04) || (logMsgFlag & 0x02) || shmp->process_info.logMsisdn)
       DebugMsg(debugmib.display.asciiOut, " %s:%d, tariff: unit=%d*%.2f, assigned/bundle/cap=[%d/%d/%d], discount=%d, charge=%.2f",
	   	__FUNCTION__, __LINE__,
	   	shmp->process_info.rest_if.unit,
	   	shmp->process_info.rest_if.unit_charge,
	   	*assigned_duration, *new_assigned_bundle, left_duration_to_assign,
	   	discount,
	   	charge
	   	);

	shmp->process_info.chargedTime ++;
	
	return charge;
}

float pps_refund_ro_voice_with_crm(_state_data *shmp, u32 refund_duration, u32 *refund_bundle)
{
	float charge = 0.0;
	u32 bundle_duration = 0, cap_duration = 0;
	int	discount;

	discount = shmp->process_info.rest_if.tariff_discount;

	if (shmp->process_info.ro_if.last_ro_quota_type == 0)// bundle
	{
		if (refund_duration <= shmp->process_info.ro_if.last_bundle_duration)
		{
			// all refund to bundle
			bundle_duration = refund_duration;
		}
		else
		{
			bundle_duration = shmp->process_info.ro_if.last_bundle_duration;
			cap_duration = refund_duration - bundle_duration;
		}
	}
	else
	{
		if (refund_duration <= shmp->process_info.ro_if.last_cap_duration)
		{
			// all refund to balance
			cap_duration = refund_duration;
		}
		else
		{
			cap_duration = shmp->process_info.ro_if.last_cap_duration;
			bundle_duration = refund_duration - cap_duration;
		}
	}
	
	*refund_bundle = bundle_duration;
	if ((shmp->process_info.rest_if.unit > 0) && (cap_duration >= shmp->process_info.rest_if.unit))
	{
		int units = 0;
		units = cap_duration / shmp->process_info.rest_if.unit;
		charge = (units * 1.0 * ( shmp->process_info.rest_if.unit_charge)) * discount / 100.0;
	}

	return charge;
}


float pps_refund_ro_voice(_state_data *shmp, u32 refund_duration, u32 *refund_bundle)
{
	float charge = 0.0;
	_bill_tariff *bt_ptr;
	u32 bundle_duration = 0, cap_duration = 0;
	int	discount;

	bt_ptr = &shmp->process_info.tariff;
	discount = GetDiscount(tvnow.tv_sec, bt_ptr->discountID);

	if (shmp->process_info.ro_if.last_ro_quota_type == 0)// bundle
	{
		if (refund_duration <= shmp->process_info.ro_if.last_bundle_duration)
		{
			// all refund to bundle
			bundle_duration = refund_duration;
		}
		else
		{
			bundle_duration = shmp->process_info.ro_if.last_bundle_duration;
			cap_duration = refund_duration - bundle_duration;
		}
	}
	else
	{
		if (refund_duration <= shmp->process_info.ro_if.last_cap_duration)
		{
			// all refund to balance
			cap_duration = refund_duration;
		}
		else
		{
			cap_duration = shmp->process_info.ro_if.last_cap_duration;
			bundle_duration = refund_duration - cap_duration;
		}
	}
	
	*refund_bundle = bundle_duration;
	if (bt_ptr->unit_time == 0)
	{
		bt_ptr->unit_time = 60;
	}
	if (cap_duration >= bt_ptr->unit_time)
	{
		int units = 0;
		units = cap_duration / bt_ptr->unit_time;
		charge = (units * 1.0 * ( bt_ptr->tierRating.rating[0].base_fee + bt_ptr->tierRating.rating[0].long_distance_fee)) * discount / 100.0;
	}

	return charge;
}

/*-----------CRM related functions --------------------------------*/
int pps_update_user_info_for_crm_create_account(_prepaid_info *user_ptr, 
			int customer_id, int account_id, int product_id, int plan_id, int balance, int expiry_day, int rent_charge,  int birthday, int cug_id)
{
	if(user_ptr == NULL)
		return 0;
#ifdef OCS_WITH_CRM
	user_ptr->balance = balance;
	user_ptr->mo_expiration_date = user_ptr->mt_expiration_date = expiry_day;

	user_ptr->crm_info.customer_id = customer_id;
	user_ptr->crm_info.account_id  = account_id;
	user_ptr->crm_info.product_id   = product_id;
	user_ptr->crm_info.base_plan_id  = plan_id;
	user_ptr->crm_info.rent_charge = rent_charge;	
	user_ptr->crm_info.birthday       = birthday;
	user_ptr->cug_id = cug_id;
	user_ptr->status = T_NORMAL;
#endif
	return 1;
}
