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

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

#include "../include/ppsMibTable.h"
#include "../include/mfunc.h"

extern int     xappCheckIfLocalMSISDN(char *msisdn);

extern	PPSParamStruct	ppsParam;
extern	struct timeval 	tvnow;
extern _ram_data    *ramshm_ptr;
extern 	_state_machine	*smshm_ptr;;

extern	int SendUSSDRequest(u_short portid, char *ussd_info);
extern 	int smppSendMsg(u_char *peerNumber,u_char *msg,u_char serviceNumPosition);
extern int SendPSSREnd(u_short portid, char *ussd_info);


static int SendCMBRequest(u_short portid,u8 ussdSmsFlag)
{
	char 			tmp[MAX_SMS_LEN*4];
	_state_data 	*shmp;
	
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	
	strcpy(tmp, (char *)getSmsSentenceNative(0,SMS_CMB_PROMPT));
	switch(ussdSmsFlag)
	{
		case USSD_TRANSFER:
			SendUSSDRequest(portid,tmp);
		break;
		case SMS_TRANSFER:
		default:
			smppSendMsg(shmp->caller_info.number, (u_char *)tmp,CallMeBackPosition);
		break;
	}
	return 1;
}


static int SendCMBNotifyDestRequest(u_short portid,u8 ussdSmsFlag)
{
	//u_char 		tmpMsisdn[32],tmpMsisdn1[32];
	_state_data 	*shmp;
	char			dayTimeReadable[32]={0}, tmp[MAX_SMS_LEN*4];

	if(portid>=MAX_FSM_PROCESS) return 0;
	
	ppsgetdaytime_readFormat(dayTimeReadable);	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;

	cliManipulationFunction((char *)shmp->caller_info.number, (char *)shmp->process_info.ppsService.callMeBack.destNumber);
	strcpy(tmp, (char *)shmp->caller_info.number);
	
	strcat(tmp, (char *)getSmsSentenceNative(0,SMS_CMB_NOTIFY));
	sprintf(tmp+strlen(tmp)," %s %s ", (char *)getSmsSentenceNative(0,SMS_ON), dayTimeReadable);	
	strcat(tmp, (char *)getSmsSentenceNative(0,SMS_CMB_AD));
	smppSendMOSM((char *)shmp->process_info.ppsService.callMeBack.destNumber,
				(char *)shmp->caller_info.number,
				(u_char *)tmp,
				strlen(tmp),
				0
				);
	return 1;
}

static int SendReinputCMBRequest(u_short portid,u8 ussdSmsFlag)
{
	char 			tmp[MAX_SMS_LEN*4] = {0};
	_state_data 	*shmp;
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	
	strcpy(tmp, (char *)getSmsSentenceNative(0,SMS_FORMAT_ERROR));
	strcat(tmp, (char *)getSmsSentenceNative(0,SMS_CMB_PROMPT));
	switch(ussdSmsFlag)
	{
		case USSD_TRANSFER:
			SendUSSDRequest(portid,tmp);
		break;
		case SMS_TRANSFER:
		default:
			smppSendMsg(shmp->caller_info.number, (u_char *)tmp,CallMeBackPosition);
		break;
	}
	return 1;
}

static int SendCMBDstNumberErrorRequest(u_short portid,u8 ussdSmsFlag)
{
	char 			tmp[MAX_SMS_LEN*4] = {0};
	_state_data 	*shmp;
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	
	strcpy(tmp, (char *)getSmsSentenceNative(0,SMS_INVALID_NUMBER));
	strcat(tmp, (char *)getSmsSentenceNative(0,SMS_CMB_PROMPT));

	switch(ussdSmsFlag)
	{
		case USSD_TRANSFER:
			SendUSSDRequest(portid,tmp);
		break;
		case SMS_TRANSFER:
		default:
			smppSendMsg(shmp->caller_info.number, (u_char *)tmp,CallMeBackPosition);
		break;
	}
	return 1;
}

static int SendCMBLimited(u_short portid,u8 ussdSmsFlag)
{
	char tmp[MAX_SMS_LEN*4] = {0};
	_state_data 	*shmp;


	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
		
	strcpy(tmp, (char *)getSmsSentenceNative(0,SMS_CMB_REACH_MAX_LIMIT));
	switch(ussdSmsFlag)
	{
		case USSD_TRANSFER:
			SendPSSREnd(portid,tmp);
		break;
		case SMS_TRANSFER:
		default:			
			smppSendMsg(shmp->caller_info.number, (u_char *)tmp,CallMeBackPosition);
		break;
	}
	return 1;
}

static int getCMBInfo(u_short portid,u_char *ussd_data)
{/*	format:number*amount	*/
	_state_data 	*shmp;
	u8				i,len;
	
	PutLogFunID("getTransferInfo");

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	
	len = strlen((char *)ussd_data);
	if(len <= 0)
		return -1;
	for(i = 0;i < len;i++)
	{/*	only  '0' - '9'  are allowed		*/
		if(ussd_data[i] < '0' || ussd_data[i] > '9')
			return -1;
	}
	
	CutUnknownPrefix((char *)ussd_data);
	if(strlen((char *)ussd_data)>MAX_ASCII_NUMBER_LEN)
		return -1;
		
	if(!strcmp((char *)shmp->caller_info.number, (char *)ussd_data))
	{
		return -2;
	}
	strcpy((char *)shmp->process_info.ppsService.callMeBack.destNumber, (char *)ussd_data);
	return 1;
}

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

static  u8 getCMBState(u_short portid)
{
	_state_data 	*shmp;

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	
	return shmp->process_info.ppsService.callMeBack.state;
}

void clearCMBInfo(u_short portid)
{
	_state_data 	*shmp;
	
	PutLogFunID("clearTransferInfo");

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;

	memset(&shmp->process_info.ppsService.callMeBack,0,sizeof(CMB));
}

static BOOL checkIfLocalNumber(u_char *number)
{
	if(xappCheckIfLocalMSISDN((char *)number) == 1)
		return TRUE;
	return FALSE;
}

static BOOL checkCMBDestNumber(u_short portid)
{
	_state_data 	*shmp;
	_account_info 	account;
	
	PutLogFunID("clearTransferInfo");

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;

	strcpy((char *)account.number, (char *)shmp->process_info.ppsService.callMeBack.destNumber);
	return checkIfLocalNumber(account.number);
}


int saveCMBSMSACK(u_short portid,u_char *sms)
{
	_state_data 	*shmp;
	u8				len;
	
	PutLogFunID("saveSMSACK");

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;

	len = strlen((char *)sms) % MAX_INPUT_SMS;
	
	memcpy(shmp->process_info.ppsService.callMeBack.inputSMS,sms,len);
	shmp->process_info.ppsService.callMeBack.inputSMS[len] = 0;
	shmp->process_info.ppsService.callMeBack.smsTrigger = TRUE;
	return len;
}


static int getCMBSMSACK(u_short portid,u_char *sms)
{
	_state_data 	*shmp;

	PutLogFunID("getSMSACK");

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;

	if(shmp->process_info.ppsService.callMeBack.smsTrigger == FALSE)
		return 0;
	
	shmp->process_info.ppsService.callMeBack.smsTrigger = FALSE;
	strcpy((char *)sms, (char *)shmp->process_info.ppsService.callMeBack.inputSMS);
	return strlen((char *)sms);
}

extern BOOL isPrepaidUser(_account_info *ptr);
static int isCMBAllowed(u_short portid)
{
	_state_data 			*shmp;
	COSTable				*cosTableItem;
	int						cosID=0,cmbTimes;
	BOOL					balanceExpired=FALSE,promotionExpired=FALSE;
	float					balance = 0;
	
	PutLogFunID("isCMBAllowed");
	
	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;	

	if(isPrepaidUser(&shmp->caller_info) == TRUE)
  		cosID = GetLocalCosID(&shmp->caller_info);
	else
	{
		pps_print_event(shmp->process_info.logMsisdn,"[%05d]postpaid user %s ,cmb fail",portid,shmp->caller_info.number);
		return 1;
	}

	cosTableItem = &ppsParam.ppsTables.cosTable[cosID];
	
	if(cosTableItem->serviceControl[0].callMeBackMaxTimes > 0)
	{	
		cmbTimes = GetCMBTimes(&shmp->caller_info);
		if(cmbTimes >= cosTableItem->serviceControl[0].callMeBackMaxTimes)
		{		
			pps_print_error(shmp->process_info.logMsisdn,"[%05d]user %s reach max call me back times",portid,shmp->caller_info.number);
			return 2;
		}
	}
	
	if(GetExpiration(&shmp->caller_info) <= tvnow.tv_sec)
	{
		balanceExpired = TRUE;		
	}
	else
		balance += GetBalanceWithOutPromotion(shmp->caller_info.head,shmp->caller_info.tail);
	if(GetPromotionExpiration(&shmp->caller_info) <= tvnow.tv_sec)	
		promotionExpired = TRUE;
	else
		balance += GetPromotion(&shmp->caller_info);
	
	if(balanceExpired && promotionExpired)
	{
		pps_print_error(shmp->process_info.logMsisdn,"[%05d]user %s account expired",portid,shmp->caller_info.number);
		return 3;
	}
	balance += GetOverdraft(&shmp->caller_info);	
	
	if(cosTableItem->serviceControl[0].callMeBackThreshold>0 &&
	         balance > cosTableItem->serviceControl[0].callMeBackThreshold)
		return 4;
	return 0;	
}

enum CMB_STATE{
	S_CMB_IDLE = 0,
	S_WART_INPUT_DESTNUMBER,
	S_CMB_CHECK_DSTNUMBER,
	S_CMB_PROCESSING,
	S_CMB_EXIT,
};

int callMebackUSSDProc(u_short portid)
{
	_state_data 	*shmp;
	u_char			ussd_data[256]={0};
	int 			result = 0;
	CMB				*callMeBack;
	
	PutLogFunID("callMebackUSSDProc");

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	
	switch(getCMBState(portid))
	{
		case S_CMB_IDLE:
			result = isCMBAllowed(portid);
			if(result != 0)
			{
				switch(result)
				{
					case 2:
						SendCMBLimited(portid,USSD_TRANSFER);
					break;
					default:
						//SendServiceNotApplied(portid,USSD_TRANSFER,CallMeBackPosition);
						shmp->process_info.u_error = _PPS_ERR_SERVICE_NOT_APPLIED;
					break;
				}
				changeCMBState(portid,S_CMB_EXIT);
				break;
			}
			clearCMBInfo(portid);			
			SendCMBRequest(portid,USSD_TRANSFER);
			changeCMBState(portid,S_WART_INPUT_DESTNUMBER);
		break;		
		case S_WART_INPUT_DESTNUMBER:
			result = getUssdACK(portid,ussd_data);
			switch(result)
			{
				case 0:
				break;
				case -1:					
					return SERVICE_EXIT_PPS;
				break;
				default:					
					ussd_data[result] = 0;
					result = getCMBInfo(portid,ussd_data);
					switch(result)
					{
						case -1:
							if(shmp->process_info.serviceInput++ >= SERVICE_INPUT_NUM)
								return SERVICE_EXIT_PPS;
						 	SendReinputCMBRequest(portid,USSD_TRANSFER);
						break;
						case -2:
							if(shmp->process_info.serviceInput++ >= SERVICE_INPUT_NUM)
								return SERVICE_EXIT_PPS;
						 	SendCMBDstNumberErrorRequest(portid,USSD_TRANSFER);
						break;
						default:
							changeCMBState(portid,S_CMB_CHECK_DSTNUMBER);
						break;
					}						
				break;
			}		
		break;		
		case S_CMB_CHECK_DSTNUMBER:
			if(checkCMBDestNumber(portid) == FALSE)
			{
				if(shmp->process_info.serviceInput++ >= SERVICE_INPUT_NUM)
				{
					return SERVICE_EXIT_PPS;
				}
				SendCMBDstNumberErrorRequest(portid,USSD_TRANSFER);
				changeCMBState(portid,S_WART_INPUT_DESTNUMBER);
			}
			else
				changeCMBState(portid,S_CMB_PROCESSING);
		break;				
		case S_CMB_PROCESSING:
			UpdateCMBTimes(&shmp->caller_info,1);
			SendCMBNotifyDestRequest(portid,SMS_TRANSFER);
			changeCMBState(portid,S_CMB_EXIT);
		break;		
		case S_CMB_EXIT:
			clearCMBInfo(portid);
			return SERVICE_EXIT_PPS;
		break;
	}
	return SERVICE_KEEP_STATE;
}

int callMebackSMSProc(u_short portid)
{
	_state_data 	*shmp;
	u_char			short_message[256];
	int 			result = 0;
	CMB				*callMeBack;
	
	PutLogFunID("callMebackSMSProc");

	shmp = (_state_data *)&smshm_ptr->state_data;
	shmp += portid;
	
	switch(getCMBState(portid))
	{
		case S_CMB_IDLE:
			result = isCMBAllowed(portid);
			if(result != 0)
			{
				switch(result)
				{
					case 2:
						SendCMBLimited(portid,SMS_TRANSFER);
					break;
					default:
						SendServiceNotApplied(portid,SMS_TRANSFER,CallMeBackPosition);						
					break;
				}
				clearCMBInfo(portid);
				return SERVICE_RETURN_TO_PARENT;
			}
			changeCMBState(portid,S_WART_INPUT_DESTNUMBER);
		case S_WART_INPUT_DESTNUMBER:
			result = getCMBSMSACK(portid,short_message);
			switch(result)
			{
				case 0:
				break;
				case -1:					
					return SERVICE_EXIT_PPS;
				break;
				default:					
					short_message[result] = 0;
					result = getCMBInfo(portid,short_message);
					switch(result)
					{
						case -1:
							if(shmp->process_info.serviceInput++ >= SERVICE_INPUT_NUM)	
								return SERVICE_EXIT_PPS;
						 	SendReinputCMBRequest(portid,SMS_TRANSFER);
							return SERVICE_EXIT_PPS;
						case -2:
							if(shmp->process_info.serviceInput++ >= SERVICE_INPUT_NUM)	
								return SERVICE_EXIT_PPS;
						 	SendCMBDstNumberErrorRequest(portid,SMS_TRANSFER);
							return SERVICE_EXIT_PPS;
						default:
							changeCMBState(portid,S_CMB_CHECK_DSTNUMBER);
						break;
					}			
				break;
			}		
		case S_CMB_CHECK_DSTNUMBER:
			if(checkCMBDestNumber(portid) == FALSE)
			{				
				SendCMBDstNumberErrorRequest(portid,SMS_TRANSFER);
				return SERVICE_EXIT_PPS;
			}
			else
				changeCMBState(portid,S_CMB_PROCESSING);
		case S_CMB_PROCESSING:
			UpdateCMBTimes(&shmp->caller_info,1);
			SendCMBNotifyDestRequest(portid,SMS_TRANSFER);
			changeCMBState(portid,S_CMB_EXIT);
		case S_CMB_EXIT:
			clearCMBInfo(portid);
			return SERVICE_RETURN_TO_PARENT;
		break;
		}
	return SERVICE_KEEP_STATE;
}



