
#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	PPSParamStruct	ppsParam;
extern	struct timeval 	tvnow;
extern _ram_data    *ramshm_ptr;

extern void				DebugMsg(void *ptr, const char *fmt, ...);
extern u_short			debug_messages;
extern int		  		logMsgFlag;

enum PROMOTION_USED_SERVICE{
	PROMOTION_USED_CALL,
	PROMOTION_USED_SMS,
	PROMOTION_USED_GPRS,
};

enum PROMOTION_USED_RANGE{
	MO_LOCAL,
	MO_NDD,
	MO_IDD,
};


enum _TIER_RATING_PLAN{
	BILL_TIER_PLAN_A = 0,
	BILL_TIER_PLAN_B,
	BILL_TIER_PLAN_C,
};


BOOL isPromotionCanBeUsedForRoDebit(_account_info 	*shmp)
{
	if(shmp == NULL) return 0;
	COSTable					*cosTableItem;
	int 						cos_id=0;
		
	cos_id = GetAccountCosID(shmp)%MaxCosTableItem;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];			
	
	return cosTableItem->tariff[0].promotionUsedForRoDebit;
}

BOOL isPromotionCanBeUsedAtThisPoint(_state_data 	*shmp)
{
	COSTable					*cosTableItem;
	int 						cos_id=0;
	promotion_plan_control		*pPromotionControl;
	u8 							serviceType,call_type;
		
	cos_id = GetAccountCosID(&shmp->caller_info)%MaxCosTableItem;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];			
	pPromotionControl = &cosTableItem->tariff[0].promotionUseControl;

	serviceType = shmp->context_info.callType;	
	switch(serviceType)
	{
		case CT_MO_CALL:
			call_type = shmp->process_info.tariff.call_type;
			switch(call_type)
			{
				case MO_LOCAL:
					return pPromotionControl->usedForCall.LOCAL;
				break;				
				case MO_NDD:
					return pPromotionControl->usedForCall.NDD;
				break;
				case MO_IDD:
					return pPromotionControl->usedForCall.IDD;
				break;					
			}
		break;
		case CT_MO_SM:
			call_type = shmp->process_info.smsTariff.call_type;
			switch(call_type)
			{
				case MO_LOCAL:
					return pPromotionControl->usedForSMS.LOCAL;
				break;				
				case MO_NDD:
					return pPromotionControl->usedForSMS.NDD;
				break;
				case MO_IDD:
					return pPromotionControl->usedForSMS.IDD;
				break;					
			}
		break;
		case CT_GPRS:
			return pPromotionControl->usedForGPRS.allowed;
		break;
		case CT_RO_DEBIT:
			return cosTableItem->tariff[0].promotionUsedForRoDebit;
		break;
		default:
			return TRUE;
		break;
	}
	return TRUE;
}

BOOL isPromotionCanBeTransfered(_account_info      *accountInfo)
{
	COSTable					*cosTableItem;
	int 						cos_id=0;
	promotion_plan_control		*pPromotionControl;
		
	cos_id = GetAccountCosID(accountInfo)%MaxCosTableItem;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];			
	pPromotionControl = &cosTableItem->tariff[0].promotionUseControl;

	return pPromotionControl->transfer.transfer;
}

BOOL isPromotionFirstTransfered(_account_info      *accountInfo)
{
	COSTable					*cosTableItem;
	int 						cos_id=0;
	promotion_plan_control		*pPromotionControl;
	
	cos_id = GetAccountCosID(accountInfo)%MaxCosTableItem;
	cosTableItem = &ppsParam.ppsTables.cosTable[cos_id];			
	pPromotionControl = &cosTableItem->tariff[0].promotionUseControl;

	return pPromotionControl->transfer.transferPrioriy;
}


double GetBalanceWithPromotionControl(_state_data 	*stateProc,_account_info *ptr)
{
    _prepaid_data    *shmp=&ramshm_ptr->prepaid_data;
	double			balance=0;
	
	PutLogFunID("GetBalanceWithPromotionControl");

    if (ptr->head < MSISDN_HEAD_NUM && ptr->tail < UNIT_STORE_ACCOUNT)
	{
	    if(shmp->prepaid_info[ptr->head][ptr->tail].balance>=ppsParam.serviceControl.maxBalance)// balance overflow prevetion
			shmp->prepaid_info[ptr->head][ptr->tail].status = T_SUSPEND;
		if(shmp->prepaid_info[ptr->head][ptr->tail].msisdn[MAX_MSISDN_BLEN-1]<=2)//  msisdn length field
			return 0;
		if(GetExpiration(ptr) > tvnow.tv_sec)// check validity date
		{
			balance = shmp->prepaid_info[ptr->head][ptr->tail].balance;
		}
		else
			pps_print_error(stateProc->process_info.logMsisdn,"[%05d]%s balance validity expired",stateProc->process_info.portid,ptr->number);
		if(isPromotionCanBeUsedAtThisPoint(stateProc))
		{
			if(GetPromotionExpiration(ptr)> tvnow.tv_sec)	
				balance += shmp->prepaid_info[ptr->head][ptr->tail].promo_balance ;
			else				
				; //pps_print_error(stateProc->process_info.logMsisdn,"[%05d]%s promotion validity expired",stateProc->process_info.portid,ptr->number);
		}
        return balance;
	}
    return 0;
}

static void supplementaryServiceChangeBasciFee(_state_data *shmp,_bill_tariff 	*ptar)
{
	if(shmp->context_info.callType == CT_MF_CALL)
	{
		if(shmp->process_info.poolCall)
		{
			ChangeTariffToPoolCallTariff(shmp,ptar);
			#if DEBUG
			if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
			{
				DebugMsg(debugmib.display.asciiOut,"Pool call!");
			}
			#endif

		}
		else if(shmp->process_info.cugCall)
		{
			if(shmp->process_info.favoriteCall)
			{
				ChangeTariffToCfCugFavTariff(shmp,ptar);
				#if DEBUG
				if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
				{
					DebugMsg(debugmib.display.asciiOut,"CF + CUG + Favorite call!");
				}
				#endif

			}
			else
			{
				ChangeTariffToCfCugTariff(shmp,ptar);
				#if DEBUG
				if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
				{
					DebugMsg(debugmib.display.asciiOut,"CF + CUG  call!");
				}
				#endif

			}
		}
		else if(shmp->process_info.favoriteCall)
		{
			ChangeTariffToCfFavTariff(shmp,ptar);
			#if DEBUG
			if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
			{
				DebugMsg(debugmib.display.asciiOut,"CF + Favorite call!");
			}
			#endif

		}
		else
		{			
			ChangeTariffToCfTariff(shmp,ptar);
			#if DEBUG
			if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
			{
					DebugMsg(debugmib.display.asciiOut,"CF call!");
			}
			#endif
		}
		pps_print_event(shmp->process_info.logMsisdn,"[%05d]CF service,replace basic fee with common fee",
							shmp->process_info.portid);
	}//MF CALL
	else if(shmp->context_info.callType == CT_MO_CALL)
	{
		if(shmp->process_info.poolCall)
		{
			ChangeTariffToPoolCallTariff(shmp,ptar);
			#if DEBUG
			if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
			{
				DebugMsg(debugmib.display.asciiOut,"Pool call!");
			}
			#endif
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]Pool call service,replace basic fee with common fee",
							shmp->process_info.portid);
		}
		else if(shmp->process_info.cugCall)		
		{
			if(shmp->process_info.favoriteCall)
			{
			   ChangeTariffToCugFavTariff(shmp,ptar);
				#if DEBUG
				if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
				{
					DebugMsg(debugmib.display.asciiOut,"CUG + Favorite call!");
				}
				#endif
			}
			else
			{
			   	ChangeTariffToCugTariff(shmp,ptar);
				#if DEBUG
				if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
				{
					DebugMsg(debugmib.display.asciiOut,"CUG call!");
				}
				#endif
			}
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]CUG service,replace basic fee with common fee",
							shmp->process_info.portid);
		}
		else if(shmp->process_info.favoriteCall)
		{
			if(shmp->context_info.callType != CT_MT_CALL)
			{
				ChangeTariffToFavoriteCallTariff(shmp,ptar);//
			}
			#if DEBUG
			if((logMsgFlag & 0x02)|| shmp->process_info.logMsisdn)
			{
				DebugMsg(debugmib.display.asciiOut,"Favorite call!");
			}
			#endif
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]Favorite call service,replace basic fee with common fee",
							shmp->process_info.portid);
		}
	}
		
		
	if(shmp->process_info.roamingFlag == 1)
	{
	   	if(shmp->context_info.callType == CT_MT_CALL)
	   	{
	      	ChangeTariffToMTRoamingTariff(shmp,ptar);
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]MT Roaming service,change basic fee with roaming fee,long fee",
							shmp->process_info.portid);
	   	}
		else
		{
		    ChangeTariffToMORoamingTariff(shmp,ptar);
			pps_print_event(shmp->process_info.logMsisdn,"[%05d]MO Roaming service,replace basic fee with common fee",
							shmp->process_info.portid);
		}		
	}		
}

static  int getTierIndex(_state_data *shmp,_bill_tariff *ptar,u32 talkTime)
{
	TIER_RATING		*pTierRating;
	u8				tierNum,tierIndex;
	u32				tierAUnits,tierBUnits,tierCUnits;
	u32				duration,duration_units,tierCycle;
	int             unit_time=60;

	
	pTierRating = &ptar->tierRating;
	tierNum = pTierRating->tierNumber;
	if ((unit_time = ptar->unit_time) <= 0) 
        unit_time = 60;	
	
	if(talkTime >= ptar->free_time)		
		duration = talkTime - ptar->free_time;
	else
	{
		pps_print_event(shmp->process_info.logMsisdn,"[%05d]get tier index error,free time,no need to charge,use tier 0",
							shmp->process_info.portid);
		return BILL_TIER_PLAN_A;
	}
	
	duration_units = duration / unit_time;
	switch(tierNum)
	{
		case BILL_TIERS_ONE:
			tierIndex = BILL_TIER_PLAN_A;
		break;
		case BILL_TIERS_TWO:
			tierAUnits = pTierRating->rating[BILL_TIER_PLAN_A].units;
			tierBUnits = pTierRating->rating[BILL_TIER_PLAN_B].units;
			if(tierAUnits == 0 || tierBUnits == 0)
			{
				tierIndex = BILL_TIER_PLAN_A;
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]get tier index,Tier level is 1,but the units of tier 0/1 is 0,only use tier 0",
								shmp->process_info.portid);
			}
			else
			{
				tierCycle = duration_units%(tierAUnits+tierBUnits);
				if(tierCycle >= tierAUnits)
					tierIndex = BILL_TIER_PLAN_B;
				else
					tierIndex = BILL_TIER_PLAN_A;
			}
		break;
		case BILL_TIERS_TRREE:
			tierAUnits = pTierRating->rating[BILL_TIER_PLAN_A].units;
			tierBUnits = pTierRating->rating[BILL_TIER_PLAN_B].units;
			tierCUnits = pTierRating->rating[BILL_TIER_PLAN_C].units;
			if(tierAUnits == 0 || tierBUnits == 0 ||tierCUnits == 0)
			{
				tierIndex = BILL_TIER_PLAN_A;
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]get tier index,Tier level is 2,but the units of tier 0/1/2 is 0,only use tier 0",
								shmp->process_info.portid);
			}
			else
			{
				tierCycle = duration_units%(tierAUnits+tierBUnits+tierCUnits);
				if(tierCycle >= (tierBUnits + tierAUnits))
					tierIndex = BILL_TIER_PLAN_C;
				else if(tierCycle >= tierAUnits)
					tierIndex = BILL_TIER_PLAN_B;
				else
					tierIndex = BILL_TIER_PLAN_A;
			}
		break;
		default:
			tierIndex = BILL_TIER_PLAN_A;
		break;
	}	
	return tierIndex;
}

u_char getCurrentTier(_state_data 	*shmp,_bill_tariff *ptar)
{
	u32 talkTime;
		
	talkTime = shmp->process_info.duration/CYCLE_STATE_INTERVAL;
	return getTierIndex(shmp,ptar,talkTime);
}

float getCurrentFee(_state_data 	*shmp)
{
	u_char 			tier;
	TIER_RATING		*pTierRating;
	_bill_tariff 	*ptar;

	ptar = &shmp->process_info.tariff;
	tier = getCurrentTier(shmp,ptar);
	supplementaryServiceChangeBasciFee(shmp,ptar);
	pTierRating = &ptar->tierRating;
	return pTierRating->rating[tier].base_fee + pTierRating->rating[tier].long_distance_fee;
}

float BillingFormula(_state_data *shmp,_bill_tariff *ptar,int startTime)
{
    float          	num=0;
	u8				index;
	TIER_RATING		*pTierRating;
	
	PutLogFunID("BillingFormula");

	pTierRating = &ptar->tierRating;

	index = getTierIndex(shmp,ptar,startTime);
	supplementaryServiceChangeBasciFee(shmp,ptar);
    num =  pTierRating->rating[index].base_fee + pTierRating->rating[index].long_distance_fee ;
	pps_print_event(shmp->process_info.logMsisdn,"[%05d]current tier is %d,basic fee:%0.4f,long fee:%0.4f",
		shmp->process_info.portid,index,pTierRating->rating[index].base_fee,pTierRating->rating[index].long_distance_fee);
    return num;
}

float TimeToCharge(_state_data *shmp,_bill_tariff *ptar, int duration,int begin_time, int init_bill)
{
    int              i=0,j=0;
    int              discount=0;
	float			 charge=0;
    int              itimes=0, unit_time=60, curr_time=0,startTime;
    int				 truncationFlag=0;

	PutLogFunID("TimeToCharge");

	if(duration<=0) return (charge = 0);
    if ((unit_time = ptar->unit_time) <= 0) 
        unit_time = 60;
    itimes = (ceil)((double) duration / unit_time);
	startTime = shmp->process_info.duration/CYCLE_STATE_INTERVAL;
	
	if(ppsParam.serviceControl.truncation_flag)
	{
		charge = BillingFormula(shmp,ptar,startTime);
		if(charge == floorf(charge))
			truncationFlag = 1;
	}
	charge = 0;
    for (i=1; i<=itimes; i++) 
	{
        curr_time = begin_time + unit_time * (i - 1);
	if(OCS_with_external_CRM())//query tariff from CRM database
	  discount = shmp->process_info.rest_if.tariff_discount;
	else		
        discount = GetDiscount(curr_time,ptar->discountID);
        if (i <= ptar->mini_unit && (!init_bill)) 
		{
			for(j=0;j < ptar->mini_unit;j++)
			{
            	charge += BillingFormula(shmp,ptar,startTime);
				startTime += unit_time;
			}
            i = ptar->mini_unit;
        } 
		else 
        {
            charge  += BillingFormula(shmp,ptar,startTime);
			startTime += unit_time;			
        }
    }    
	
	if(!init_bill)
	{
		charge = (charge + ptar->adjustment)*discount/100;
	}
	else
	{
		charge = (charge * discount)/100;
	}
	if(truncationFlag )
		return (floorf(charge));
    else
		return (charge);
}

float caculateMiniCharge(_state_data 	*shmp,_bill_tariff *ptar)
{
	float			charge=0;
    int             unit_time=60, curr_time=0,startTime,j;
	float 			discount;			
		
	charge = 0;
	curr_time = tvnow.tv_sec;
	if ((unit_time = ptar->unit_time) <= 0) 
        unit_time = 60;
	startTime = shmp->process_info.duration/CYCLE_STATE_INTERVAL;
	discount = GetDiscount(curr_time,ptar->discountID)/100;
	for(j=0;j < ptar->mini_unit;j++)
	{
     	charge += BillingFormula(shmp,ptar,startTime);
		startTime += unit_time;
	}
	return charge*discount;
}

static int caculateLeftTime(_state_data  *shmp,float	balance,_bill_tariff *ptar)
{
	float			charge=0;
    int             unit_time=60, curr_time=0,startTime;
	float 			discount;			
		
	charge = 0;
	curr_time = tvnow.tv_sec;
	if ((unit_time = ptar->unit_time) <= 0) 
        unit_time = 60;
	startTime = shmp->process_info.duration/CYCLE_STATE_INTERVAL;
	discount = GetDiscount(curr_time,ptar->discountID)/100;
	if(discount <= 0)
		return MAX_CALL_DURATION;
	charge += BillingFormula(shmp,ptar,startTime);
	while((charge*discount) <= balance)
	{		        
		startTime += unit_time;
		curr_time += unit_time;
		charge += BillingFormula(shmp,ptar,startTime);
	}
	return curr_time - tvnow.tv_sec;
}

int preLeftTime(_state_data 	*shmp,float	balance,_bill_tariff *ptar)
{	
	TIER_RATING		*pTierRating;
	TIER_INFO		*pTierInfo;
	u8				tierNum;
	u32				tierAUnits,tierBUnits,tierCUnits,S_UNITS_CYCLE;
	float				tierA_V_perUnit,tierB_V_perUnit,tierC_V_perUnit,units;
	long double		S_V_CYLCE;
	int             		unit_time=60,cycleTimes;
	float				maxDuration;
	float 			discount;
	
	pTierRating = &ptar->tierRating;
	tierNum = pTierRating->tierNumber;
	if ((unit_time = ptar->unit_time) <= 0) 
        unit_time = 60;
	
	discount = GetDiscount(tvnow.tv_sec,ptar->discountID)*1.0/100;
	if(discount <= 0.0001)
		return MAX_CALL_DURATION;
	
	switch(tierNum)
	{				
		case BILL_TIERS_TWO:
			pTierInfo = &pTierRating->rating[BILL_TIER_PLAN_A];
			tierA_V_perUnit = pTierInfo->base_fee + pTierInfo->long_distance_fee; 
			tierAUnits = pTierInfo->units;
			pTierInfo = &pTierRating->rating[BILL_TIER_PLAN_B];
			tierB_V_perUnit = pTierInfo->base_fee + pTierInfo->long_distance_fee; 
			tierBUnits = pTierInfo->units;
			
			S_V_CYLCE = tierA_V_perUnit*tierAUnits + tierB_V_perUnit*tierBUnits;
			if( S_V_CYLCE <= 0)
				return MAX_CALL_DURATION;
			if(tierAUnits == 0 || tierBUnits == 0)
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]Pre time,Tier level is 1,but the units of tier 0/1 is 0,only use tier 0",
								shmp->process_info.portid);
				break;
			}
			S_UNITS_CYCLE = tierAUnits + tierBUnits;
			cycleTimes = balance/(discount*S_V_CYLCE);

			maxDuration = cycleTimes*S_UNITS_CYCLE*unit_time;
			if(maxDuration < MAX_CALL_DURATION)
			{
				maxDuration = caculateLeftTime(shmp,balance,ptar);
			}		
			return maxDuration;
		break;
		case BILL_TIERS_TRREE:
			pTierInfo = &pTierRating->rating[BILL_TIER_PLAN_A];
			tierA_V_perUnit = pTierInfo->base_fee + pTierInfo->long_distance_fee; 
			tierAUnits = pTierInfo->units;
			pTierInfo = &pTierRating->rating[BILL_TIER_PLAN_B];
			tierB_V_perUnit = pTierInfo->base_fee + pTierInfo->long_distance_fee; 
			tierBUnits = pTierInfo->units;
			pTierInfo = &pTierRating->rating[BILL_TIER_PLAN_C];
			tierC_V_perUnit = pTierInfo->base_fee + pTierInfo->long_distance_fee; 
			tierCUnits = pTierInfo->units;
			
			S_V_CYLCE = tierA_V_perUnit*tierAUnits + tierB_V_perUnit*tierBUnits + tierC_V_perUnit*tierCUnits;
			if( S_V_CYLCE <= 0)
				return MAX_CALL_DURATION;
			if(tierAUnits == 0 || tierBUnits == 0 || tierCUnits == 0 )
			{
				pps_print_error(shmp->process_info.logMsisdn,"[%05d]Pre time,Tier level is 2,but the units of tier 0/1/2 is 0,only use tier 0",
								shmp->process_info.portid);
				break;
			}
			S_UNITS_CYCLE = tierAUnits + tierBUnits + tierCUnits;
			cycleTimes = balance/(discount*S_V_CYLCE);

			maxDuration = cycleTimes*S_UNITS_CYCLE*unit_time;	
			if(maxDuration < MAX_CALL_DURATION)
			{
				maxDuration = caculateLeftTime(shmp,balance,ptar);
			}				
			return maxDuration;
		break;		
	}	 

	pTierInfo = &pTierRating->rating[BILL_TIER_PLAN_A];
	tierA_V_perUnit = pTierInfo->base_fee + pTierInfo->long_distance_fee; 

	if( tierA_V_perUnit <= 0)
		return MAX_CALL_DURATION;

	units = (floorf)(balance/(discount*tierA_V_perUnit));
	maxDuration = units*unit_time;
	
	return maxDuration;
}



