#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../include/includes.h"

#include "../include/ppsm.h"
#include "../include/param.h"
#include "../include/ramdata.h"
#include "../include/message.h"
#include "../include/mfunc.h"
//#include "../include/ppsMibTable.h"

#include    "../../../../plat/smpp/src/include/smpp.h"
/*----------------------------------------------------------*/

extern 	int 	SendAccountInfoBySMPP();
extern	int 	RechargeCardRequest(u_short proc_id,char *card_hrn, int *result);
extern 	int 	RechargeRequestProc(u_short portid);

extern 	int 	saveCMBSMSACK(u_short portid,u_char *sms);
extern 	int		callMebackSMSProc(u_short portid);
extern int switchServiceProc(u_short portid, u_char *shortMsg);
extern	int 	saveTransferSMSACK(u_short portid,u_char *sms);
extern 	int 	transferAccountSMSProc(u_short portid);

extern	PPSParamStruct	ppsParam;
extern char logMsisdn[18];

#define MAX_SMPP_MSG		128
#define MAX_SMPP_LINK_SET	4

#define	PROMPT_SM_DELAY_TIME	20 //seconds delay to send SM promtp
/*---------------------------------------------------------*/
int smppSendMOSM(char *caller, char *called, u_char *msg, int msgLen,u_char dataCoding);


typedef enum _SMPP_DIR
{
   _SMPP_SEND,
   _SMPP_RECV,
}SMPP_DIR;

typedef enum _SMPP_LINK_STATUS
{
   _SMPP_UNUSED=0,
   _SMPP_USED,
}SMPP_LINK_STATUS;

typedef struct _SMPP_STATUS
{
  int linkNo[MAX_SMPP_LINK_SET];
  int link_state[MAX_SMPP_LINK_SET];
  int dir[MAX_SMPP_LINK_SET];
}SMPP_STATUS;
/* index=0/1/2=Recharge Tx, Inquiry Tx, Recharge Rx */

SMPP_STATUS smpp_status;

typedef struct _smpp_recv_buf
{
  struct  SMPP_MSG rSM_msg;
}smpp_recv_buf;

typedef struct _smpp_send_buf
{
  struct  SMPP_MSG sSM_msg[MAX_SMPP_MSG];
  u_long  submitTime[MAX_SMPP_MSG];	
  u_short head;
  u_short tail;
}smpp_send_buf;

typedef struct _pps_smpp_msg_buf{
	smpp_send_buf    ppsSMPPsBufRecharge;
	smpp_send_buf    ppsSMPPsBufInquiry;
}pps_smpp_msg_buf;

pps_smpp_msg_buf    ppsSMPPmsgBuf;
static int	smpp_link_selection = 0;
/*---------------------------------------------------------*/
extern int				logMsgFlag;
extern u_short			debug_messages;
extern _ram_data		*ramshm_ptr;
extern _state_machine	*smshm_ptr;
extern u_char			alarm_code[2];
/*---------------------------------------------------------*/
static	int	pps_smppOnDataRecv(BYTE linkNo, SMPP_MSG *pdaa, WORD dataLen);
static	int	pps_smppOnLinkStateChange(BYTE linkNo, BYTE state);
extern int GetAccountLastCallInfo(_account_info *ptr, _last_call_info *pInfo);
extern void SendAccountInfoBySMPPtoThirdParty(_account_info *ptr, char *called, char *third_party_number);
extern int getTransferInfo(u_short portid,u_char *ussd_data);
/*---------------------------------------------------------*/
int smppEncodeSM(char *asciiIn, u_char *bcdOut)
{

	int i,len,n,cur,index=0;
	unsigned char mid1,mid2;
	char  strIn[180];

	n=0;

	len=strlen(asciiIn);
	if(len < 1)
	{
		return 0;
	}

	if(len > 160)
	{
		len=160;
		memcpy(strIn, asciiIn, len);
		strIn[160] = 0;
	}
	else
	{
		strcpy(strIn,asciiIn);
	}
	
	for(i=0;i<len;i++)
	{
		switch(strIn[i])
		{
			case '@':
				strIn[i] = 0x00;
			break;
			case '$':
				strIn[i] = 0x02;
			break;
			case '_':
				strIn[i] = 0x11;
			break;
			case '[':
				strIn[i] = 0x28;
			break;
			case ']':
				strIn[i] = 0x29;
			break;
			case '/':
				strIn[i] = 0x2F;
			break;
			case '`':
				strIn[i] = 0x27;
			break;
			case '{':
				strIn[i] = 0x28;
			break;
			case '}':
				strIn[i] = 0x92;
			break;
			default:
			break;
		}
	}

	for(i=0;i<len;i++)
	{
		if(i<len-1)
		{
			mid1=strIn[i];
			mid2=strIn[i+1];
			cur=(mid1>>n) |((mid2<<(7-n))&0xff);
		}
		else
		{
			mid1=strIn[i];
			if(n==6)
			{
				mid2 = 0x0d; /* ussd filler, isn't the same as SMS, refer to gsm03.38 */
			}
			else
			{
				mid2 = 0x00;
			}			
			cur=(mid1>>n) | ((mid2<<(7-n))&0xff);
		}
		bcdOut[index++]=cur;
		n=(n+1)%7;
		if(n==0 ) i++;
	}

	bcdOut[index]='\0';
	return index;
}
int smppDecodeSM(char *strIn, char *strOut,int length)
{
  int i,n,j;
  int len;
  unsigned char mid1,mid2,cur;
  unsigned char strHex[512];
     
  PutLogFunID("smppDecodeSM");

  n=0;j=0;
  //len = length - length/8;
  len = length;
  if(len>160) len = 160;

  memcpy(strHex,strIn,len);    
    
  for(i=0;i<=len;i++)
  {
    if(i>0)
    {
      mid1=strHex[i];
      mid2=strHex[i-1];
      cur=((mid1<<n)&0x7f) |(mid2>>(8-n));      
    }
    else
    {
       mid1=strHex[i]&0x7f;
       cur=mid1;
    }
    strOut[j++] = cur;
    n =(n+1)%8;    
    if(n==0) i--;
  }  
  
  for(i=0;i<j;i++)
  {
    switch(strOut[i])
    {
    	case 0x02:
    		strOut[i] = '$';
    	break;
    	case 0x00:
    		strOut[i] = '@';
    	break;
    	case 0x11:
    		strOut[i] = '_';
    	break;
    	case 0x2F:
    		strOut[i] = '/';
    	break;
    	default:
    	break;  
    }  
  }  
  strOut[length*8/7] = 0;

  return 1;
}

int smppEncodeASCtoUC2(char *asciiIn, u_char *bcdOut,int orgLen)
{

	int i,len,index=0;
	unsigned char mid1;
	char  strIn[180];

	memcpy(strIn,asciiIn,orgLen);

	len = orgLen;

	if(len>70 ) len=70;
	if(len<1)  return 0;

	index = 0;
	for(i=0;i<len;i++)
	{		
		mid1=strIn[i];
		if(mid1<0x80)
		{
			bcdOut[index++] = 0x00;
			bcdOut[index++] = mid1;
		}
		else
		{
			bcdOut[index++] = 0xc0 | (mid1>>6);
			bcdOut[index++] = 0x80 | (mid1 & 0x3f);
		}
	}

	bcdOut[index]='\0';
	return index;
}

/*---------------------------------------------------------------*/
int isValidMsisdn(char *msisdn)
{
    int len = strlen(msisdn);
    int i;
    for (i=0; i<len; i++)
    {
        if (!isdigit(msisdn[i]))
        {
            return 0;
        }
    }
    return 1;
}

int DecodeDeliverSM(SMPP_MSG *pMsg)
{
    //caller number,
	//called number
	//context
	char	short_message[1024];
	char 	expire_date[64],buf[1024];
	int	proc_id,action,sms_len=0, len=0;
	int	instance, cos_id,result,maxThreshold = 0;
	_state_data 	*shmp;
	COSTable	*cosTableItem;
	
	PutLogFunID("DecodeDeliverSM");

	smppDecodeSM((char *)pMsg->pdu.deliver_sm.short_message,
				short_message,
				pMsg->pdu.deliver_sm.sm_length);
	sms_len = strlen(short_message);
	if((pMsg->pdu.deliver_sm.sm_length %7 == 0) && (sms_len > 0))
	{
		if(short_message[sms_len-1] == '@') /* the last char can't be '@', it's a padding byte */
			short_message[sms_len-1] = '\0';
	}


	proc_id = AssignProcID(0);
	if(proc_id<0) return 0;
	
	shmp = (_state_data *)&smshm_ptr->state_data;
   	shmp += proc_id;
   	shmp->process_info.portid = proc_id;
	shmp->process_info.chargeOther = 0;

   	strcpy((char *)shmp->caller_info.number,pMsg->pdu.deliver_sm.source_addr);	
	strcpy((char *)shmp->called_info.number,pMsg->pdu.deliver_sm.destination_addr);
	strcpy((char *)shmp->origin_info.number,pMsg->pdu.deliver_sm.destination_addr);

	
    	shmp->cdr_info.seizuretime = time(NULL);
    	shmp->cdr_info.answertime = time(NULL);


	len = strlen((char *)shmp->called_info.number);
	strcpy(buf, (char *)shmp->called_info.number);
	if(len %2 ==1){
		strcat(buf,"F");
		len ++;
	}
	strcat(buf,"FF");
	ppsAsciiToBcdR(shmp->cdr_info.called, buf, len+2);

	if((logMsgFlag & 0x02)   && debug_messages<=MAX_DEBUG_MESSAGES)
	{
		DebugMsgDelimiter();
		DebugMsg(debugmib.display.asciiOut,
       				"Recv SM from %s to %s through SMPP : %s\n",
       				pMsg->pdu.deliver_sm.source_addr,
       				shmp->called_info.number,
       				short_message);
	}
	
	shmp->process_info.all_in_one_input_flag = 0;	
	shmp->context_info.callType = CT_MO_SM;
	action = GetServiceType_0(shmp);	

	if((logMsgFlag & 0x04) || (logMsgFlag & 0x02) || shmp->process_info.logMsisdn)
	{  

		DebugMsg(debugmib.display.asciiOut,
       				"%s, Service number: %s, action = %d\n", __FUNCTION__,
       				shmp->called_info.number, action);
	}
	
	if((action == SRV_SCRATCH_CARD_RECHARGE && sms_len > 16 ) ||
		(action == SRV_BUNDLE_PLAN && sms_len >6))
	{//the operation is: xx*MSISDN, minimum length of MSISDN is 5 digits
		int offset = 0;
		char *delimeter_ptr = NULL;

		if((delimeter_ptr = strstr(short_message, "*") ) != NULL)
		{
			offset = delimeter_ptr - short_message;
			if((sms_len - offset)>5)
			{
				strcpy((char *)shmp->origin_info.number, (char *)shmp->caller_info.number);
				strncpy((char *)shmp->caller_info.number, &short_message[offset+1], MAX_ASCII_NUMBER_LEN-1);
                shmp->caller_info.number[MAX_ASCII_NUMBER_LEN-1] = '\0';
				short_message[offset] = '\0';
				shmp->process_info.chargeOther = 1;
				
				if((logMsgFlag & 0x04) || (logMsgFlag & 0x02) || shmp->process_info.logMsisdn)
				{  

					DebugMsg(debugmib.display.asciiOut,
			       				"[%04d], by 3rd party, real caller is: %s, sms=%s\r\n", proc_id,
			       				shmp->caller_info.number,
			       				short_message);
				}

				if(isValidMsisdn((char *)shmp->caller_info.number) <= 0)
				{
                    if((logMsgFlag & 0x04) || (logMsgFlag & 0x02) || shmp->process_info.logMsisdn)
                    {  
                        DebugMsg(debugmib.display.asciiOut,
                                    "[%04d], invalid real caller=%s\r\n", proc_id,
                                    shmp->caller_info.number);
                    }
                    return 0;
				}
			}
		}
		
	}

	
	memset(shmp->caller_info.IMSI, 0xFF, 8);

	len = strlen((char *)shmp->caller_info.number);
	strcpy(buf, (char *)shmp->caller_info.number);
	if(len %2 ==1){
		strcat(buf,"F");
		len ++;
	}
	strcat(buf,"FF");
	ppsAsciiToBcdR(shmp->cdr_info.caller,buf,len+2);
	memcpy(shmp->cdr_info.msisdn, shmp->cdr_info.caller,12);

	/*
	len = strlen(short_message);
	strcpy(buf,short_message);
	strcat(buf,"F");
	ppsAsciiToBcdR(shmp->cdr_info.called,buf,len+2);
	*/
	
	
	GetAccountPos(&shmp->caller_info);
	cos_id = GetAccountCosID(&shmp->caller_info);

	shmp->caller_info.status = GetAccountStatus(&shmp->caller_info);
	shmp->caller_info.cos_id = cos_id;

    if(!pps_get_adaptor_flag())
    {
        if(shmp->caller_info.head>=MSISDN_HEAD_NUM  ||
          shmp->caller_info.status == T_BLACKLIST ||
          shmp->caller_info.status == T_RELEASED)
        {
            DebugMsg(debugmib.display.asciiOut,
                "Unused caller for SM recharging/inqurying");
            ReleasePortStatus(proc_id);
            return 0;
        }
    }
    
	if(logMsgFlag & 0x08)
	{
	 	if(strstr((char *)shmp->caller_info.number,logMsisdn))
		{
			shmp->process_info.logMsisdn = 0x01;
		}
		if(strstr((char *)shmp->called_info.number,logMsisdn))
		{
			shmp->process_info.logMsisdn = 0x01;
		}
	}
	
	shmp->process_info.all_in_one_input_flag = 0;	

	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	if(action == SRV_INQUIRY )
	{
		AddPPSStatistics(MIBPPS_SM_INQUIRY,shmp->process_info.dir);

		if(pps_get_adaptor_flag())
		{
			shmp->process_info.state = S_SMPP_MAIN;
			shmp->process_info.sub_state = S_SMPP_REST_QUERY;
			shmp->process_info.sub_state_2 = 0;	
			shmp->process_info.rest_if.rest_oc = 0;
			shmp->process_info.rest_if.rest_src = 1;			
			return 0;
		}
		
		switch(shmp->caller_info.status)
		{
			case T_FRESH:
				if(!(cosTableItem->accountValidity[0].activiationByInquiryIs))
					break;
				UpdateAccountStatus(shmp->caller_info.head,
					shmp->caller_info.tail,
					T_NORMAL);
				UpdateAccountFirstUsedDate(
					shmp->caller_info.head,
					shmp->caller_info.tail);
				UpdateExpirationFirstInquiry(&shmp->caller_info,
					cosTableItem->accountValidity[0].validiDaysActivatedByInquiringAccountWithZeroBalance);

				instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT
							+ shmp->caller_info.tail;
				SendAccountRealSync(instance,0.0,0.0,OMC_OCODE_EDIT);
				smppSendActivicationInfo(&shmp->caller_info,
							(char *)shmp->called_info.number);
				ReleasePortStatus(proc_id);
				return 1;
				break;
			case T_NORMAL:
			case T_OPRTRIAL:	
				AsciiTime(expire_date,GetExpiration(&shmp->caller_info));
				if(cosTableItem->smsNotificationInquiry[0].smsNotificationWhenInquiry)
				{
				    SendAccountInfoBySMPP(&shmp->caller_info,
						shmp->called_info.number);
				}
			break;
			default:
				DebugMsg(debugmib.display.asciiOut, "Error caller : %s\n", 	shmp->caller_info.number);
				ReleasePortStatus(proc_id);
    			return 0;
			break;
		}
	}
	else if(action ==  SRV_FEE_TRANSFER)
	{
		clearTransferInfo(proc_id);
		saveTransferSMSACK(proc_id, (u_char *)short_message);
	
		if(pps_get_adaptor_flag())
		{
			result = getTransferInfo(proc_id, (u_char *)short_message);
			if(result == 1)
			{
				shmp->process_info.state = S_SMPP_MAIN;
				shmp->process_info.sub_state = S_SMPP_REST_TRANSFER;
				shmp->process_info.sub_state_2 = 0;	
				shmp->process_info.rest_if.rest_oc = 1;
				shmp->process_info.rest_if.rest_src = 1;			
				return 0;
			}
		}
		
		
		transferAccountSMSProc(proc_id);		
	}	
	else if(action ==  SRV_CALL_ME_BACK)
	{		
		clearCMBInfo(proc_id);
		if(shmp->process_info.all_in_one_input_flag)
			saveCMBSMSACK(proc_id,shmp->called_info.number);
		else
			saveCMBSMSACK(proc_id, (u_char *)short_message);
		callMebackSMSProc(proc_id);
	}
	else if(action ==  SRV_FREE_SMS)
	{
		switchServiceProc(proc_id, (u_char *)short_message);
	}
	else if(action == SRV_SCRATCH_CARD_RECHARGE)
	{//RECHARGE_REQUEST		
		AddPPSStatistics(MIBPPS_SM_RECHARGE,shmp->process_info.dir);

		if(!pps_get_adaptor_flag())
		{
			if(shmp->caller_info.status != T_FRESH &&
				shmp->caller_info.status != T_NORMAL &&
				shmp->caller_info.status != T_SUSPEND &&
				shmp->caller_info.status !=	T_OPRTRIAL
				) //LOW BALANCE, EXPIRED
			{
				if(0)
				DebugMsg(debugmib.display.asciiOut,
							"Recharging failed for caller status is error!\n");
				ReleasePortStatus(proc_id);
	    		       return 0;
			}
		}
		
		len = strlen(short_message);
		if(len>16)
		{
			if(0)
			DebugMsg(debugmib.display.asciiOut,
						"Recharging failed for wrong card length!\n");
			ReleasePortStatus(proc_id);
    		       return 0;
		}
		
		shmp->process_info.chargeFrom = 2; /*recharge from SHORT MESSAGE */
		shmp->process_info.rechargeByOther = 3;	
		strcpy((char *)shmp->called_info.number,short_message);

		len = strlen((char *)shmp->called_info.number);
		strcpy(buf, (char *)shmp->called_info.number);
		if(len %2 ==1){
			strcat(buf,"F");
			len ++;
		}
		strcat(buf,"FF");
		ppsAsciiToBcdR(shmp->cdr_info.called, buf, len+2);
	
		if(UseExternalVC())
		{
			strcpy((char *)shmp->called_info.number, short_message);
			shmp->process_info.state = S_SMMP_RECHAGE_ACCOUNT;
			shmp->process_info.sub_state = 0;
			return 1;
		}
		if(OCS_with_external_CRM())
		{
			strcpy((char *)shmp->called_info.number, short_message);
			shmp->process_info.state = S_SMMP_RECHAGE_ACCOUNT;
			shmp->process_info.sub_state = 2;
			return 1;		
		}
		
		if(pps_get_adaptor_flag())
		{
			strcpy((char *)shmp->called_info.number, short_message);
			shmp->process_info.state = S_SMPP_MAIN;
			shmp->process_info.sub_state = S_SMPP_REST_TOPUP;
			shmp->process_info.sub_state_2 = 0;	
			shmp->process_info.rest_if.rest_oc = 1;
			shmp->process_info.rest_if.rest_src = 1;			
			return 0;
		}
		
		if(RechargeCardRequest(proc_id,short_message, &result)!= 1)
		{
			SendRechargeCDR(proc_id, CT_RECHARGE_FAIL, 0);
			UpdateAccountRechargeFail(shmp->caller_info.head,
							         shmp->caller_info.tail);
			maxThreshold = GetParamRechargeAttemptLimit();
			
		    if(maxThreshold>0 && 
		    		maxThreshold < 	GetAccountRechargeFail(	shmp->caller_info.head, shmp->caller_info.tail) ) 
			{
						  UpdateAccountStatus(shmp->caller_info.head,
                                shmp->caller_info.tail, 
                                T_BLACKLIST);
			              ResetAccountRechargeFail(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);
			
			if(result == 5)
			{
				char maxBalanceNote[]={"Recharge failed, you have exceeded your maximum balance"};
				smppSendMOSM((char *)shmp->caller_info.number,
					(char *)shmp->origin_info.number,
					(u_char *)maxBalanceNote,
					strlen(maxBalanceNote),
					0);				
			}
			else
			{
				char failedNote[]={"Recharge failed, check PIN and try again"};
				smppSendMOSM((char *)shmp->caller_info.number,
					(char *)shmp->origin_info.number,
					(u_char *)failedNote,
					strlen(failedNote),
					0);				

			}
			if(0)
			DebugMsg(debugmib.display.asciiOut,
						"Recharge failed for error prepaid card!\n");
			ReleasePortStatus(proc_id);
	    		return 0;
		}
		AsciiTime(expire_date,GetExpiration(&shmp->caller_info));
		RechargeRequestProc(proc_id);

		if(shmp->process_info.chargeOther == 1)
		{
			SendAccountInfoBySMPPtoThirdParty(&shmp->caller_info, 
				(char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number, 
				(char *)shmp->origin_info.number);
		}
		
	}
	else if(action == SRV_BUNDLE_PLAN)
	{
		subscribe_bundle_plan(shmp, short_message, 0, NULL);
	}
	else if(action == SRV_BUNDLE_QUERY)
	{
		ocs_bundle_plan_query(shmp, short_message, 0, NULL);
	}
	else
	{
		if(0)
		DebugMsg(debugmib.display.asciiOut,
       				"Unknown service number: %s\n",
       				shmp->called_info.number);
	}
	
	ReleasePortStatus(proc_id);

	return 1;
}

int  get_cos_bundle_control(int cos_id)
{
	COSTable	*cosTableItem;

	if(cos_id >= MaxCosTableItem) return 1; //default is data plan only
	
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];
	return cosTableItem->serviceControl[0].bundle_plan_contorl;
	
}

int	subscribe_bundle_plan_send_used_up_info(char *caller_number, SMS_SENTENCE id)
{
	char sms_msg[256];
	char	bundle_service_number[32];

	if(OCS_with_external_CRM())
		return 0;
	
	switch (id)
	{
	case SMS_BUNDLE_PLAN_DATA_USEDUP:
	case SMS_BUNDLE_PLAN_VOICE_USEDUP:
	case SMS_BUNDLE_PLAN_SMS_USEDUP:
		break;
	default:
		return 0;
	}
	
	sprintf(sms_msg, "%s" , getSmsSentenceNative(0, id));
	sprintf(bundle_service_number, "%s", 
		ppsParam.ppsTables.serviceNumber[CreditCardRechargeNumberPosition].number);
		
	smppSendMOSM(caller_number,
		bundle_service_number, 
		(u_char *)sms_msg,
		strlen(sms_msg),
		0);

	return 1;
}

int	subscribe_bundle_plan_send_warning_info(_state_data *shmp)
{
	char sms_msg[256];
	char	bundle_service_number[32];
	long ret_value[10], data_warning_threshold = 2, data_warning_threshold_low=1, warning_sms_counter=0;

	int cos_id;

	if(OCS_with_external_CRM())
		return 0;
	
	cos_id = GetAccountCosID(&shmp->caller_info);

	data_warning_threshold = ppsParam.ppsTables.cosTable[cos_id].serviceControl[0].bundle_data_warning_threashold * 5;// COS
	data_warning_threshold_low = ppsParam.ppsTables.cosTable[cos_id].serviceControl[0].bundle_data_warning_threashold_low * 5;// COS 

	GetAccountBundlePlanInfo(&shmp->caller_info, ret_value);

	warning_sms_counter = ret_value[5];
	if(warning_sms_counter >= 2)// warning_sms_flag, don't send warning SMS, if 2 threshold warning SMS is already sent.
		return 0;
	if(ret_value[0] > data_warning_threshold)// remain bundle > threshold, don't send warning SMS
		return 0;
	if(warning_sms_counter == 1)
	{
		if(ret_value[0] > data_warning_threshold_low)
			return 0;
	}
	
	if ((ret_value[1] > 0) || (ret_value[3] > 0))
	{
		sprintf(sms_msg, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_DATA_WARN_HAS_V), 
			ret_value[0], ret_value[1], ret_value[3]);
	}
	else
	{
		sprintf(sms_msg, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_DATA_WARN), 
			ret_value[0]);
	}
	
	sprintf(bundle_service_number, "%s", 
		ppsParam.ppsTables.serviceNumber[CreditCardRechargeNumberPosition].number);

	UpdateAccountBundlePlanInfoWarningFlag(shmp, ret_value[5]+1);
	
	smppSendMOSM((char *)shmp->caller_info.number,
		bundle_service_number, 
		(u_char *)sms_msg,
		strlen(sms_msg),
		0);

	return 1;
}

int	subscribe_bundle_plan_query_info(_state_data *shmp, int operation, int method, char *ret_prompt)
{
	char sms_msg[1024], sms_buf[1024];
	int    i, unit_index=0, unit = 1;
	char data_unit_name[2][8] ={"MB","GB"};
	char	data_content[128];
	int	cos_bundle_control=0;;
	int	menu_index = 1;
	
	memset(sms_msg, 0x00, 1024);

	cos_bundle_control = get_cos_bundle_control(shmp->caller_info.cos_id);

	if(operation == 1)
	{
		if(method == 0)
			sprintf(sms_msg, "%s\r\n" , getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_PLEASE));
	}
	else
	{
		//sprintf(sms_msg, "%s\r\n" , getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_CMD));
		if(method == 0)
			sprintf(sms_msg, "%s\r\n" , getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_PLEASE));
	}

	
	for(i=1; i<MAX_BUNDLE_PLANS; i++)
	{
		if(ppsParam.ppsTables.bundlePlans[i].existFlag &&
			ppsParam.ppsTables.bundlePlans[i].apply_flag == 1)
		{
			if(((ppsParam.ppsTables.bundlePlans[i].mo_voice > 0) \
				|| (ppsParam.ppsTables.bundlePlans[i].sms > 0))
				)
			{
				if (cos_bundle_control)// usr has data plan only
				{
					continue;
				}
				
				if(ppsParam.ppsTables.bundlePlans[i].data > 1000)
				{
					unit_index = 1;
					unit = 1024;
					if(ppsParam.ppsTables.bundlePlans[i].data % unit)
					{
						sprintf(data_content, "%.1f%s", ppsParam.ppsTables.bundlePlans[i].data*1.0/unit, data_unit_name[unit_index]);
					}
					else
					{
						sprintf(data_content, "%d%s", ppsParam.ppsTables.bundlePlans[i].data/unit, data_unit_name[unit_index]);
					}
				}
				else
				{
					unit_index = 0;
					unit = 1;
					sprintf(data_content, "%d%s", ppsParam.ppsTables.bundlePlans[i].data/unit, data_unit_name[unit_index]);
				}
				
				if(method == 0)
				{
					sprintf(sms_buf, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN), 
						getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_CMD),
						ppsParam.ppsTables.bundlePlans[i].charge / 100,
						ppsParam.ppsTables.bundlePlans[i].charge /100,
						//ppsParam.ppsTables.bundlePlans[i].data,
						data_content,
						ppsParam.ppsTables.bundlePlans[i].mo_voice,
						//ppsParam.ppsTables.bundlePlans[i].mt_voice,
						ppsParam.ppsTables.bundlePlans[i].sms,
						ppsParam.ppsTables.bundlePlans[i].validity
						);
				}
				else if(method == 1)
				{
					continue;/*
					sprintf(sms_buf, getSmsSentenceNative(0, SMS_BUNDLE_PLAN_USSD_SUB_MENU), 
						menu_index++, //i,
						data_content,
						ppsParam.ppsTables.bundlePlans[i].validity,
						ppsParam.ppsTables.bundlePlans[i].charge /100,
						ppsParam.ppsTables.bundlePlans[i].mo_voice,
						ppsParam.ppsTables.bundlePlans[i].sms
						);*/
				}
				else if(method == 2)
				{
					u8 apply_code;
					if (shmp->process_info.serviceKey != 7)// not query bundle plan
					{
						continue;
					}
				
					apply_code = menu_index++;
					if(ppsParam.ppsTables.bundlePlans[i].apply_code > 0)
						apply_code = ppsParam.ppsTables.bundlePlans[i].apply_code;
					
					sprintf(sms_buf, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_USSD_SUB_CHOICE), 
						apply_code, 
						data_content,
						ppsParam.ppsTables.bundlePlans[i].mo_voice,
						ppsParam.ppsTables.bundlePlans[i].sms,
						ppsParam.ppsTables.bundlePlans[i].validity,
						ppsParam.ppsTables.bundlePlans[i].charge /100
						);

				}	
			}
			else
			{

				if(ppsParam.ppsTables.bundlePlans[i].data > 1000)
				{
					unit_index = 1;
					unit = 1024;
					if(ppsParam.ppsTables.bundlePlans[i].data % unit)
					{
						sprintf(data_content, "%.1f%s", ppsParam.ppsTables.bundlePlans[i].data*1.0/unit, data_unit_name[unit_index]);
					}
					else
					{
						sprintf(data_content, "%d%s", ppsParam.ppsTables.bundlePlans[i].data/unit, data_unit_name[unit_index]);
					}
				}
				else
				{

					unit_index = 0;
					unit = 1;
					sprintf(data_content, "%d%s", ppsParam.ppsTables.bundlePlans[i].data/unit, data_unit_name[unit_index]);
				}
				
				if(method == 0)
				{
					sprintf(sms_buf, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_DATA), 
						getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_CMD),
						ppsParam.ppsTables.bundlePlans[i].charge / 100,
						ppsParam.ppsTables.bundlePlans[i].charge /100,
						//ppsParam.ppsTables.bundlePlans[i].data /unit,
						//data_unit_name[unit_index],
						data_content,
						ppsParam.ppsTables.bundlePlans[i].validity
						);
				}
				else if(method == 1)
				{
					u8	apply_code = menu_index++; //i;
					if(ppsParam.ppsTables.bundlePlans[i].apply_code > 0)
						apply_code = ppsParam.ppsTables.bundlePlans[i].apply_code;
					
					sprintf(sms_buf, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_USSD_DATA), 
						apply_code, 
						data_content,
						ppsParam.ppsTables.bundlePlans[i].validity,
						ppsParam.ppsTables.bundlePlans[i].charge /100
						);

				}	
				else if(method == 2)
				{
					u8 apply_code;
					if (shmp->process_info.serviceKey != 6)// not query data plan
					{
						continue;
					}
				
					apply_code = menu_index++;
					if(ppsParam.ppsTables.bundlePlans[i].apply_code > 0)
						apply_code = ppsParam.ppsTables.bundlePlans[i].apply_code;
					
					sprintf(sms_buf, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_USSD_SUB_DATA_CHOICE), 
						apply_code, 
						data_content,
						ppsParam.ppsTables.bundlePlans[i].validity,
						ppsParam.ppsTables.bundlePlans[i].charge /100
						);

				}	

			}
			
			strcat(sms_msg, sms_buf);
			strcat(sms_msg, "\r\n");
			
		}
	}
	strcat(sms_msg, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_NO_REFUND));

	if(method == 0)
	{
		smppSendMOSM((char *)shmp->caller_info.number,
			(char *)shmp->called_info.number, 
			(u_char *)sms_msg,
			strlen(sms_msg),
			0);
	}
	else
	{
		if(ret_prompt != NULL)
			strcpy(ret_prompt, sms_msg);
	}
	
	return 1;
}

int	subscribe_bundle_plan_subs_index(_state_data *shmp, int plan_index, int method, char *ret_prompt)
{
	float	charge = 0.0;

	serviceBundlePlan *bp_ptr = NULL;
	float balance;
	char	sms_msg[256], sms_tmp[256];
	
	balance = GetAccountBalance(shmp->caller_info.head, shmp->caller_info.tail);
	bp_ptr = &ppsParam.ppsTables.bundlePlans[plan_index];
	
	charge = bp_ptr->charge;
	if(balance < charge)
	{
		sprintf(sms_msg, "%s", getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_NOK));
		
		goto bundle_plan_send_sms;
	}

	if(1)
	{
		CHARGED_FEE chargeFee;
		int instance;
		long	ret_value[10];
		char		expire_date[64];
		
		UpdateBalance(shmp, &shmp->caller_info, -charge, &chargeFee);

		ret_value[0] = 0 - bp_ptr->charge;
		ret_value[1] = bp_ptr->data;
		ret_value[2] = bp_ptr->mo_voice;
		ret_value[3] = bp_ptr->mt_voice;
		ret_value[4] = bp_ptr->sms;
		ret_value[5] = bp_ptr->validity;
		ret_value[6] = 0;
		
		UpdateAccountBundlePlanInfo(&shmp->caller_info, ret_value);

		instance = shmp->caller_info.head * UNIT_STORE_ACCOUNT + shmp->caller_info.tail;
		
		SendAccountRealSync(instance, chargeFee.balance, chargeFee.promotion, OMC_OCODE_EDIT);

		shmp->caller_info.charge = charge;
		shmp->process_info.call_type = CT_BUNDLE_PLAN_CHARGE;
		shmp->process_info.duration = 0;
		if ((bp_ptr->mo_voice > 0) || (bp_ptr->mt_voice > 0))
		{
			shmp->process_info.tcos = 1;// bundle plan
		}
		else
		{
			shmp->process_info.tcos = 0;// data plan
		}

	 	shmp->cdr_info.seizuretime = time(NULL);
	    	shmp->cdr_info.answertime = time(NULL);
	    	shmp->cdr_info.releasetime = time(NULL);
		
		SendCDR(shmp->process_info.portid);

		GetAccountBundlePlanInfo(&shmp->caller_info, ret_value);

		AsciiTimeYMD(expire_date,ret_value[4]);		

		sprintf(sms_msg, "%s", (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_OK)); 
		if(ret_value[1]>0 || ret_value[3]>0)
			sprintf(sms_tmp, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_QUERY),
				ret_value[0],
				ret_value[1],
				// ret_value[2],// disable MT voice
				ret_value[3],
				expire_date);
		else
			sprintf(sms_tmp, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_QUERY_DATA),
				ret_value[0],
				expire_date);
			
		strcat(sms_msg, sms_tmp);
		
	}

bundle_plan_send_sms:
	if(method)
	{
		if(ret_prompt != NULL)
			strcpy(ret_prompt, sms_msg);
	}
	else
	{
		smppSendMOSM((char *)shmp->caller_info.number, 
			(char *)shmp->called_info.number, 		
			(u_char *)sms_msg,
			strlen(sms_msg),
			0);

		if(shmp->process_info.chargeOther == 1)
		{
			char real_sms[1024];
			sprintf(real_sms, "%s: %s", shmp->caller_info.number,
				sms_msg);
			smppSendMOSM((char *)shmp->origin_info.number,
				(char *)shmp->called_info.number, 		
				(u_char *)real_sms,
				strlen(real_sms),
				0);
		}
		
	}
	
	return 1;
}

int	ocs_bundle_plan_query(_state_data *shmp, char *sms, int method, char *ret_prompt)
{
	//int	sms_len;
	//int 	i;
	//char bs_command[128];
	
	u_long	ret_value[10];
	char		expire_date[64];
	char		sms_msg[256];
	
	if(0)
	DebugMsg(debugmib.display.asciiOut,
						"[%04d] %s, content: %s, method=%d\n", 
						shmp->process_info.portid, __FUNCTION__, sms, method);

	
	if(1)	
	{
		if(GetAccountBundlePlanInfo(&shmp->caller_info, (long *)ret_value))
		{
			AsciiTimeYMD(expire_date,ret_value[4]);		

			if(ret_value[1]>0 || ret_value[3]>0)
			{
				sprintf(sms_msg, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_QUERY),
					ret_value[0],
					ret_value[1],
					// ret_value[2],// disable MT voice
					ret_value[3],
					expire_date);
			}
			else
				sprintf(sms_msg, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_QUERY_DATA),
					ret_value[0],
					expire_date);
				
			if(method)
			{
				if(ret_prompt != NULL)
					strcpy(ret_prompt, sms_msg);
			}
			else
			{
				smppSendMOSM((char *)shmp->caller_info.number, (char *)shmp->called_info.number, 		
					(u_char *)sms_msg,
					strlen(sms_msg),
					0);
			}
			return 0;
		
		}
	
	}

	return subscribe_bundle_plan_query_info(shmp, 0, method, ret_prompt);

}

int	subscribe_bundle_plan(_state_data *shmp, char *sms, int method, char *ret_prompt)
{/*method=0/1, over SMS or USSD */
	int	sms_len;
	int 	i;
	char bs_command[128];
	
	u_long	ret_value[10];
	char		expire_date[64];
	char		sms_msg[256];
	int		menu_index=1;
	//int	cos_bundle_control=0;

	//cos_bundle_control = get_cos_bundle_control(shmp->caller_info.cos_id);
	
	sms_len = strlen(sms);

	/*
	if(sms_len <= 1)
	{
		return subscribe_bundle_plan_query_info(shmp, 0);
	}
	*/
	/*
	DebugMsg(debugmib.display.asciiOut,
						"[%04d] %s, content: %s, method=%d\n", 
						shmp->process_info.portid, __FUNCTION__, sms, method);
	*/
	
	if(sms_len > 0)
	{
		for(i=1; i<MAX_BUNDLE_PLANS; i++)
		{
			if(ppsParam.ppsTables.bundlePlans[i].existFlag &&
				ppsParam.ppsTables.bundlePlans[i].apply_flag == 1)
			{
				if(method == 0)
				{
					sprintf(bs_command, "%s%d",
										getSmsSentenceNative(0, SMS_BUNDLE_PLAN_SUBSCRIBE_CMD), 
										ppsParam.ppsTables.bundlePlans[i].charge /100);
					if(strncasecmp(bs_command, sms, sms_len) == 0) 		
					{
						subscribe_bundle_plan_subs_index(shmp, i, method, ret_prompt);
						return 1;
					}
				}
				else if(method == 1)
				{
					int apply_code;
					
					if(ppsParam.ppsTables.bundlePlans[i].mo_voice > 0 || ppsParam.ppsTables.bundlePlans[i].sms > 0)
						continue;
						
					apply_code = menu_index ++;
					if (ppsParam.ppsTables.bundlePlans[i].apply_code > 0)
					{
						apply_code = ppsParam.ppsTables.bundlePlans[i].apply_code;
					}
					if(atoi(sms) == apply_code)
					{
						subscribe_bundle_plan_subs_index(shmp, i, method, ret_prompt);
						return 1;					
					}
				}
				else if (method == 2)
				{
					int apply_code ; 
					if (shmp->process_info.serviceKey == 7)
					{
						if(ppsParam.ppsTables.bundlePlans[i].mo_voice <= 0 && ppsParam.ppsTables.bundlePlans[i].sms <= 0)
							continue;
					}
					else if (shmp->process_info.serviceKey == 6)
					{
						if(ppsParam.ppsTables.bundlePlans[i].mo_voice > 0 || ppsParam.ppsTables.bundlePlans[i].sms > 0)
							continue;
					}
					
					apply_code = menu_index ++;
					if (ppsParam.ppsTables.bundlePlans[i].apply_code > 0)
					{
						apply_code = ppsParam.ppsTables.bundlePlans[i].apply_code;
					}
					if(atoi(sms) == apply_code)
					{
						subscribe_bundle_plan_subs_index(shmp, i, method, ret_prompt);
						return 1;					
					}
				}
			}
		}
	}

	ocs_bundle_plan_query(shmp, sms, method, ret_prompt);

	return 0;
	
	{
		if(GetAccountBundlePlanInfo(&shmp->caller_info, (long *)ret_value))
		{
			AsciiTimeYMD(expire_date,ret_value[4]);		

			if(ret_value[1]>0 || ret_value[3]>0)
				sprintf(sms_msg, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_QUERY),
					ret_value[0],
					ret_value[1],
					// ret_value[2],// disable MT voice
					ret_value[3],
					expire_date);
			else
				sprintf(sms_msg, (char *)getSmsSentenceNative(0, SMS_BUNDLE_PLAN_QUERY_DATA),
					ret_value[0],
					expire_date);
				
			if(method)
			{
				if(ret_prompt != NULL)
					strcpy(ret_prompt, sms_msg);
			}
			else
			{
				smppSendMOSM((char *)shmp->caller_info.number, (char *)shmp->called_info.number, 		
					(u_char *)sms_msg,
					strlen(sms_msg),
					0);
			}
			return 1;
		
		}
	
		//return subscribe_bundle_plan_query_info(shmp, 1);
	}

	return subscribe_bundle_plan_query_info(shmp, 0, method, ret_prompt);
	
	return 1;
}
/*---------------------------------------------------------------*/



static int pps_smppOnDataRecv(BYTE linkNo, SMPP_MSG *pMsg, WORD dataLen)
{ /* no trafic control method applied */


	SMPP_MSG	smpp_msg;

	switch(pMsg->message_type)
	{
		case 0x0B:	/* Submit SM */
		break;
		case 0x0C:	/*Submit SM Resp. */
		if((logMsgFlag & 0x02)   && debug_messages<=MAX_DEBUG_MESSAGES)
		{
			DebugMsgDelimiter();
			DebugMsg(debugmib.display.asciiOut,
			"Submit SM response received , link = %d", linkNo);
		}
		break;
		case 0x0D:	/*Submit Multi. */
		break;
		case 0x0E:	/*Submit Multi. Resp. */
		break;
		case 0x0F:	/*Deliver SM */
	
			/*send deliver sm response */
			smpp_msg.message_type = 0x10;
			smpp_msg.pdu.deliver_sm_resp.head.command_status = htonl(ESME_ROK);
			smpp_msg.pdu.deliver_sm_resp.head.sequence_number = 
				pMsg->pdu.deliver_sm.head.sequence_number;
			smpp_msg.pdu.deliver_sm_resp.message_id = 0x00;
			smpp_send(linkNo,&smpp_msg,dataLen);

			DecodeDeliverSM(pMsg);

		break;
		case 0x10:	/*Deliver SM Resp. */

		break;
		case 0x11:	/*Data SM */
		break;
		case 0x12:	/*Data SM Resp. */
		break;
		case 0x13:	/*Query SM */
		break;
		case 0x14:	/*Query SM Resp. */
		break;
		case 0x15:	/*Cancel SM */
		break;
		case 0x16:	/*Cancel SM Resp. */
		break;
		case 0x17:	/*Replace SM */
		break;
		case 0x18:	/*Replace SM Resp. */
		break;
		default:
		break;
	}
	return 1;
}

static	int	pps_smppOnLinkStateChange(BYTE linkNo, BYTE state)
{
	int i;

	for(i=0;i<MAX_SMPP_LINK_SET;i++)
	{
		if((smpp_status.linkNo[i]&0x7f) == linkNo)
		{
			smpp_status.link_state[i] = state;
			/*if((logMsgFlag & 0x02)   && debug_messages<=MAX_DEBUG_MESSAGES)
			{
				DebugMsgDelimiter();
				DebugMsg(debugmib.display.asciiOut,
					"Recv Link [%d] state = [%d]",
					 linkNo,state);
			}
			*/
			if(smpp_status.link_state[i] == 0)
			{
				smpp_status.linkNo[i] = 0x00; 
				/* resetup link for smpp parameter changed */
			}
			
			return 1;
		}
	}
	if((logMsgFlag & 0x02)   && debug_messages<=MAX_DEBUG_MESSAGES)
	{
		DebugMsgDelimiter();
		DebugMsg(debugmib.display.asciiOut,
			"Warning : Recv Link [%d] state = [%d], but pps never registered this linkNo!",
			 linkNo,state);
	}

	return 0;
}

void ppsSetupSMPPLink(int linkNo)
{

	smpp_attach_link((smpp_status.linkNo[linkNo] & 0x7f),
						pps_smppOnDataRecv,
						pps_smppOnLinkStateChange);
	if((logMsgFlag & 0x02)   && debug_messages<=MAX_DEBUG_MESSAGES)
	{
	//	DebugMsgDelimiter();
	//	DebugMsg(debugmib.display.asciiOut,
	//		"Attach link [%d]! ",
	//		 smpp_status.linkNo[linkNo]&0x7f);
	}
}

extern int pps_get_smpp_link_type();
int pps_smppInit()
{
	PutLogFunID("pps_smppInit");

	//memset(&ppsSMPPmsgBuf,0,sizeof(ppsSMPPmsgBuf));
	
	if(!(smpp_status.linkNo[0] & 0x80) && strlen((char *)ppsParam.ppsTables.serviceNumber[InquiryNumberPosition].number)>=2) 
	{
		if(pps_get_smpp_link_type())
			smpp_status.linkNo[0]  = smpp_registerLink(SMPP_CLIENT, 
								BIND_TRX, 
								(char *)ppsParam.ppsTables.serviceNumber[InquiryNumberPosition].number, 
								TRANSACTION);
		else
			smpp_status.linkNo[0]  = smpp_registerLink(SMPP_CLIENT, 
								BIND_TRX, 
								(char *)ppsParam.ppsTables.serviceNumber[InquiryNumberPosition].number, 
								DATAGRAM);
		if((logMsgFlag & 0x02))
		{
		//	DebugMsg(debugmib.display.asciiOut,
		//		"pps register smppLink TRx Client [%s], result=%02x",
		//			ppsParam.ppsTables.serviceNumber[InquiryNumberPosition].number,
		//			smpp_status.linkNo[0]);
		}
	
		if(!(smpp_status.linkNo[0]  & 0x80))
		{
			//alarm_code[0] = 3;
			//alarm_code[1] = 1;
			//ppsSendHeartBeating();
			return 0;
		}
		else
		{
			ppsSetupSMPPLink(0);
			smpp_status.dir[0] = _SMPP_SEND;
		}
	}
	if((smpp_status.linkNo[0]  & 0x80) && !(smpp_status.link_state[0]))
	{
		ppsSetupSMPPLink(0);
	}

	if(!(smpp_status.linkNo[1]&0x80)  && strlen((char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number)>=2)
			//||!(smpp_status.link_state[1]))
	{
		if(pps_get_smpp_link_type())
			smpp_status.linkNo[0]  = smpp_registerLink(SMPP_CLIENT, 
								BIND_TRX, 
								(char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number, 
								TRANSACTION);
		else
			smpp_status.linkNo[1] = smpp_registerLink(SMPP_CLIENT, 
								BIND_TRX, 
								(char *)ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number, 
								DATAGRAM);
		if((logMsgFlag & 0x02))
		{
		//	DebugMsg(debugmib.display.asciiOut,
		//		"pps register smppLink TRx Client [%s], result=%02x",
		//			ppsParam.ppsTables.serviceNumber[ScratchCardRechargeNumberPosition].number,
		//			smpp_status.linkNo[1]);
		}

		if(!(smpp_status.linkNo[1] & 0x80))
		{
			//alarm_code[0] = 3;
			//alarm_code[1] = 2;
			//ppsSendHeartBeating();
			return 0;
		}
		else
		{
			ppsSetupSMPPLink(1);
			smpp_status.dir[1] = _SMPP_SEND;
		}

	}
	if((smpp_status.linkNo[1]  & 0x80) && !(smpp_status.link_state[1]))
	{
		ppsSetupSMPPLink(1);
	}

	return 1;
}

void	pps_resetupSMPPLink()
{
	int i;

	for(i=0;i<MAX_SMPP_LINK_SET;i++)
	{
		if((smpp_status.linkNo[i] &0x80)==0x80)
		{
			smpp_close(smpp_status.linkNo[i] & 0x7f);
		}		
	}
	pps_smppInit();	
}

int smppMonitor()
{ //smpp link sets management 
	static int linkInitCounter = 0;
	smpp_send_buf *ptr;
	int i, head,tail;	

	PutLogFunID("smppMonitor");

	if(linkInitCounter ++ % 200 ==0 ) /* 1 minute to resetup link */
	{
		pps_smppInit();
	}
	return 1;/* no buffer msg */
	for(i=0;i<2;i++)
	{
		if(smpp_status.link_state[i] == _SMPP_USED)
		{
			switch(i)
			{
				case 0:
					ptr = &ppsSMPPmsgBuf.ppsSMPPsBufRecharge;
					break;
				case 1:
					ptr = &ppsSMPPmsgBuf.ppsSMPPsBufInquiry;
					break;
			}
			head = ptr->head ;
			tail = ptr->tail ; 
			if(head != tail)
			{
				if(time(NULL)>(ptr->submitTime[head]+PROMPT_SM_DELAY_TIME)) 
				{							
					smpp_send((smpp_status.linkNo[i]&0x7f),
								&ptr->sSM_msg[head],
								0);
					ptr->head ++;
					ptr->head %= MAX_SMPP_MSG;
				}
			}
		}
	}
   return 1;
}

int smppSendMOSMReal(SMPP_MSG *sm_msg)
{
	int i;
	
	if(sm_msg == NULL)  return 0;

	for(i=0;i<2;i++)
	{
		if(smpp_status.link_state[0] ==  1 && smpp_status.link_state[1] == 1)
		{
			if(smpp_link_selection%2==i)
			{
				smpp_send((smpp_status.linkNo[i]&0x7f),
						sm_msg,
						0);
				smpp_link_selection++;
				break;
			}	
		}
		else
		{
			if(smpp_status.link_state[i] == 1)
			{
				smpp_send((smpp_status.linkNo[i]&0x7f),
						sm_msg,
						0);
				smpp_link_selection++;
				break;
			}
		}
	}
	return 1;
}

int smppSendMOSM(char *called, char *caller, u_char *msg, int msgLen,u_char dataCoding)
{
	static int smpp_sm_ref_id = 0;
	
	int		msgLength,head,tail,i,totalPackets,lastPacketLen;
	SMPP_MSG	sm_msg;
	smpp_send_buf	*ptr;

	PutLogFunID("smppSendMOSM");

	if(msgLen == 0) //empty sms is not sent
		return 0;
		
	memset(&sm_msg,0,sizeof(SMPP_MSG));
	sm_msg.message_type = 0x0B;
	sm_msg.optional_param_flag1 = 0x00;
	sm_msg.optional_param_flag2 = 0x00;

//	strcpy(sm_msg.pdu.submit_sm.service_type, "USSD");
	memset(sm_msg.pdu.submit_sm.service_type,0,6);
	sm_msg.pdu.submit_sm.source_addr_ton	= 0x00;	
	//Unknown , 1=international, 2=National
	sm_msg.pdu.submit_sm.source_addr_npi	= 0x01;	//ISDN
	strcpy(sm_msg.pdu.submit_sm.source_addr, caller);
	sm_msg.pdu.submit_sm.dest_addr_ton	= 0x01;	//National
	sm_msg.pdu.submit_sm.dest_addr_npi	= 0x01;	//ISDN
	strcpy(sm_msg.pdu.submit_sm.destination_addr, called);
	sm_msg.pdu.submit_sm.esm_class		= 0x00;
	sm_msg.pdu.submit_sm.protocol_id		= 0x00;
	sm_msg.pdu.submit_sm.priority_flag	= 0x03;
	memset(sm_msg.pdu.submit_sm.schedule_delivery_time, 0,1);
	memset(sm_msg.pdu.submit_sm.validity_period, 0,1);
	sm_msg.pdu.submit_sm.registered_delivery	= 0x00;//0x11;
	sm_msg.pdu.submit_sm.replace_if_present_flag	= 0x01;
	sm_msg.pdu.submit_sm.data_coding		= dataCoding;
	sm_msg.pdu.submit_sm.sm_default_msg_id	= 0x01;

	if(msgLen>140)
	{
		totalPackets = msgLen / 134;
		lastPacketLen = msgLen % 134;
		sm_msg.pdu.submit_sm.esm_class		= 0x40;
	}
	else
	{
		totalPackets = 0;
		lastPacketLen = msgLen;
	}
	
	i = 0;
	for(i=0;i<totalPackets;i++)
	{
		msgLength = 140;

		sm_msg.pdu.submit_sm.sm_length = msgLength;
//		if(lastPacketLen > 0)
		{
			sm_msg.pdu.submit_sm.short_message[0] = 0x05;
			sm_msg.pdu.submit_sm.short_message[1] = 0x00;
			sm_msg.pdu.submit_sm.short_message[2] = 0x03;
			sm_msg.pdu.submit_sm.short_message[3] = smpp_sm_ref_id;
			sm_msg.pdu.submit_sm.short_message[4] = totalPackets + 1;
			sm_msg.pdu.submit_sm.short_message[5] = i + 1;		
			memcpy(&sm_msg.pdu.submit_sm.short_message[6], &msg[i*134], msgLength);

			sm_msg.pdu.submit_sm.short_message[msgLength] = 0;
		}
		/*
		else
		{
			memcpy(sm_msg.pdu.submit_sm.short_message,&msg[i*140],msgLength);
			sm_msg.pdu.submit_sm.short_message[msgLength] = 0;
		}
		*/
		
		if((logMsgFlag & 0x02))
		{
			DebugMsgDelimiter();
			DebugMsg(debugmib.display.asciiOut,
				"[%s]Send account info. throught SMPP\r\n%s %s len:%d",
				called,caller,sm_msg.pdu.submit_sm.short_message,msgLength);
		}

		smppSendMOSMReal(&sm_msg);
	}
	
	if(lastPacketLen>0)
	{
		if(totalPackets >= 1)
		{
			msgLength = lastPacketLen + 6;

			sm_msg.pdu.submit_sm.sm_length = msgLength;

			sm_msg.pdu.submit_sm.short_message[0] = 0x05;
			sm_msg.pdu.submit_sm.short_message[1] = 0x00;
			sm_msg.pdu.submit_sm.short_message[2] = 0x03;
			sm_msg.pdu.submit_sm.short_message[3] = smpp_sm_ref_id;
			sm_msg.pdu.submit_sm.short_message[4] = totalPackets + 1;
			sm_msg.pdu.submit_sm.short_message[5] = i + 1;		
			memcpy(&sm_msg.pdu.submit_sm.short_message[6], &msg[i*134], msgLength);
		}
		else
		{
			msgLength = lastPacketLen ;
			sm_msg.pdu.submit_sm.sm_length = msgLength;

			memcpy(&sm_msg.pdu.submit_sm.short_message, &msg[i*140], msgLength);
		}
		sm_msg.pdu.submit_sm.short_message[msgLength] = 0;
		
		if((logMsgFlag & 0x02) )
		{
			DebugMsgDelimiter();
			DebugMsg(debugmib.display.asciiOut,
				"[%s]Send account info. throught SMPP\r\n%s %s len:%d",
				called,caller,sm_msg.pdu.submit_sm.short_message,msgLength);
		}
		smppSendMOSMReal(&sm_msg);
	}

	smpp_sm_ref_id ++;
	
	return 1; /* no buffer msg , 2005-07-24 , lhb */
	ptr = &ppsSMPPmsgBuf.ppsSMPPsBufRecharge;
	ptr = &ppsSMPPmsgBuf.ppsSMPPsBufInquiry;
	
	head = ptr->head;
	tail = ptr->tail;
	memcpy(&ptr->sSM_msg[tail],
		   &sm_msg,
		   sizeof(SMPP_MSG));
	ptr->submitTime[tail] = time(NULL);
	tail ++;

	if(tail == head ) head++;
	tail = tail %MAX_SMPP_MSG;
	head = head %MAX_SMPP_MSG;
	ptr->head = head;
	ptr->tail = tail;

	if((logMsgFlag & 0x02)   && debug_messages<=MAX_DEBUG_MESSAGES)
	{
		debug_messages ++;
		DebugMsgDelimiter();
		DebugMsg(debugmib.display.asciiOut,
			"[%s]Send account info. throught SMPP\n%s %s len:%d-%d",
			called,caller,msg,strlen((char *)msg),msgLength);
	}
	return 1;
}

int fillMoneyToContent(float balance,char *sms)
{
	char	tmpbuf[MAX_SMS_LEN]={0};
	
	sprintf(tmpbuf,"%s",ppsParam.serviceControl.currencyName);

    switch(ppsParam.ppsTables.promptResolution[0].currencyResolution)
	{
	  case 0:
	    sprintf(sms,"%ld",(long)balance);
	  break;
	  case 1:
	    sprintf(sms,"%.1f",(float)balance/10);
	  break;
	  case 2:
	  default:
	    sprintf(sms,"%.2f",(float)balance/100);
	  break;
	}

	if(ppsParam.ppsTables.promptResolution[0].currencySymbolPosition == 0) /* front */
	{
		strcat(tmpbuf,sms);
		strcpy(sms,tmpbuf);
	}
	else /* rear */
	{
		strcat(sms,tmpbuf);
	}	
	return strlen(sms);
}

int fillMoneyToContentWithoutCurrency(float balance,char *sms)
{
    switch(ppsParam.ppsTables.promptResolution[0].currencyResolution)
	{
	  case 0:
	    sprintf(sms,"%ld",(long)balance);
	  break;
	  case 1:
	    sprintf(sms,"%.1f",(float)balance/10.0);
	  break;
	  case 2:
	  default:
	    sprintf(sms,"%.2f",(float)balance/100.0);
	  break;
	}
	return strlen(sms);
}

int fillDateToContent(char *expire_date,char *sms)
{	
	switch(ppsParam.ppsTables.promptResolution[0].dataFormat)
	{
			case 0: /* YYYY-MM-DD */				
				memcpy(sms,expire_date,4);
				sms[4]='-';
				memcpy(&sms[5],&expire_date[4],2);
				sms[7]='-';
				memcpy(&sms[8],&expire_date[6],2);
			break;
			case 1: /* DD-MM-YYYY */
				memcpy(&sms[0],&expire_date[6],2);
				sms[2]='-';
				memcpy(&sms[3],&expire_date[4],2);
				sms[5]='-';
				memcpy(&sms[6],expire_date,4);
			break;
			case 2: /* MM-DD-YYYY */
				memcpy(&sms[0],&expire_date[4],2);
				sms[2]='-';
				memcpy(&sms[3],&expire_date[6],2);
				sms[5]='-';
				memcpy(&sms[6],expire_date,4);
			break;
	}
	sms[10]=0;
	return 10;
}

int fillMoneyAndDate(	
			long balance, char *expire_date,
			int calltype0,
			u_char	flag,
			u_char *smMsgBuf) /* flag = 0, normal, 1= low balance prompt */
{
	char			buf[128]={0};
	u_char			daybuf[32]={0},bcd_buf[140];
	int				instance=0,len,totalLen=0;
	u_char			dataCoding=0;
	//int				calltype = calltype0;

	instance = (flag & 0x7f)%MAX_LANGUAGE_TYPES;
	dataCoding = ppsParam.serviceControl.languageSmEncodeType[instance];

	memset(buf,0,128);
	fillMoneyToContent(balance,buf);
	
	len = strlen(buf);

	if(dataCoding ==0)
	{
		strcat((char *)smMsgBuf, buf);

	}
	else
	{
		len = smppEncodeASCtoUC2(buf,bcd_buf,len);
		memcpy(&smMsgBuf[totalLen],bcd_buf,len);
	}	
	memset(buf,0,128);
	totalLen += len;
#ifdef _ZED_NO_CALLER_NUM_AND_EXPIRY_DATE_
	return totalLen;
#endif
	if(strlen((char *)getSmsSentenceNative(0,SMS_EXPIRED_ON)) || !(flag&0x80))
	{
		sprintf(buf,",");
		
		len = strlen(buf);

		if(dataCoding ==0)
		{
			strcat((char *)smMsgBuf, buf);

		}
		else
		{
			len = smppEncodeASCtoUC2(buf,bcd_buf,len);
			memcpy(&smMsgBuf[totalLen],bcd_buf,len);
		}	
		totalLen += len;

		sprintf(buf,"%s",getSmsSentenceNative(0,SMS_EXPIRED_ON));
		
		len = strlen(buf);
		memcpy(&smMsgBuf[totalLen],buf,len);	
		totalLen += len;
		
		len = fillDateToContent(expire_date, (char *)daybuf);
		if(dataCoding ==0)
		{
			memcpy(&smMsgBuf[totalLen],daybuf,len);	
		}
		else
		{
			len = smppEncodeASCtoUC2((char *)daybuf, bcd_buf, len);
			memcpy(&smMsgBuf[totalLen],bcd_buf,len);
		}	
		totalLen += len;
	}
	return totalLen;
}

int smppSendAccountInfo(char *caller, char *called,
			long balance, char *expire_date,
			long promotion,char *promotionExpireDate,
			int calltype0,
			char *call_date, 
			char *msisdn,
			u_char	flag) /* flag = 0, normal, 1= low balance prompt */
{

	char				smMsgBuf[MAX_SMS_LEN*8]={0},buf[MAX_SMS_LEN*2]={0};
	u_char				daybuf[32]={0},bcd_buf[140];
	int					len,totalLen;//instance=0,
	u_char				dataCoding=0;
	int					calltype = calltype0;

	//instance = (flag & 0x7f)%MAX_LANGUAGE_TYPES;
	//dataCoding = ppsParam.ppsTables.smsSentence[instance].smEncodeType;

	memset(smMsgBuf,0,MAX_SMS_LEN*8);
		

	/******************** balance **************************************/
	if((flag & 0x40) == 0x40)
		sprintf(buf,"%s: %s",msisdn, getSmsSentenceNative(0,SMS_ACCOUNT_BALANCE));
	else
		sprintf(buf,"%s",getSmsSentenceNative(0,SMS_ACCOUNT_BALANCE));
	len = strlen(buf);

	strcpy(smMsgBuf,buf);
	totalLen = len;

	memset(buf,0,128);
	len = fillMoneyAndDate(balance, expire_date, calltype0, flag, (u_char *)buf);
	strcat(smMsgBuf,buf);
	totalLen += len;
	/******************** balance **************************************/

	/******************** promotion **************************************/
	if(promotion > 0)
	{
		sprintf(buf,"%s",getSmsSentenceNative(0,SMS_ACCOUNT_PROMOTION));
		len = strlen(buf);

		strcat(smMsgBuf,buf);
		totalLen += len;
			
		memset(buf,0,128);
		len = fillMoneyAndDate(promotion, promotionExpireDate, calltype0, flag, (u_char *)buf);
		strcat(smMsgBuf,buf);
		totalLen += len;
	}
	/******************** promotion **************************************/
	
	if(calltype>0 && strlen((char *)getSmsSentenceNative(0,SMS_LAST_CALL_ON))
		&& strlen((char *)getSmsSentenceNative(0,SMS_LAST_RECEIVE_ON)))
	{
		if(calltype == 2)
			sprintf(buf," %s ",getSmsSentenceNative(0,SMS_LAST_RECEIVE_ON));
		else
			sprintf(buf," %s ",getSmsSentenceNative(0,SMS_LAST_CALL_ON));

		len = strlen(buf);

		memcpy(&smMsgBuf[totalLen],buf,len);
		totalLen += len;

		len = fillDateToContent(call_date, (char *)daybuf);

		if(dataCoding ==0)
		{
			strcat(smMsgBuf, (char *)daybuf);

		}
		else
		{
			len = smppEncodeASCtoUC2((char *)daybuf,bcd_buf,len);
			memcpy(&smMsgBuf[totalLen],bcd_buf,len);
		}	
		totalLen += len;

		sprintf(buf," %s ",getSmsSentenceNative(0,SMS_AT));

		len = strlen(buf);

		memcpy(&smMsgBuf[totalLen],buf,len);
		totalLen += len;

		memcpy(&daybuf[0],&call_date[8],2);
		daybuf[2]=':';
		memcpy(&daybuf[3],&call_date[10],2);
		daybuf[5]=0;

		len = 5;
		if(dataCoding ==0)
		{
			strcat(smMsgBuf, (char *)daybuf);

		}
		else
		{
			len = smppEncodeASCtoUC2((char *)daybuf,bcd_buf,len);
			memcpy(&smMsgBuf[totalLen],bcd_buf,len);
		}	
		totalLen += len;

		switch(calltype)
		{
			case 2:
			sprintf(buf," %s ",getSmsSentenceNative(0,SMS_FROM));
			break;
			default:
			sprintf(buf," %s ",getSmsSentenceNative(0,SMS_TO));
			break;
		}
		len = strlen(buf);
		memcpy(&smMsgBuf[totalLen],buf,len);
		totalLen += len;
		
		len = strlen(msisdn);
		
		//if(calltype == 2) /* mt call */ /* added by pierre for CLI */
		{
			cliManipulationFunction(msisdn,caller);
			len = strlen(msisdn);
		}
	
		if(dataCoding ==0)
		{
			strcat(smMsgBuf,msisdn);
		}
		else
		{
			len = smppEncodeASCtoUC2(msisdn,bcd_buf,len);
			memcpy(&smMsgBuf[totalLen],bcd_buf,len);
		}	
		totalLen += len;
	}//if(callType>0)
	
	if((flag & 0x80))
	{
		sprintf(buf,",%s",getSmsSentenceNative(0,SMS_RECHARGE_ON));

		len = strlen(buf);
		memcpy(&smMsgBuf[totalLen],buf,len);
		totalLen += len;
	}
/*
DebugMsg(debugmib.display.asciiOut,"TotalLen: %03d-%03d,DCS:%d",
	totalLen,strlen(smMsgBuf),dataCoding);
buf[0]=0;
for(len=0;len<totalLen; len++)
{
	sprintf(tmpbuf,"%02x ",smMsgBuf[len]);
	strcat(buf,tmpbuf);
}
DebugMsg(debugmib.display.asciiOut,"msg: %s",buf);
*/
	if(totalLen > 254) totalLen = 254;
	smppSendMOSM(caller, called, (u_char *)smMsgBuf, totalLen, dataCoding);

    return 1;
 }
 
 
 
int GetAccountPromptInfo(_account_info *caller_info,char *prompt_info)
{
    long 			balance, promotion;
    char            expire_date[64]={0},promotion_expire_date[64]={0};
   	char            buf[2*MAX_SMS_LEN]="",buf2[4*MAX_SMS_LEN]="";
    int             len,totalLen, tmp_len=0;

    PutLogFunID("GetAccountPromptInfo");

	memset(buf,0,MAX_SMS_LEN*2);
	memset(buf2,0,MAX_SMS_LEN*4);
	
	AsciiTime(expire_date,GetExpiration(caller_info));		
	AsciiTime(promotion_expire_date,GetPromotionExpiration(caller_info));		
	balance = GetBalanceWithOutPromotion(caller_info->head,caller_info->tail);	
	promotion = GetPromotion(caller_info);
 	/******************** balance **************************************/
	sprintf(buf,"%s",getSmsSentenceNative(0,SMS_ACCOUNT_BALANCE));
	len = strlen(buf);
	
	strcpy(buf2,buf);
	totalLen = len;

	memset(buf,0,MAX_SMS_LEN*2);
	len = fillMoneyAndDate(balance,expire_date,0,0, (u_char *)buf);
	strcat(buf2,buf);
	totalLen += len;

	tmp_len = totalLen;
	/******************** balance **************************************/

	/******************** promotion **************************************/
	if(promotion > 0)
	{
		memset(buf,0,MAX_SMS_LEN*2);
		sprintf(buf,"%s",getSmsSentenceNative(0,SMS_ACCOUNT_PROMOTION));
		len = strlen(buf);

		strcat(buf2,buf);
		totalLen += len;
			
		memset(buf,0,MAX_SMS_LEN*2);
		len = fillMoneyAndDate(promotion,promotion_expire_date,0,0, (u_char *)buf);
		strcat(buf2,buf);
		totalLen += len;
	}
	memcpy(prompt_info,buf2,240);
	prompt_info[240]=0;
	/******************** promotion **************************************/
    return tmp_len;
}


int smppSendActivicationInfo(_account_info *caller_info, char *called)
{
	char account_info[256], msg_send[560], notify_sms[256];
	
	GetAccountPromptInfo(caller_info, account_info);

	sprintf(notify_sms,"%s", getSmsSentenceNative(0,SMS_ACTIVATION_NOTIFY));

	if(strlen(notify_sms))
		sprintf(msg_send, "%s %s, %s", notify_sms, caller_info->number, account_info);
	else
		sprintf(msg_send, "%s",account_info);

	smppSendMOSM((char *)caller_info->number, called, (u_char *)msg_send, strlen(msg_send), 0);
	return 1;
}

int pps_smpp_state(u_short portid)
{
	int retval = 0;
	_state_data         	*shmp = (_state_data *) smshm_ptr->state_data;    

	if(portid>=MAX_FSM_PROCESS)
	{
		return -1;
	}


	if(portid>=MAX_FSM_PROCESS)
	{
		return -1;
	}
	
	shmp += (portid);
	shmp->process_info.duration ++;
	
	if(shmp->process_info.funnel ++ > ONE_MINUTE_STATE_TIME)
	{
		if((logMsgFlag & 0x01) || shmp->process_info.logMsisdn)
		{
			DebugMsg(debugmib.display.asciiOut,
				"\n\33[31m portid: %d %s timeout\33[0m", portid, __FUNCTION__);
		}		
		return -1;			
	}
	
	switch(shmp->process_info.sub_state)
	{
		case S_SMPP_IDLE:
			retval = -1;
		break;
		case S_SMPP_REST_QUERY:
			retval = pps_rest_query_process(portid);
		break;
		case S_SMPP_REST_TOPUP:
			retval = pps_rest_topup_process(portid);
			if(retval == -1)
			{
				/*
				char failedNote[]={"Refill failed, check PIN and try again."};
				smppSendMOSM((char *)shmp->caller_info.number,
					(char *)shmp->origin_info.number,
					(u_char *)failedNote,
					strlen(failedNote),
					0);	
				*/
			}
		break;
		case S_SMPP_REST_TRANSFER:
			retval = pps_rest_transfer_process(portid);
		break;
		default:
			retval = -1;
		break;
	}
	
	return retval;
}
