<commit_before><?php
/***************************************************************************
 *
 * Class.RateEngine.php : RateEngine Functions for A2Billing
 * Written for PHP 4.x & PHP 5.X versions.
 *
 * A2Billing -- Asterisk billing solution.
 * Copyright (C) 2004, 2007 <NAME> <areski _atl_ gmail com>
 *
 * See http://www.asterisk2billing.org for more information about
 * the A2Billing project.
 * Please submit bug reports, patches, etc to <areski _atl_ gmail com>
 *
 * This software is released under the terms of the GNU Lesser General Public License v2.1
 * A copy of which is available from http://www.gnu.org/copyleft/lesser.html
 *
 ****************************************************************************/
 include_once (FSROOT."lib/Misc.php");

class RateEngine
{
	var $debug_st = 0;

	var $ratecard_obj = array();

	var $freetimetocall_left = array();
	var $freecall = array();
	var $package_to_apply= array();

	var $number_trunk		= 0;
	var $lastcost			= 0;
	var $lastbuycost		= 0;
	var $answeredtime 		= 0;
	var $real_answeredtime	= 0;
	var $dialstatus			= 0;
	var $usedratecard		= 0;
	var $webui 				= 1;
	var $usedtrunk			= 0;
	var $freetimetocall_used= 0;
	
	// List of dialstatus
	var $dialstatus_rev_list;
	

	/* CONSTRUCTOR */
	function RateEngine ()
	{
		$this -> dialstatus_rev_list = Constants::getDialStatus_Revert_List();
	}

	/* Reinit */
	function Reinit ()
	{
		$this -> number_trunk = 0;
		$this -> answeredtime = 0;
		$this -> real_answeredtime = 0;
		$this -> dialstatus = '';
		$this -> usedratecard = '';
		$this -> usedtrunk = '';
		$this -> lastcost = '';
		$this -> lastbuycost = '';
		
	}

	/*
		RATE ENGINE
		CALCUL THE RATE ACCORDING TO THE RATEGROUP, LCR - RATECARD
	*/
	function rate_engine_findrates (&$A2B, $phonenumber, $tariffgroupid)
	{
		global $agi;

		// Check if we want to force the call plan
		if (is_numeric($A2B->agiconfig['force_callplan_id']) && ($A2B->agiconfig['force_callplan_id'] > 0)){
			$A2B -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "force the call plan : ".$A2B->agiconfig['force_callplan_id']);
			$tariffgroupid = $A2B->tariff = $A2B->agiconfig['force_callplan_id'];
		}

		if ($this->webui){
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: ($tariffgroupid, $phonenumber)]");
		}


		/***  0 ) CODE TO RETURN THE DAY OF WEEK + CREATE THE CLAUSE  ***/

		$daytag = date("w", time()); // Day of week ( Sunday = 0 .. Saturday = 6 )
		$hours = date("G", time()); // Hours in 24h format ( 0-23 )
		$minutes = date("i", time()); // Minutes (00-59)
		if (!$daytag) $daytag=6;
		else $daytag--;
		if ($this -> debug_st) echo "$daytag $hours $minutes <br>";
		// Race condiction on $minutes ?!
		$minutes_since_monday = ($daytag * 1440) + ($hours * 60) + $minutes;
		if ($this -> debug_st) echo "$minutes_since_monday<br> ";
		//$sql_clause_days = " AND ".$daytable[$daytag]."='1' ";
		$sql_clause_days = " AND (starttime <= ".$minutes_since_monday." AND endtime >=".$minutes_since_monday.") ";

		/*

		SELECT tariffgroupname, lcrtype, idtariffgroup, cc_tariffgroup_plan.idtariffplan, tariffname, destination,

		cc_ratecard.id,
		dialprefix, destination, buyrate, buyrateinitblock, buyrateincrement, rateinitial, initblock, billingblock,
		connectcharge, disconnectcharge, stepchargea, chargea, timechargea, billingblocka, stepchargeb, chargeb,
		timechargeb, billingblockb, stepchargec, chargec, timechargec, billingblockc,

		cc_tariffplan.id_trunk AS tp_id_trunk, tp_trunk.trunkprefix AS tp_trunk, tp_trunk.providertech AS tp_providertech,
		tp_trunk.providerip AS tp_providerip, tp_trunk.removeprefix AS tp_removeprefix,
		cc_ratecard.id_trunk AS rc_id_trunk, rt_trunk.trunkprefix AS rc_trunkprefix, rt_trunk.providertech AS rc_providertech,
		rt_trunk.providerip AS rc_providerip, rt_trunk.removeprefix AS rc_removeprefix

		FROM cc_tariffgroup
		LEFT JOIN cc_tariffgroup_plan ON idtariffgroup=id
		LEFT JOIN cc_tariffplan ON cc_tariffplan.id=cc_tariffgroup_plan.idtariffplan
		LEFT JOIN cc_ratecard ON cc_ratecard.idtariffplan=cc_tariffplan.id
		LEFT JOIN trunk AS rt_trunk ON cc_ratecard.id_trunk=rt_trunk.id_trunk
		LEFT JOIN trunk AS tp_trunk ON cc_tariffplan.id_trunk=tp_trunk.id_trunk

		WHERE dialprefix=SUBSTRING('346586699595',1,length(dialprefix))
		AND startingdate<= CURRENT_TIMESTAMP AND (expirationdate > CURRENT_TIMESTAMP OR expirationdate IS NULL OR LENGTH(expirationdate)<5)
		AND startdate<= CURRENT_TIMESTAMP AND (stopdate > CURRENT_TIMESTAMP OR stopdate IS NULL OR LENGTH(stopdate)<5)
		ORDER BY LENGTH(dialprefix) DESC

		*/

		if (strlen($A2B->dnid)>=1) $mydnid = $A2B->dnid;
		if (strlen($A2B->CallerID)>=1) $mycallerid = $A2B->CallerID;

		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: CALLERID]\n".$A2B->CallerID."\n",0);

		if ($A2B->config["database"]['dbtype'] != "postgres") {
			$DNID_QUERY = "SELECT count(dnidprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix))";
			$result_sub = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $DNID_QUERY);
			if (!is_array($result_sub) || count($result_sub)==0) $nb_dnid = 0;
			else $nb_dnid = $result_sub[0][0];
			$DNID_SUB_QUERY = "AND 0 = $nb_dnid";

			$CALLERID_QUERY = "SELECT count(calleridprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix))";
			$result_sub = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $CALLERID_QUERY);
			if (!is_array($result_sub) || count($result_sub)==0) $nb_callerid = 0;
			else $nb_callerid = $result_sub[0][0];
			$CID_SUB_QUERY = "AND 0 = $nb_callerid";

		} else {
			$DNID_SUB_QUERY = "AND 0 = (SELECT count(dnidprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix)) ) ";

			$CID_SUB_QUERY = "AND 0 = (SELECT count(calleridprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix)) ) ";
		}


		// $prefixclause to allow good DB servers to use an index rather than sequential scan
		// justification at http://forum.asterisk2billing.org/viewtopic.php?p=9620#9620
		$max_len_prefix = min(strlen($phonenumber), 15);	// don't match more than 15 digits (the most I have on my side is 8 digit prefixes)
		$prefixclause = '(';
		while ($max_len_prefix > 0 ) {
			$prefixclause .= "dialprefix='".substr($phonenumber,0,$max_len_prefix)."' OR ";
			$max_len_prefix--;
		}
		$prefixclause .= "dialprefix='defaultprefix')";

		// match Asterisk/POSIX regex prefixes,  rewrite the Asterisk '_XZN.' characters to
		// POSIX equivalents, and test each of them against the dialed number
		$prefixclause .= " OR (dialprefix LIKE '&_%' ESCAPE '&' AND '$phonenumber' ";
		if ($A2B->config["database"]['dbtype'] != "postgres"){
			$prefixclause .= "REGEXP REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(CONCAT('^', dialprefix, '$'), ";
			$prefixclause .= "'X', '[0-9]'), 'Z', '[1-9]'), 'N', '[2-9]'), '.', '+'), '_', ''))";
		} else {
			$prefixclause .= "~* REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE('^' || dialprefix || '$', ";
			$prefixclause .= "'X', '[0-9]', 'g'), 'Z', '[1-9]', 'g'), 'N', '[2-9]', 'g'), E'\\\\.', '+', 'g'), '_', '', 'g'))";
		}
        // select group by 5 ... more easy to count
		$QUERY = "SELECT
		tariffgroupname, lcrtype, idtariffgroup, cc_tariffgroup_plan.idtariffplan, tariffname, 
        id_cc_prefix, cc_ratecard.id, dialprefix, id_cc_prefix, buyrate,
        buyrateinitblock, buyrateincrement, rateinitial, initblock, billingblock,
		connectcharge, disconnectcharge, stepchargea, chargea, timechargea,
        billingblocka, stepchargeb, chargeb,timechargeb, billingblockb, 
        stepchargec, chargec, timechargec, billingblockc,cc_tariffplan.id_trunk AS tp_id_trunk,
        tp_trunk.trunkprefix AS tp_trunk, tp_trunk.providertech AS tp_providertech,tp_trunk.providerip AS tp_providerip, tp_trunk.removeprefix AS tp_removeprefix,cc_ratecard.id_trunk AS rc_id_trunk,
        rt_trunk.trunkprefix AS rc_trunkprefix, rt_trunk.providertech AS rc_providertech,rt_trunk.providerip AS rc_providerip, rt_trunk.removeprefix AS rc_removeprefix, musiconhold,
		tp_trunk.failover_trunk AS tp_failover_trunk, rt_trunk.failover_trunk AS rt_failover_trunk,tp_trunk.addparameter AS tp_addparameter_trunk, rt_trunk.addparameter AS rt_addparameter_trunk, id_outbound_cidgroup,
        id_cc_package_offer,tp_trunk.status, rt_trunk.status, tp_trunk.inuse, rt_trunk.inuse, 
        tp_trunk.maxuse,  rt_trunk.maxuse,tp_trunk.if_max_use, rt_trunk.if_max_use,cc_ratecard.rounding_calltime AS rounding_calltime,
        cc_ratecard.rounding_threshold AS rounding_threshold,cc_ratecard.additional_block_charge AS additional_block_charge,cc_ratecard.additional_block_charge_time AS additional_block_charge_time, cc_ratecard.additional_grace AS additional_grace, cc_ratecard.minimal_cost AS minimal_cost,disconnectcharge_after

		FROM cc_tariffgroup
		RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=cc_tariffgroup.id
		INNER JOIN cc_tariffplan ON (cc_tariffplan.id=cc_tariffgroup_plan.idtariffplan )
		LEFT JOIN cc_ratecard ON cc_ratecard.idtariffplan=cc_tariffplan.id
		LEFT JOIN cc_trunk AS rt_trunk ON cc_ratecard.id_trunk=rt_trunk.id_trunk
		LEFT JOIN cc_trunk AS tp_trunk ON cc_tariffplan.id_trunk=tp_trunk.id_trunk

		WHERE cc_tariffgroup.id=$tariffgroupid AND ($prefixclause)
		AND startingdate<= CURRENT_TIMESTAMP AND (expirationdate > CURRENT_TIMESTAMP OR expirationdate IS NULL OR LENGTH(expirationdate)<5)
		AND startdate<= CURRENT_TIMESTAMP AND (stopdate > CURRENT_TIMESTAMP OR stopdate IS NULL OR LENGTH(stopdate)<5)
		$sql_clause_days
		AND idtariffgroup='$tariffgroupid'
		AND ( dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix)) OR (dnidprefix='all' $DNID_SUB_QUERY))
		AND ( calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix)) OR (calleridprefix='all' $CID_SUB_QUERY))
		ORDER BY LENGTH(dialprefix) DESC";

		$A2B->instance_table = new Table();
		$result = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $QUERY);


		if (!is_array($result) || count($result)==0) return 0; // NO RATE FOR THIS NUMBER

		if ($this->debug_st) echo "::> Count Total result ".count($result)."\n\n";
		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[rate-engine: Count Total result ".count($result)."]");

		// CHECK IF THERE IS OTHER RATE THAT 'DEFAULT', IF YES REMOVE THE DEFAULT RATES
		// NOT NOT REMOVE SHIFT THEM TO THE END :P
		$ind_stop_default = -1;
		for ($i=0;$i<count($result);$i++){
			if ( $result[$i][7] != 'defaultprefix'){
				$ind_stop_default = $i;
				break;
			}
		}
		// IMPORTANT TO CUT THE PART OF THE defaultprefix CAUSE WE WILL APPLY THE SORT ACCORDING TO THE RATE
		// DEFAULPERFIX IS AN ESCAPE IN CASE OF NO RATE IS DEFINED, NOT BE COUNT WITH OTHER DURING THE
		// SORT OF RATE
		if ($ind_stop_default>0) {
			$result_defaultprefix = array_slice ($result, 0, $ind_stop_default);
			$result = array_slice ($result, $ind_stop_default, count($result)-$ind_stop_default);
		}


		//if ($ind_stop_default>0)  array_rotate($result, $ind_stop_default);


		//1) REMOVE THOSE THAT HAVE A SMALLER DIALPREFIX
		$max_len_prefix = strlen($result[0][7]);
		for ($i=1;$i<count($result);$i++) {
			if ( strlen($result[$i][7]) < $max_len_prefix) break;
		}
		$result = array_slice ($result, 0, $i);


		//2) TAKE THE VALUE OF LCTYPE
		//LCR : According to the buyer price	-0 	buyrate [col 6]
		//LCD : According to the seller price	-1  rateinitial	[col 9]

		$LCtype = $result[0][1];

		// Thanks for the fix from the wiki :D next time email me, lol
		if ($LCtype==0) {
			//$result = $this -> array_csort($result,'6',SORT_ASC); GOTTYA!
			$result = $this -> array_csort($result,'9',SORT_ASC); //1
		} else {
			//$result = $this -> array_csort($result,'9',SORT_ASC); GOTTYA!
			$result = $this -> array_csort($result,'12',SORT_ASC); //1
		}


		// WE ADD THE DEFAULTPREFIX WE REMOVE BEFORE
		if ($ind_stop_default > 0) {
			$result = array_merge ($result, $result_defaultprefix);
		}


		// 3) REMOVE THOSE THAT USE THE SAME TRUNK - MAKE A DISTINCT
		//    AND THOSE THAT ARE DISABLED.
		$mylistoftrunk = array();
		for ($i=0;$i<count($result);$i++) {

			if ($result[$i][34] == -1) {
				$status = $result[$i][46];
				$mylistoftrunk_next[]= $mycurrenttrunk = $result[$i][29];
			} else {
				$status = $result[$i][47];
				$mylistoftrunk_next[]= $mycurrenttrunk = $result[$i][34];
			}

			// Check if we already have the same trunk in the ratecard
			if (($i==0 || !in_array ($mycurrenttrunk , $mylistoftrunk)) && $status == 1) {
				$distinct_result[] = $result[$i];
			}

			if ($status == 1)
				$mylistoftrunk[]= $mycurrenttrunk;
		}


		//4) $result[K][34]==-1 USE THE VALUES OF THE TRUNK OF $result[K][29]


		$this -> ratecard_obj = $distinct_result;
		$this -> number_trunk = count($distinct_result);

		// if an extracharge DID number was called increase rates with the extracharge fee
		if (strlen($A2B->dnid)>2 && is_array($A2B->agiconfig['extracharge_did']) && in_array($A2B->dnid, $A2B->agiconfig['extracharge_did']))
		{
			$fee=$A2B->agiconfig['extracharge_fee'][array_search($A2B->dnid, $A2B->agiconfig['extracharge_did'])];
			$buyfee=$A2B->agiconfig['extracharge_buyfee'][array_search($A2B->dnid, $A2B->agiconfig['extracharge_did'])];
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: Extracharge DID found: ".$A2B->dnid.", extra fee: ".$fee.", extra buy fee: ".$buyfee."]");
			for ($i=0; $i<count($this->ratecard_obj); $i++)
			{
				$this->ratecard_obj[$i][9] +=$buyfee;
				$this->ratecard_obj[$i][12]+=$fee;
			}
		}

		if ($this->debug_st) echo "::> Count Total distinct_result ".count($distinct_result)."\n\n";
		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: Count Total result ".count($distinct_result)."]");
		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: number_trunk ".$this -> number_trunk."]");

		return 1;
	}


	/*
		RATE ENGINE - CALCUL TIMEOUT
		* CALCUL THE DURATION ALLOWED FOR THE CALLER TO THIS NUMBER
	*/
	function rate_engine_all_calcultimeout (&$A2B, $credit)
	{
		global $agi;

		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_ALL_CALCULTIMEOUT ($credit)]");
		if (!is_array($this -> ratecard_obj) || count($this -> ratecard_obj)==0) return false;

		for ($k=0;$k<count($this -> ratecard_obj);$k++){
			$res_calcultimeout = $this -> rate_engine_calcultimeout ($A2B,$credit,$k);
			if ($this->webui)
				$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_ALL_CALCULTIMEOUT: k=$k - res_calcultimeout:$res_calcultimeout]");

			if (substr($res_calcultimeout,0,5)=='ERROR')	return false;
		}

		return true;
	}

	/*
		RATE ENGINE - CALCUL TIMEOUT
		* CALCUL THE DURATION ALLOWED FOR THE CALLER TO THIS NUMBER
	*/
	function rate_engine_calcultimeout (&$A2B, $credit, $K=0)
	{
		global $agi;

		$rateinitial 					= a2b_round (abs($this -> ratecard_obj[$K][12]));
		$initblock 						= $this -> ratecard_obj[$K][13];
		$billingblock 					= $this -> ratecard_obj[$K][14];
		$connectcharge 					= a2b_round (abs($this -> ratecard_obj[$K][15]));
		$disconnectcharge 				= a2b_round (abs($this -> ratecard_obj[$K][16]));
		$disconnectcharge_after         		= $this->ratecard_obj[$K][60];
		$stepchargea 					= $this -> ratecard_obj[$K][17];
		$chargea 						= a2b_round (abs($this -> ratecard_obj[$K][18]));
		$timechargea 					= $this -> ratecard_obj[$K][19];
		$billingblocka 					= $this -> ratecard_obj[$K][20];
		$stepchargeb 					= $this -> ratecard_obj[$K][21];
		$chargeb 						= a2b_round (abs($this -> ratecard_obj[$K][22]));
		$timechargeb 					= $this -> ratecard_obj[$K][23];
		$billingblockb 					= $this -> ratecard_obj[$K][24];
		$stepchargec 					= $this -> ratecard_obj[$K][25];
		$chargec 						= a2b_round (abs($this -> ratecard_obj[$K][26]));
		$timechargec 					= $this -> ratecard_obj[$K][27];
		$billingblockc 					= $this -> ratecard_obj[$K][28];
		// ****************  PACKAGE PARAMETERS ****************
		$id_cc_package_group 			= $this -> ratecard_obj[$K][45];
		$id_rate 						= $this -> ratecard_obj[$K][6];
		$initial_credit					= $credit;
		// CHANGE THIS - ONLY ALLOW FREE TIME FOR CUSTOMER THAT HAVE MINIMUM CREDIT TO CALL A DESTINATION

		$this -> freetimetocall_left[$K] = 0;
		$this -> freecall[$K] = false;
		$this -> package_to_apply [$K] = null;
		
		//CHECK THE PACKAGES TOAPPLY TO THIS RATES
		if($id_cc_package_group!=-1){
			
			//$table_packages = new Table("cc_package_group,cc_packgroup_package,cc_package_offer,cc_package_rate", "cc_package_offer.id, packagetype,billingtype,startday,freetimetocall");
			//$clause_packages= "cc_package_group.id= ".$id_cc_package_group." AND cc_package_group.id=cc_packgroup_package.packagegroup_id AND cc_packgroup_package.package_id = cc_package_offer.id AND cc_package_offer.id = cc_package_rate.package_id  AND cc_package_rate.rate_id = ".$id_rate;
			//$order_packages = "cc_package_offer.packagetype";
			//$sens_packages = "ASC";
			//$result_packages= $table_packages -> Get_list ($A2B -> DBHandle, $clause_packages, $order_packages, $sens_packages, null, null, null, null);
			$query_pakages ="SELECT cc_package_offer.id, packagetype,billingtype,startday,freetimetocall 
			FROM cc_package_group,cc_packgroup_package,cc_package_offer,cc_package_rate 
			WHERE cc_package_group.id= ".$id_cc_package_group." 
				AND cc_package_group.id=cc_packgroup_package.packagegroup_id 
				AND cc_packgroup_package.package_id = cc_package_offer.id 
				AND cc_package_offer.id = cc_package_rate.package_id  
				AND cc_package_rate.rate_id = ".$id_rate." ORDER BY packagetype ASC";
			$table_packages = new Table();
			$result_packages = $table_packages -> SQLExec ($A2B -> DBHandle, $query_pakages);
			$idx_pack = 0;
			if(!empty($result_packages))
			{ 
				$package_selected = false;
				while(!$package_selected && $idx_pack < count($result_packages)){
					$freetimetocall 	= $result_packages[$idx_pack]["freetimetocall"];
					$packagetype 		= $result_packages[$idx_pack]["packagetype"];
					$billingtype 		= $result_packages[$idx_pack]["billingtype"];
					$startday 			= $result_packages[$idx_pack]["startday"];
					$id_cc_package_offer= $result_packages[$idx_pack][0];
					if ($this -> debug_st) print_r("[ID PACKAGE  TO APPLY = $id_cc_package_offer]");
					switch($packagetype){
						// 0 : UNLIMITED PACKAGE
						//IF PACKAGE IS "UNLIMITED" SO WE DON'T NEED TO CALCULATE THE USED TIMES
						case 0 : $this -> freecall[$K] = true;
								$package_selected = true;
								$this -> package_to_apply [$K] =  array("id"=>$id_cc_package_offer,"label"=>"Unlimited calls","type"=>$packagetype);
						        break;
						// 1 : FREE CALLS         
						//IF PACKAGE IS "NUMBER OF FREE CALLS"  AND WE CAN USE IT ELSE WE CHECK THE OTHERS PACKAGE LIKE FREE TIMES
						case 1 :  if  ( $freetimetocall>0){
									  $number_calls_used =$A2B -> number_free_calls_used($A2B->DBHandle, $A2B->id_card, $id_cc_package_offer, $billingtype, $startday);
							          if($number_calls_used < $freetimetocall){
								          $this -> freecall[$K] = true;
								          $package_selected = true;
								          $this ->package_to_apply [$K] =  array("id"=>$id_cc_package_offer,"label"=> "Number of Free calls","type"=>$packagetype);
							          }
								  }
						          break;
						//2 : FREE TIMES 
						case 2 : 
							// CHECK IF WE HAVE A FREETIME THAT CAN APPLY FOR THIS DESTINATION
							if  ( $freetimetocall>0){
								// WE NEED TO RETRIEVE THE AMOUNT OF USED MINUTE FOR THIS CUSTOMER ACCORDING TO BILLINGTYPE (Monthly ; Weekly) & STARTDAY
								$this -> freetimetocall_used = $A2B -> FT2C_used_seconds($A2B->DBHandle, $A2B->id_card, $id_cc_package_offer, $billingtype, $startday);
								$this -> freetimetocall_left[$K] = $freetimetocall - $this->freetimetocall_used;
								if ($this -> freetimetocall_left[$K] < 0) $this -> freetimetocall_left[$K] = 0;
								if ($this -> freetimetocall_left[$K] > 0) {
									$package_selected = true; 
									$this ->package_to_apply [$K] =  array("id"=>$id_cc_package_offer,"label"=> "Free minutes","type"=>$packagetype);
								}
				
							}
							if ($this -> debug_st) print_r($this -> freetimetocall_left);
							// ****************  END PACKAGE PARAMETERS ****************
						        break;
					}
					
					$idx_pack++;
				}
				
				
				
	
			}
		}

		$credit -= $connectcharge;
		if ($disconnectcharge_after==0){
	               $credit -= $disconnectcharge; 
	               //no disconnenct charge on timeout if disconnectcharge_after is set
               }

		//$credit -= ($initblock/60)*$rateinitial;

		$callbackrate = array();

		if(($A2B->mode == 'cid-callback') || ($A2B->mode == 'all-callback')){
			$callbackrate['rateinitial']=$rateinitial;
			$callbackrate['initblock']=$initblock;
			$callbackrate['billingblock']=$billingblock;
			$callbackrate['connectcharge']=$connectcharge;
			$callbackrate['disconnectcharge']=$disconnectcharge;
			$callbackrate['stepchargea']=$stepchargea;
			$callbackrate['timechargea']=$timechargea;
			$callbackrate['stepchargeb']=$stepchargeb;
			$callbackrate['timechargeb']=$timechargeb;
			$callbackrate['stepchargec']=$stepchargec;
			$callbackrate['timechargec']=$timechargec;
		}

		$this -> ratecard_obj[$K]['callbackrate']=$callbackrate;
		$this -> ratecard_obj[$K]['timeout']=0;
		$this -> ratecard_obj[$K]['timeout_without_rules']=0;

		// CHECK IF THE USER IS ALLOW TO CALL WITH ITS CREDIT AMOUNT
		/*
		Comment from <NAME>
		This following "if" statement used to verify the minimum credit to call can be improved.
		This mininum credit should be calculated based on the destination, and the minimum billing block.
		*/
		if ($credit < $A2B->agiconfig['min_credit_2call'] && !$this -> freecall[$K]){
			return "ERROR CT1";  //NO ENOUGH CREDIT TO CALL THIS NUMBER
		}

		// if ($rateinitial==0) return "ERROR RATEINITIAL($rateinitial)";
		$TIMEOUT = 0;
		$answeredtime_1st_leg = 0;

		if ($rateinitial<=0){
			$this -> ratecard_obj[$K]['timeout']= $A2B->agiconfig['maxtime_tocall_negatif_free_route'];
			$this -> ratecard_obj[$K]['timeout_without_rules'] =	$A2B->agiconfig['maxtime_tocall_negatif_free_route'];
			// 90 min
			if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
			return $TIMEOUT;
		}
		
		if ($this -> freecall[$K]){
			if(	$this -> package_to_apply [$K] ["type"] == 0){
				$this -> ratecard_obj[$K]['timeout']= $A2B->agiconfig['maxtime_tounlimited_calls']; // default : 90 min
				$this -> ratecard_obj[$K]['timeout_without_rules'] =$A2B->agiconfig['maxtime_tounlimited_calls'];
			}else{
				$this -> ratecard_obj[$K]['timeout']= $A2B->agiconfig['maxtime_tofree_calls'];
				$this -> ratecard_obj[$K]['timeout_without_rules'] = $A2B->agiconfig['maxtime_tofree_calls'];
			}
			
			if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
			return $TIMEOUT;
		}

		// IMPROVE THE get_variable AND TRY TO RETRIEVE THEM ALL SOMEHOW
		if($A2B->mode == 'callback'){
			$calling_party_rateinitial = $agi->get_variable('RATEINITIAL', true);
			$calling_party_initblock = $agi->get_variable('INITBLOCK', true);
			$calling_party_billingblock = $agi->get_variable('BILLINGBLOCK', true);
			$calling_party_connectcharge = $agi->get_variable('CONNECTCHARGE', true);
			$calling_party_disconnectcharge = $agi->get_variable('DISCONNECTCHARGE', true);
			$calling_party_stepchargea = $agi->get_variable('STEPCHARGEA', true);
			$calling_party_timechargea = $agi->get_variable('TIMECHARGEA', true);
			$calling_party_stepchargeb = $agi->get_variable('STEPCHARGEB', true);
			$calling_party_timechargeb = $agi->get_variable('TIMECHARGEB', true);
			$calling_party_stepchargec = $agi->get_variable('STEPCHARGEC', true);
			$calling_party_timechargec = $agi->get_variable('TIMECHARGEC', true);
		}

		// 2 KIND OF CALCULATION : PROGRESSIVE RATE & FLAT RATE
		// IF FLAT RATE
		if (empty($chargea) || $chargea==0 || empty($timechargea) || $timechargea==0 ){

			if($A2B->mode == 'callback'){
				/*
				Comment from <NAME>
				In all-callback or cid-callback mode, the number of minutes for the call must be calculated
				according to the rates of both legs of the call.
				*/

				$credit -= $calling_party_connectcharge;
				$credit -= $calling_party_disconnectcharge;
				$num_min = $credit/($rateinitial + $calling_party_rateinitial);
				//I think that the answered time is in seconds
				$answeredtime_1st_leg = intval($agi->get_variable('ANSWEREDTIME', true));
			} else {
				$num_min = $credit/$rateinitial;
			}

			if ($this -> debug_st) echo "num_min:$num_min ($credit/$rateinitial)\n";
			$num_sec = intval($num_min * 60) - $answeredtime_1st_leg;
			if ($this -> debug_st) echo "num_sec:$num_sec \n";

			if ($billingblock > 0) {
				$mod_sec = $num_sec % $billingblock;
				$num_sec=$num_sec-$mod_sec;
			}

			$TIMEOUT = $num_sec;

		// IF PROGRESSIVE RATE
		}else{
			if ($this -> debug_st) echo "CYCLE A	TIMEOUT:$TIMEOUT\n";
			// CYCLE A
			$credit -= $stepchargea;

			//if ($credit<=0) return "ERROR CT2";		//NO ENOUGH CREDIT TO CALL THIS NUMBER
			if ($credit<=0){
				if ($this -> freetimetocall_left[$K] > 0){
					$this -> ratecard_obj[$K]['timeout'] = $this -> freetimetocall_left[$K];
					if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
					return $this -> freetimetocall_left[$K];
				}else{
					return "ERROR CT2";		//NO ENOUGH CREDIT TO CALL THIS NUMBER
				}
			}
			if (!($chargea>0)) return "ERROR CHARGEA($chargea)";
			$num_min = $credit/$chargea;
			if ($this -> debug_st) echo "			CYCLEA num_min:$num_min ($credit/$chargea)\n";
			$num_sec = intval($num_min * 60);
			if ($this -> debug_st) echo "			CYCLEA num_sec:$num_sec \n";
			if ($billingblocka > 0) {
				$mod_sec = $num_sec % $billingblocka;
				$num_sec=$num_sec-$mod_sec;
			}


			if (($num_sec>$timechargea) && !(empty($chargeb) || $chargeb==0 || empty($timechargeb) || $timechargeb==0) ){
				$TIMEOUT += $timechargea;
				$credit -= ($chargea/60) * $timechargea;

				if ($this -> debug_st) echo "		CYCLE B		TIMEOUT:$TIMEOUT\n";
				// CYCLE B
				$credit -= $stepchargeb;
				if ($credit<=0){
					$this -> ratecard_obj[$K]['timeout'] = $TIMEOUT + $this -> freetimetocall_left[$K];
					return $TIMEOUT + $this -> freetimetocall_left[$K];		//NO ENOUGH CREDIT TO GO TO THE CYCLE B
				}

				if (!($chargeb>0)) return "ERROR CHARGEB($chargeb)";
				$num_min = $credit/$chargeb;
				if ($this -> debug_st) echo "			CYCLEB num_min:$num_min ($credit/$chargeb)\n";
				$num_sec = intval($num_min * 60);
				if ($this -> debug_st) echo "			CYCLEB num_sec:$num_sec \n";
				if ($billingblockb > 0) {
					$mod_sec = $num_sec % $billingblockb;
					$num_sec=$num_sec-$mod_sec;
				}

				if (($num_sec>$timechargeb) && !(empty($chargec) || $chargec==0 || empty($timechargec) || $timechargec==0) )
				{
					$TIMEOUT += $timechargeb;
					$credit -= ($chargeb/60) * $timechargeb;

					if ($this -> debug_st) echo "				CYCLE C		TIMEOUT:$TIMEOUT\n";
					// CYCLE C
					$credit -= $stepchargec;
					if ($credit<=0){
						$this -> ratecard_obj[$K]['timeout'] = $TIMEOUT + $this -> freetimetocall_left[$K];
						return $TIMEOUT + $this -> freetimetocall_left[$K];		//NO ENOUGH CREDIT TO GO TO THE CYCLE C
					}

					if (!($chargec>0)) return "ERROR CHARGEC($chargec)";
					$num_min = $credit/$chargec;
					if ($this -> debug_st) echo "			CYCLEC num_min:$num_min ($credit/$chargec)\n";
					$num_sec = intval($num_min * 60);
					if ($this -> debug_st) echo "			CYCLEC num_sec:$num_sec \n";
					if ($billingblockc > 0) {
						$mod_sec = $num_sec % $billingblockc;
						$num_sec=$num_sec-$mod_sec;
					}


					if (($num_sec>$timechargec)){
						if ($this -> debug_st) echo "		OUT CYCLE C		TIMEOUT:$TIMEOUT\n";
						$TIMEOUT += $timechargec;
						$credit -= ($chargec/60) * $timechargec;

						// IF CYCLE C IS FINISH USE THE RATEINITIAL
						$num_min = $credit/$rateinitial;
						if ($this -> debug_st) echo "			OUT CYCLEC num_min:$num_min ($credit/$rateinitial)\n";
						$num_sec = intval($num_min * 60);
						if ($this -> debug_st) echo "			OUT CYCLEC num_sec:$num_sec \n";
						if ($billingblock > 0) {
							$mod_sec = $num_sec % $billingblock;
							$num_sec=$num_sec-$mod_sec;
						}
						$TIMEOUT += $num_sec;
						// THIS IS THE END

					}else{
						$TIMEOUT += $num_sec;
					}

				}else{

					if (($num_sec>$timechargeb)){
						$TIMEOUT += $timechargeb;
						if ($this -> debug_st) echo "		OUT CYCLE B		TIMEOUT:$TIMEOUT\n";
						$credit -= ($chargeb/60) * $timechargeb;

						// IF CYCLE B IS FINISH USE THE RATEINITIAL
						$num_min = $credit/$rateinitial;
						if ($this -> debug_st) echo "			OUT CYCLEB num_min:$num_min ($credit/$rateinitial)\n";
						$num_sec = intval($num_min * 60);
						if ($this -> debug_st) echo "			OUT CYCLEB num_sec:$num_sec \n";
						if ($billingblock > 0) {
							$mod_sec = $num_sec % $billingblock;
							$num_sec=$num_sec-$mod_sec;
						}

						$TIMEOUT += $num_sec;
						// THIS IS THE END

					} else {
						$TIMEOUT += $num_sec;
					}
				}

			} else {

				if (($num_sec>$timechargea)) {
					$TIMEOUT += $timechargea;
					if ($this -> debug_st) echo "		OUT CYCLE A		TIMEOUT:$TIMEOUT\n";
					$credit -= ($chargea/60) * $timechargea;

					// IF CYCLE A IS FINISH USE THE RATEINITIAL
					$num_min = $credit/$rateinitial;
					if ($this -> debug_st) echo "			OUT CYCLEA num_min:$num_min ($credit/$rateinitial)\n";
					$num_sec = intval($num_min * 60);
					if ($this -> debug_st) echo "			OUT CYCLEA num_sec:$num_sec \n";;
					if ($billingblock > 0) {
						$mod_sec = $num_sec % $billingblock;
						$num_sec=$num_sec-$mod_sec;
					}

					$TIMEOUT += $num_sec;
					// THIS IS THE END

				} else {
					$TIMEOUT += $num_sec;
				}
			}
		}
		//Call time to speak without rate rules... idiot rules
		$num_min_WR = $initial_credit/$rateinitial;
		$num_sec_WR = intval($num_min_WR * 60);
		$this -> ratecard_obj[$K]['timeout_without_rules'] = $num_sec_WR+$this -> freetimetocall_left[$K];
		
		$this -> ratecard_obj[$K]['timeout']=$TIMEOUT + $this -> freetimetocall_left[$K];
		if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
		RETURN $TIMEOUT + $this -> freetimetocall_left[$K];
	}

	/*
	 * RATE ENGINE - CALCUL COST OF THE CALL
	 * - calcul the credit consumed by the call
	 */
	function rate_engine_calculcost (&$A2B, $callduration, $K=0)
	{
		global $agi;
		$K = $this->usedratecard;

		$buyrate 						= a2b_round(abs($this -> ratecard_obj[$K][9]));
		$buyrateinitblock 				= $this -> ratecard_obj[$K][10];
		$buyrateincrement 				= $this -> ratecard_obj[$K][11];

		$rateinitial 					= a2b_round(abs($this -> ratecard_obj[$K][12]));
		$initblock 						= $this -> ratecard_obj[$K][13];
		$billingblock 					= $this -> ratecard_obj[$K][14];
		$connectcharge 					= a2b_round(abs($this -> ratecard_obj[$K][15]));
		$disconnectcharge 				= a2b_round(abs($this -> ratecard_obj[$K][16]));
		$disconnectcharge_after				= $this->ratecard_obj[$K][60];
		$stepchargea 					= $this -> ratecard_obj[$K][17];
		$chargea 						= a2b_round(abs($this -> ratecard_obj[$K][18]));
		$timechargea 					= $this -> ratecard_obj[$K][19];
		$billingblocka 					= $this -> ratecard_obj[$K][20];
		$stepchargeb 					= $this -> ratecard_obj[$K][21];
		$chargeb 						= a2b_round(abs($this -> ratecard_obj[$K][22]),4);
		$timechargeb 					= $this -> ratecard_obj[$K][23];
		$billingblockb 					= $this -> ratecard_obj[$K][24];
		$stepchargec 					= $this -> ratecard_obj[$K][25];
		$chargec 						= a2b_round(abs($this -> ratecard_obj[$K][26]),4);
		$timechargec 					= $this -> ratecard_obj[$K][27];
		$billingblockc 					= $this -> ratecard_obj[$K][28];
		// Initialization rounding calltime and rounding threshold variables
		$rounding_calltime 				= $this->ratecard_obj[$K][54];
		$rounding_threshold 			= $this->ratecard_obj[$K][55];
		// Initialization additional block charge and additional block charge time variables
		$additional_block_charge 		= $this->ratecard_obj[$K][56];
		$additional_block_charge_time 	= $this->ratecard_obj[$K][57];
		$additional_grace_time			= $this->ratecard_obj[$K][58];
		$minimal_call_cost 				= $this->ratecard_obj[$K][59];

		
		if (!is_numeric($rounding_calltime))			$rounding_calltime = 0;
		if (!is_numeric($rounding_threshold))			$rounding_threshold = 0;
		if (!is_numeric($additional_block_charge))		$additional_block_charge = 0;
		if (!is_numeric($additional_block_charge_time))	$additional_block_charge_time = 0;

		if (!is_numeric($this->freetimetocall_used))	$this->freetimetocall_used = 0;
		if ($this -> debug_st)  echo "CALLDURATION: $callduration - freetimetocall_used=$this->freetimetocall_used\n\n";
		$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_CALCULCOST: K=$K - CALLDURATION:$callduration - freetimetocall_used=$this->freetimetocall_used]");

		$cost = 0;
		$cost -= $connectcharge;
		if (($disconnectcharge_after>=$callduration)||($disconnectcharge_after==0)){
                $cost -= $disconnectcharge;
	        }

		$this -> real_answeredtime = $callduration;
		$callduration =$callduration + $additional_grace_time;			
		
		/*
		 * In following condition callduration will be updated
		 * according to the the rounding_calltime and rounding_threshold
		 * Reference to the TODO : ADDITIONAL CHARGES ON REALTIME BILLING - 1
		 */
		if($rounding_calltime > 0 && $rounding_threshold > 0 && $callduration > $rounding_threshold && $rounding_calltime > $callduration) {
			$callduration = $rounding_calltime;
			// RESET THE SESSIONTIME FOR CDR
			$this -> answeredtime = $rounding_calltime;
		}

		/*
		 * Following condition will append cost of call
		 * according to the the additional_block_charge and additional_block_charge_time
		 * Reference to the TODO : ADDITIONAL CHARGES ON REALTIME BILLING - 2
		 */
		// If call duration is greater then block charge time
		if ($callduration >= $additional_block_charge_time) {
			$block_charge = intval($callduration / $additional_block_charge_time);
			$cost -= $block_charge * $additional_block_charge;
		}

		// #### 	CALCUL BUYRATE COST   #####
		$buyratecallduration = $this -> real_answeredtime + $additional_grace_time;

		$buyratecost =0;
		if ($buyratecallduration < $buyrateinitblock) $buyratecallduration = $buyrateinitblock;
		if ($buyrateincrement > 0 && $buyratecallduration > $buyrateinitblock) {
			$mod_sec = $buyratecallduration % $buyrateincrement; // 12 = 30 % 18
			if ($mod_sec>0) $buyratecallduration += ($buyrateincrement - $mod_sec); // 30 += 18 - 12
		}
		$buyratecost -= ($buyratecallduration/60) * $buyrate;
		if ($this -> debug_st)  echo "1. cost: $cost\n buyratecost:$buyratecost\n";

        // IF IT S A FREE CALL, WE CAN STOP HERE COST = 0
        if($this -> freecall[$K]){
        	$this -> lastcost = 0;
			$this -> lastbuycost = $buyratecost;
			if ($this -> debug_st)  echo "FINAL COST: $cost\n\n";
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_CALCULCOST: K=$K - BUYCOST: $buyratecost - SELLING COST: $cost]");
        	return; 
        }

		// #### 	CALCUL SELLRATE COST   #####
		if ($callduration < $initblock) $callduration = $initblock;
		if ($this -> freetimetocall_left[$K] >= $callduration) {
			$this -> freetimetocall_used = $callduration;
		}
		

		$callduration = $callduration - $this->freetimetocall_used;

		// 2 KIND OF CALCULATION : PROGRESSIVE RATE & FLAT RATE
		// IF FLAT RATE
		if (empty($chargea) || $chargea==0 || empty($timechargea) || $timechargea==0) {

			if ($billingblock > 0 && $callduration > $initblock) {
				$mod_sec = $callduration % $billingblock;
				if ($mod_sec>0) $callduration += ($billingblock - $mod_sec);
			}

			$cost -= ($callduration/60) * $rateinitial;
			if ($this -> debug_st)  echo "1.a cost: $cost\n";
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[TEMP - CC_RATE_ENGINE_CALCULCOST: 1. COST: $cost]:[ ($callduration/60) * $rateinitial ]");

		// IF PROGRESSIVE RATE
		} else {

			if ($this -> debug_st) echo "CYCLE A	COST:$cost\n";
			// CYCLE A
			$cost -= $stepchargea;
			if ($this -> debug_st)  echo "1.A cost: $cost\n\n";

			if ($callduration > $timechargea) {
				$duration_report = $callduration - $timechargea;
				$callduration = $timechargea;
			}

			if ($billingblocka > 0) {
				$mod_sec = $callduration % $billingblocka;
				if ($mod_sec>0) $callduration += ($billingblocka - $mod_sec);
			}
			$cost -= ($callduration/60) * $chargea;

			if (($duration_report>0) && !(empty($chargeb) || $chargeb==0 || empty($timechargeb) || $timechargeb==0)) {
				$callduration = $duration_report;
				$duration_report = 0;

				// CYCLE B
				$cost -= $stepchargeb;
				if ($this -> debug_st)  echo "1.B cost: $cost\n\n";

				if ($callduration > $timechargeb) {
					$duration_report = $callduration - $timechargeb;
					$callduration=$timechargeb;
				}

				if ($billingblockb > 0) {
					$mod_sec = $callduration % $billingblockb;
					if ($mod_sec>0) $callduration += ($billingblockb - $mod_sec);
				}
				$cost -= ($callduration/60) * $chargeb; // change chargea -> chargeb thanks to Abbas :D

				if (($duration_report>0) && !(empty($chargec) || $chargec==0 || empty($timechargec) || $timechargec==0) )
				{
					$callduration=$duration_report;
					$duration_report=0;

					// CYCLE C
					$cost -= $stepchargec;
					if ($this -> debug_st)  echo "1.C cost: $cost\n\n";

					if ($callduration > $timechargec){
						$duration_report = $callduration - $timechargec;
						$callduration=$timechargec;
					}

					if ($billingblockc > 0) {
						$mod_sec = $callduration % $billingblockc;
						if ($mod_sec>0) $callduration += ($billingblockc - $mod_sec);
					}
					$cost -= ($callduration/60) * $chargec;
				}
			}

			if ($duration_report > 0){

				if ($billingblock > 0) {
					$mod_sec = $duration_report % $billingblock;
					if ($mod_sec>0) $duration_report += ($billingblock - $mod_sec);
				}

				$cost -= ($duration_report/60) * $rateinitial;
				$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[TEMP - CC_RATE_ENGINE_CALCULCOST: 2. DURATION_REPORT:$duration_report - COST: $cost]");
			}

		}
		$cost = a2b_round($cost);
		if ($this -> debug_st)  echo "FINAL COST: $cost\n\n";
		$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_CALCULCOST: K=$K - BUYCOST: $buyratecost - SELLING COST: $cost]");
		
		if($cost> (0-$minimal_call_cost)){
			$this -> lastcost = 0 - $minimal_call_cost;
		}else{
			$this -> lastcost = $cost;
		}
		$this -> lastbuycost = $buyratecost;
		
	}


    /*
		SORT_ASC : Tri en ordre ascendant
      	SORT_DESC : Tri en ordre descendant
	*/
	function array_csort()
	{
		$args = func_get_args();
		$marray = array_shift($args);
		$i=0;
		$msortline = "return(array_multisort(";
		foreach ($args as $arg) {
			$i++;
			if (is_string($arg)) {
				foreach ($marray as $row) {
					$sortarr[$i][] = $row[$arg];
				}
			} else {
				$sortarr[$i] = $arg;
			}
			$msortline .= "\$sortarr[".$i."],";
		}
		$msortline .= "\$marray));";

		eval($msortline);
		return $marray;
	}


	/*
	 * RATE ENGINE - UPDATE SYSTEM (DURATIONCALL)
	 * Calcul the duration allowed for the caller to this number
	 */
	function rate_engine_updatesystem (&$A2B, &$agi, $calledstation, $doibill = 1, $didcall=0, $callback=0)
	{
		$K = $this->usedratecard;
		// ****************  PACKAGE PARAMETERS ****************
		//$freetimetocall_package_offer = $this -> ratecard_obj[$K][45];
		//$freetimetocall = $this -> ratecard_obj[$K][46];
		$id_cc_package_group= $this -> ratecard_obj[$K][45];
		$additional_grace_time= $this->ratecard_obj[$K][58];
		if ($A2B -> CC_TESTING){
			$sessiontime = 120;
			$dialstatus = 'ANSWERED';
		} else {
			$sessiontime = $this -> answeredtime;
			$dialstatus = $this -> dialstatus;
		}
		if ($sessiontime > 0) {
			// HANDLE FREETIME BEFORE CALCULATE THE COST
			$this->freetimetocall_used = 0;
			$A2B -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "ft2c_package_offer : $freetimetocall_package_offer ; $freetimetocall ; ".$this -> freetimetocall_left[$K]);
			if ($this -> debug_st) print_r($this -> freetimetocall_left[$K]);

			if (($id_cc_package_group!=-1) && ($this ->package_to_apply[$K] !=null )){
			$id_package_offer =$this ->package_to_apply[$K]["id"];
	
				//$id_card_package_offer = $this ->package_to_apply[$K]["id"];
                switch($this ->package_to_apply[$K]["type"] ){
                	//Unlimited
                	case 0 : $this->freetimetocall_used = $sessiontime;break;
                	//free calls
                	case 1 : $this->freetimetocall_used = $sessiontime;break;
                	//free times
                	case 2 :
						if ($this -> freetimetocall_left[$K] >= $sessiontime){
							$this->freetimetocall_used = $sessiontime;
						}else{
							$this->freetimetocall_used = $this -> freetimetocall_left[$K];
						}
						break;
                	
                }

				$this -> rate_engine_calculcost ($A2B, $sessiontime, 0);
				// rate_engine_calculcost could have change the duration of the call
				$sessiontime = $this -> answeredtime;
				//add grace time
				if($sessiontime>0 && $additional_grace_time>0){
					$sessiontime = $sessiontime + $additional_grace_time;
				}

				$QUERY_FIELS = 'id_cc_card, id_cc_package_offer, used_secondes';
				$QUERY_VALUES = "'".$A2B -> id_card."', '$id_package_offer', '$this->freetimetocall_used'";
				$id_card_package_offer = $A2B -> instance_table -> Add_table ($A2B -> DBHandle, $QUERY_VALUES, $QUERY_FIELS, 'cc_card_package_offer', 'id');
				$A2B -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, ":[ID_CARD_PACKAGE_OFFER CREATED : $id_card_package_offer]:[$QUERY_VALUES]");
			} else {

				$this -> rate_engine_calculcost ($A2B, $sessiontime, 0);
				
				// rate_engine_calculcost could have change the duration of the call
				$sessiontime = $this -> answeredtime;
				if($sessiontime>0 && $additional_grace_time>0){
					$sessiontime = $sessiontime + $additional_grace_time;
				}
			}


		} else {
			$sessiontime = 0;
		}

		$calldestination = $this -> ratecard_obj[$K][5];
		$id_tariffgroup = $this -> ratecard_obj[$K][2];
		$id_tariffplan = $this -> ratecard_obj[$K][3];
		$id_ratecard = $this -> ratecard_obj[$K][6];
		
		$buycost = 0;
		if ($doibill==0 || $sessiontime < $A2B->agiconfig['min_duration_2bill']) {
			$cost = 0;
		} else{
			$cost = $this -> lastcost;
			$buycost = abs($this -> lastbuycost);
		}

		if ($cost<0) {
			$signe='-';
			$signe_cc_call ='+';
		} else {
			$signe='+';
			$signe_cc_call ='-';
		}

		$buyrateapply = $this -> ratecard_obj[$K][9];
		$rateapply = $this -> ratecard_obj[$K][12];

		$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_UPDATESYSTEM: usedratecard K=$K - (sessiontime=$sessiontime :: dialstatus=$dialstatus :: buycost=$buycost :: cost=$cost : signe_cc_call=$signe_cc_call: signe=$signe)]");

		// CALLTYPE -  0 = NORMAL CALL ; 1 = VOIP CALL (SIP/IAX) ; 2= DIDCALL + TRUNK ; 3 = VOIP CALL DID ; 4 = CALLBACK call
		if ($didcall) $calltype = 2;
		elseif ($callback) $calltype = 4;
		else $calltype = 0;
		
		if (strlen($this -> dialstatus_rev_list[$dialstatus])>0)
			$terminatecauseid = $this -> dialstatus_rev_list[$dialstatus];
		else
			$terminatecauseid = 0;
			
		$QUERY_COLUMN = "uniqueid, sessionid, card_id, nasipaddress, starttime, sessiontime, real_sessiontime, calledstation, ".
			" terminatecauseid, stoptime, sessionbill, id_tariffgroup, id_tariffplan, id_ratecard, " .
			" id_trunk, src, sipiax, buycost, id_card_package_offer, dnid, id_cc_prefix";
		$QUERY = "INSERT INTO cc_call ($QUERY_COLUMN) VALUES ('".$A2B->uniqueid."', '".$A2B->channel."', '".
			$A2B->id_card."', '".$A2B->hostname."', ";

		if ($A2B->config["global"]['cache_enabled']){
			$QUERY .= " datetime( strftime('%s','now') - $sessiontime, 'unixepoch','localtime')";	
		}else{
			if ($A2B->config["database"]['dbtype'] == "postgres"){
				$QUERY .= "CURRENT_TIMESTAMP - interval '$sessiontime seconds' ";
			}else{
				$QUERY .= "CURRENT_TIMESTAMP - INTERVAL $sessiontime SECOND ";
			}
		}

		$QUERY .= 	", '$sessiontime', '".$this->real_answeredtime."', '$calledstation', '$terminatecauseid', "; 
		if ($A2B->config["global"]['cache_enabled']){
			$QUERY .= "datetime('now','localtime')"; 
		}else{
			$QUERY .= "now()";
		}
		
		$QUERY .= " , '$rateapply', ".
					" '$id_tariffgroup', '$id_tariffplan', '$id_ratecard', '".$this -> usedtrunk."', '".$A2B->CallerID."', '$calltype', ".
					" '$buycost', '$id_card_package_offer', '".$A2B->dnid."', '".$calldestination."')";<commit_msg>sellcost nto isnerted propertly in cc_call Ticket #296 <commit_after><?php
/***************************************************************************
 *
 * Class.RateEngine.php : RateEngine Functions for A2Billing
 * Written for PHP 4.x & PHP 5.X versions.
 *
 * A2Billing -- Asterisk billing solution.
 * Copyright (C) 2004, 2007 <NAME> <areski _atl_ gmail com>
 *
 * See http://www.asterisk2billing.org for more information about
 * the A2Billing project.
 * Please submit bug reports, patches, etc to <areski _atl_ gmail com>
 *
 * This software is released under the terms of the GNU Lesser General Public License v2.1
 * A copy of which is available from http://www.gnu.org/copyleft/lesser.html
 *
 ****************************************************************************/
 include_once (FSROOT."lib/Misc.php");

class RateEngine
{
	var $debug_st = 0;

	var $ratecard_obj = array();

	var $freetimetocall_left = array();
	var $freecall = array();
	var $package_to_apply= array();

	var $number_trunk		= 0;
	var $lastcost			= 0;
	var $lastbuycost		= 0;
	var $answeredtime 		= 0;
	var $real_answeredtime	= 0;
	var $dialstatus			= 0;
	var $usedratecard		= 0;
	var $webui 				= 1;
	var $usedtrunk			= 0;
	var $freetimetocall_used= 0;
	
	// List of dialstatus
	var $dialstatus_rev_list;
	

	/* CONSTRUCTOR */
	function RateEngine ()
	{
		$this -> dialstatus_rev_list = Constants::getDialStatus_Revert_List();
	}

	/* Reinit */
	function Reinit ()
	{
		$this -> number_trunk = 0;
		$this -> answeredtime = 0;
		$this -> real_answeredtime = 0;
		$this -> dialstatus = '';
		$this -> usedratecard = '';
		$this -> usedtrunk = '';
		$this -> lastcost = '';
		$this -> lastbuycost = '';
		
	}

	/*
		RATE ENGINE
		CALCUL THE RATE ACCORDING TO THE RATEGROUP, LCR - RATECARD
	*/
	function rate_engine_findrates (&$A2B, $phonenumber, $tariffgroupid)
	{
		global $agi;

		// Check if we want to force the call plan
		if (is_numeric($A2B->agiconfig['force_callplan_id']) && ($A2B->agiconfig['force_callplan_id'] > 0)){
			$A2B -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "force the call plan : ".$A2B->agiconfig['force_callplan_id']);
			$tariffgroupid = $A2B->tariff = $A2B->agiconfig['force_callplan_id'];
		}

		if ($this->webui){
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: ($tariffgroupid, $phonenumber)]");
		}


		/***  0 ) CODE TO RETURN THE DAY OF WEEK + CREATE THE CLAUSE  ***/

		$daytag = date("w", time()); // Day of week ( Sunday = 0 .. Saturday = 6 )
		$hours = date("G", time()); // Hours in 24h format ( 0-23 )
		$minutes = date("i", time()); // Minutes (00-59)
		if (!$daytag) $daytag=6;
		else $daytag--;
		if ($this -> debug_st) echo "$daytag $hours $minutes <br>";
		// Race condiction on $minutes ?!
		$minutes_since_monday = ($daytag * 1440) + ($hours * 60) + $minutes;
		if ($this -> debug_st) echo "$minutes_since_monday<br> ";
		//$sql_clause_days = " AND ".$daytable[$daytag]."='1' ";
		$sql_clause_days = " AND (starttime <= ".$minutes_since_monday." AND endtime >=".$minutes_since_monday.") ";

		/*

		SELECT tariffgroupname, lcrtype, idtariffgroup, cc_tariffgroup_plan.idtariffplan, tariffname, destination,

		cc_ratecard.id,
		dialprefix, destination, buyrate, buyrateinitblock, buyrateincrement, rateinitial, initblock, billingblock,
		connectcharge, disconnectcharge, stepchargea, chargea, timechargea, billingblocka, stepchargeb, chargeb,
		timechargeb, billingblockb, stepchargec, chargec, timechargec, billingblockc,

		cc_tariffplan.id_trunk AS tp_id_trunk, tp_trunk.trunkprefix AS tp_trunk, tp_trunk.providertech AS tp_providertech,
		tp_trunk.providerip AS tp_providerip, tp_trunk.removeprefix AS tp_removeprefix,
		cc_ratecard.id_trunk AS rc_id_trunk, rt_trunk.trunkprefix AS rc_trunkprefix, rt_trunk.providertech AS rc_providertech,
		rt_trunk.providerip AS rc_providerip, rt_trunk.removeprefix AS rc_removeprefix

		FROM cc_tariffgroup
		LEFT JOIN cc_tariffgroup_plan ON idtariffgroup=id
		LEFT JOIN cc_tariffplan ON cc_tariffplan.id=cc_tariffgroup_plan.idtariffplan
		LEFT JOIN cc_ratecard ON cc_ratecard.idtariffplan=cc_tariffplan.id
		LEFT JOIN trunk AS rt_trunk ON cc_ratecard.id_trunk=rt_trunk.id_trunk
		LEFT JOIN trunk AS tp_trunk ON cc_tariffplan.id_trunk=tp_trunk.id_trunk

		WHERE dialprefix=SUBSTRING('346586699595',1,length(dialprefix))
		AND startingdate<= CURRENT_TIMESTAMP AND (expirationdate > CURRENT_TIMESTAMP OR expirationdate IS NULL OR LENGTH(expirationdate)<5)
		AND startdate<= CURRENT_TIMESTAMP AND (stopdate > CURRENT_TIMESTAMP OR stopdate IS NULL OR LENGTH(stopdate)<5)
		ORDER BY LENGTH(dialprefix) DESC

		*/

		if (strlen($A2B->dnid)>=1) $mydnid = $A2B->dnid;
		if (strlen($A2B->CallerID)>=1) $mycallerid = $A2B->CallerID;

		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: CALLERID]\n".$A2B->CallerID."\n",0);

		if ($A2B->config["database"]['dbtype'] != "postgres") {
			$DNID_QUERY = "SELECT count(dnidprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix))";
			$result_sub = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $DNID_QUERY);
			if (!is_array($result_sub) || count($result_sub)==0) $nb_dnid = 0;
			else $nb_dnid = $result_sub[0][0];
			$DNID_SUB_QUERY = "AND 0 = $nb_dnid";

			$CALLERID_QUERY = "SELECT count(calleridprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix))";
			$result_sub = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $CALLERID_QUERY);
			if (!is_array($result_sub) || count($result_sub)==0) $nb_callerid = 0;
			else $nb_callerid = $result_sub[0][0];
			$CID_SUB_QUERY = "AND 0 = $nb_callerid";

		} else {
			$DNID_SUB_QUERY = "AND 0 = (SELECT count(dnidprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix)) ) ";

			$CID_SUB_QUERY = "AND 0 = (SELECT count(calleridprefix) FROM cc_tariffplan RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=$tariffgroupid WHERE calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix)) ) ";
		}


		// $prefixclause to allow good DB servers to use an index rather than sequential scan
		// justification at http://forum.asterisk2billing.org/viewtopic.php?p=9620#9620
		$max_len_prefix = min(strlen($phonenumber), 15);	// don't match more than 15 digits (the most I have on my side is 8 digit prefixes)
		$prefixclause = '(';
		while ($max_len_prefix > 0 ) {
			$prefixclause .= "dialprefix='".substr($phonenumber,0,$max_len_prefix)."' OR ";
			$max_len_prefix--;
		}
		$prefixclause .= "dialprefix='defaultprefix')";

		// match Asterisk/POSIX regex prefixes,  rewrite the Asterisk '_XZN.' characters to
		// POSIX equivalents, and test each of them against the dialed number
		$prefixclause .= " OR (dialprefix LIKE '&_%' ESCAPE '&' AND '$phonenumber' ";
		if ($A2B->config["database"]['dbtype'] != "postgres"){
			$prefixclause .= "REGEXP REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(CONCAT('^', dialprefix, '$'), ";
			$prefixclause .= "'X', '[0-9]'), 'Z', '[1-9]'), 'N', '[2-9]'), '.', '+'), '_', ''))";
		} else {
			$prefixclause .= "~* REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE('^' || dialprefix || '$', ";
			$prefixclause .= "'X', '[0-9]', 'g'), 'Z', '[1-9]', 'g'), 'N', '[2-9]', 'g'), E'\\\\.', '+', 'g'), '_', '', 'g'))";
		}
        // select group by 5 ... more easy to count
		$QUERY = "SELECT
		tariffgroupname, lcrtype, idtariffgroup, cc_tariffgroup_plan.idtariffplan, tariffname, 
        id_cc_prefix, cc_ratecard.id, dialprefix, id_cc_prefix, buyrate,
        buyrateinitblock, buyrateincrement, rateinitial, initblock, billingblock,
		connectcharge, disconnectcharge, stepchargea, chargea, timechargea,
        billingblocka, stepchargeb, chargeb,timechargeb, billingblockb, 
        stepchargec, chargec, timechargec, billingblockc,cc_tariffplan.id_trunk AS tp_id_trunk,
        tp_trunk.trunkprefix AS tp_trunk, tp_trunk.providertech AS tp_providertech,tp_trunk.providerip AS tp_providerip, tp_trunk.removeprefix AS tp_removeprefix,cc_ratecard.id_trunk AS rc_id_trunk,
        rt_trunk.trunkprefix AS rc_trunkprefix, rt_trunk.providertech AS rc_providertech,rt_trunk.providerip AS rc_providerip, rt_trunk.removeprefix AS rc_removeprefix, musiconhold,
		tp_trunk.failover_trunk AS tp_failover_trunk, rt_trunk.failover_trunk AS rt_failover_trunk,tp_trunk.addparameter AS tp_addparameter_trunk, rt_trunk.addparameter AS rt_addparameter_trunk, id_outbound_cidgroup,
        id_cc_package_offer,tp_trunk.status, rt_trunk.status, tp_trunk.inuse, rt_trunk.inuse, 
        tp_trunk.maxuse,  rt_trunk.maxuse,tp_trunk.if_max_use, rt_trunk.if_max_use,cc_ratecard.rounding_calltime AS rounding_calltime,
        cc_ratecard.rounding_threshold AS rounding_threshold,cc_ratecard.additional_block_charge AS additional_block_charge,cc_ratecard.additional_block_charge_time AS additional_block_charge_time, cc_ratecard.additional_grace AS additional_grace, cc_ratecard.minimal_cost AS minimal_cost,disconnectcharge_after

		FROM cc_tariffgroup
		RIGHT JOIN cc_tariffgroup_plan ON cc_tariffgroup_plan.idtariffgroup=cc_tariffgroup.id
		INNER JOIN cc_tariffplan ON (cc_tariffplan.id=cc_tariffgroup_plan.idtariffplan )
		LEFT JOIN cc_ratecard ON cc_ratecard.idtariffplan=cc_tariffplan.id
		LEFT JOIN cc_trunk AS rt_trunk ON cc_ratecard.id_trunk=rt_trunk.id_trunk
		LEFT JOIN cc_trunk AS tp_trunk ON cc_tariffplan.id_trunk=tp_trunk.id_trunk

		WHERE cc_tariffgroup.id=$tariffgroupid AND ($prefixclause)
		AND startingdate<= CURRENT_TIMESTAMP AND (expirationdate > CURRENT_TIMESTAMP OR expirationdate IS NULL OR LENGTH(expirationdate)<5)
		AND startdate<= CURRENT_TIMESTAMP AND (stopdate > CURRENT_TIMESTAMP OR stopdate IS NULL OR LENGTH(stopdate)<5)
		$sql_clause_days
		AND idtariffgroup='$tariffgroupid'
		AND ( dnidprefix=SUBSTRING('$mydnid',1,length(dnidprefix)) OR (dnidprefix='all' $DNID_SUB_QUERY))
		AND ( calleridprefix=SUBSTRING('$mycallerid',1,length(calleridprefix)) OR (calleridprefix='all' $CID_SUB_QUERY))
		ORDER BY LENGTH(dialprefix) DESC";

		$A2B->instance_table = new Table();
		$result = $A2B->instance_table -> SQLExec ($A2B -> DBHandle, $QUERY);


		if (!is_array($result) || count($result)==0) return 0; // NO RATE FOR THIS NUMBER

		if ($this->debug_st) echo "::> Count Total result ".count($result)."\n\n";
		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[rate-engine: Count Total result ".count($result)."]");

		// CHECK IF THERE IS OTHER RATE THAT 'DEFAULT', IF YES REMOVE THE DEFAULT RATES
		// NOT NOT REMOVE SHIFT THEM TO THE END :P
		$ind_stop_default = -1;
		for ($i=0;$i<count($result);$i++){
			if ( $result[$i][7] != 'defaultprefix'){
				$ind_stop_default = $i;
				break;
			}
		}
		// IMPORTANT TO CUT THE PART OF THE defaultprefix CAUSE WE WILL APPLY THE SORT ACCORDING TO THE RATE
		// DEFAULPERFIX IS AN ESCAPE IN CASE OF NO RATE IS DEFINED, NOT BE COUNT WITH OTHER DURING THE
		// SORT OF RATE
		if ($ind_stop_default>0) {
			$result_defaultprefix = array_slice ($result, 0, $ind_stop_default);
			$result = array_slice ($result, $ind_stop_default, count($result)-$ind_stop_default);
		}


		//if ($ind_stop_default>0)  array_rotate($result, $ind_stop_default);


		//1) REMOVE THOSE THAT HAVE A SMALLER DIALPREFIX
		$max_len_prefix = strlen($result[0][7]);
		for ($i=1;$i<count($result);$i++) {
			if ( strlen($result[$i][7]) < $max_len_prefix) break;
		}
		$result = array_slice ($result, 0, $i);


		//2) TAKE THE VALUE OF LCTYPE
		//LCR : According to the buyer price	-0 	buyrate [col 6]
		//LCD : According to the seller price	-1  rateinitial	[col 9]

		$LCtype = $result[0][1];

		// Thanks for the fix from the wiki :D next time email me, lol
		if ($LCtype==0) {
			//$result = $this -> array_csort($result,'6',SORT_ASC); GOTTYA!
			$result = $this -> array_csort($result,'9',SORT_ASC); //1
		} else {
			//$result = $this -> array_csort($result,'9',SORT_ASC); GOTTYA!
			$result = $this -> array_csort($result,'12',SORT_ASC); //1
		}


		// WE ADD THE DEFAULTPREFIX WE REMOVE BEFORE
		if ($ind_stop_default > 0) {
			$result = array_merge ($result, $result_defaultprefix);
		}


		// 3) REMOVE THOSE THAT USE THE SAME TRUNK - MAKE A DISTINCT
		//    AND THOSE THAT ARE DISABLED.
		$mylistoftrunk = array();
		for ($i=0;$i<count($result);$i++) {

			if ($result[$i][34] == -1) {
				$status = $result[$i][46];
				$mylistoftrunk_next[]= $mycurrenttrunk = $result[$i][29];
			} else {
				$status = $result[$i][47];
				$mylistoftrunk_next[]= $mycurrenttrunk = $result[$i][34];
			}

			// Check if we already have the same trunk in the ratecard
			if (($i==0 || !in_array ($mycurrenttrunk , $mylistoftrunk)) && $status == 1) {
				$distinct_result[] = $result[$i];
			}

			if ($status == 1)
				$mylistoftrunk[]= $mycurrenttrunk;
		}


		//4) $result[K][34]==-1 USE THE VALUES OF THE TRUNK OF $result[K][29]


		$this -> ratecard_obj = $distinct_result;
		$this -> number_trunk = count($distinct_result);

		// if an extracharge DID number was called increase rates with the extracharge fee
		if (strlen($A2B->dnid)>2 && is_array($A2B->agiconfig['extracharge_did']) && in_array($A2B->dnid, $A2B->agiconfig['extracharge_did']))
		{
			$fee=$A2B->agiconfig['extracharge_fee'][array_search($A2B->dnid, $A2B->agiconfig['extracharge_did'])];
			$buyfee=$A2B->agiconfig['extracharge_buyfee'][array_search($A2B->dnid, $A2B->agiconfig['extracharge_did'])];
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: Extracharge DID found: ".$A2B->dnid.", extra fee: ".$fee.", extra buy fee: ".$buyfee."]");
			for ($i=0; $i<count($this->ratecard_obj); $i++)
			{
				$this->ratecard_obj[$i][9] +=$buyfee;
				$this->ratecard_obj[$i][12]+=$fee;
			}
		}

		if ($this->debug_st) echo "::> Count Total distinct_result ".count($distinct_result)."\n\n";
		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: Count Total result ".count($distinct_result)."]");
		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_asterisk_rate-engine: number_trunk ".$this -> number_trunk."]");

		return 1;
	}


	/*
		RATE ENGINE - CALCUL TIMEOUT
		* CALCUL THE DURATION ALLOWED FOR THE CALLER TO THIS NUMBER
	*/
	function rate_engine_all_calcultimeout (&$A2B, $credit)
	{
		global $agi;

		if ($this->webui) $A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_ALL_CALCULTIMEOUT ($credit)]");
		if (!is_array($this -> ratecard_obj) || count($this -> ratecard_obj)==0) return false;

		for ($k=0;$k<count($this -> ratecard_obj);$k++){
			$res_calcultimeout = $this -> rate_engine_calcultimeout ($A2B,$credit,$k);
			if ($this->webui)
				$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_ALL_CALCULTIMEOUT: k=$k - res_calcultimeout:$res_calcultimeout]");

			if (substr($res_calcultimeout,0,5)=='ERROR')	return false;
		}

		return true;
	}

	/*
		RATE ENGINE - CALCUL TIMEOUT
		* CALCUL THE DURATION ALLOWED FOR THE CALLER TO THIS NUMBER
	*/
	function rate_engine_calcultimeout (&$A2B, $credit, $K=0)
	{
		global $agi;

		$rateinitial 					= a2b_round (abs($this -> ratecard_obj[$K][12]));
		$initblock 						= $this -> ratecard_obj[$K][13];
		$billingblock 					= $this -> ratecard_obj[$K][14];
		$connectcharge 					= a2b_round (abs($this -> ratecard_obj[$K][15]));
		$disconnectcharge 				= a2b_round (abs($this -> ratecard_obj[$K][16]));
		$disconnectcharge_after         		= $this->ratecard_obj[$K][60];
		$stepchargea 					= $this -> ratecard_obj[$K][17];
		$chargea 						= a2b_round (abs($this -> ratecard_obj[$K][18]));
		$timechargea 					= $this -> ratecard_obj[$K][19];
		$billingblocka 					= $this -> ratecard_obj[$K][20];
		$stepchargeb 					= $this -> ratecard_obj[$K][21];
		$chargeb 						= a2b_round (abs($this -> ratecard_obj[$K][22]));
		$timechargeb 					= $this -> ratecard_obj[$K][23];
		$billingblockb 					= $this -> ratecard_obj[$K][24];
		$stepchargec 					= $this -> ratecard_obj[$K][25];
		$chargec 						= a2b_round (abs($this -> ratecard_obj[$K][26]));
		$timechargec 					= $this -> ratecard_obj[$K][27];
		$billingblockc 					= $this -> ratecard_obj[$K][28];
		// ****************  PACKAGE PARAMETERS ****************
		$id_cc_package_group 			= $this -> ratecard_obj[$K][45];
		$id_rate 						= $this -> ratecard_obj[$K][6];
		$initial_credit					= $credit;
		// CHANGE THIS - ONLY ALLOW FREE TIME FOR CUSTOMER THAT HAVE MINIMUM CREDIT TO CALL A DESTINATION

		$this -> freetimetocall_left[$K] = 0;
		$this -> freecall[$K] = false;
		$this -> package_to_apply [$K] = null;
		
		//CHECK THE PACKAGES TOAPPLY TO THIS RATES
		if($id_cc_package_group!=-1){
			
			//$table_packages = new Table("cc_package_group,cc_packgroup_package,cc_package_offer,cc_package_rate", "cc_package_offer.id, packagetype,billingtype,startday,freetimetocall");
			//$clause_packages= "cc_package_group.id= ".$id_cc_package_group." AND cc_package_group.id=cc_packgroup_package.packagegroup_id AND cc_packgroup_package.package_id = cc_package_offer.id AND cc_package_offer.id = cc_package_rate.package_id  AND cc_package_rate.rate_id = ".$id_rate;
			//$order_packages = "cc_package_offer.packagetype";
			//$sens_packages = "ASC";
			//$result_packages= $table_packages -> Get_list ($A2B -> DBHandle, $clause_packages, $order_packages, $sens_packages, null, null, null, null);
			$query_pakages ="SELECT cc_package_offer.id, packagetype,billingtype,startday,freetimetocall 
			FROM cc_package_group,cc_packgroup_package,cc_package_offer,cc_package_rate 
			WHERE cc_package_group.id= ".$id_cc_package_group." 
				AND cc_package_group.id=cc_packgroup_package.packagegroup_id 
				AND cc_packgroup_package.package_id = cc_package_offer.id 
				AND cc_package_offer.id = cc_package_rate.package_id  
				AND cc_package_rate.rate_id = ".$id_rate." ORDER BY packagetype ASC";
			$table_packages = new Table();
			$result_packages = $table_packages -> SQLExec ($A2B -> DBHandle, $query_pakages);
			$idx_pack = 0;
			if(!empty($result_packages))
			{ 
				$package_selected = false;
				while(!$package_selected && $idx_pack < count($result_packages)){
					$freetimetocall 	= $result_packages[$idx_pack]["freetimetocall"];
					$packagetype 		= $result_packages[$idx_pack]["packagetype"];
					$billingtype 		= $result_packages[$idx_pack]["billingtype"];
					$startday 			= $result_packages[$idx_pack]["startday"];
					$id_cc_package_offer= $result_packages[$idx_pack][0];
					if ($this -> debug_st) print_r("[ID PACKAGE  TO APPLY = $id_cc_package_offer]");
					switch($packagetype){
						// 0 : UNLIMITED PACKAGE
						//IF PACKAGE IS "UNLIMITED" SO WE DON'T NEED TO CALCULATE THE USED TIMES
						case 0 : $this -> freecall[$K] = true;
								$package_selected = true;
								$this -> package_to_apply [$K] =  array("id"=>$id_cc_package_offer,"label"=>"Unlimited calls","type"=>$packagetype);
						        break;
						// 1 : FREE CALLS         
						//IF PACKAGE IS "NUMBER OF FREE CALLS"  AND WE CAN USE IT ELSE WE CHECK THE OTHERS PACKAGE LIKE FREE TIMES
						case 1 :  if  ( $freetimetocall>0){
									  $number_calls_used =$A2B -> number_free_calls_used($A2B->DBHandle, $A2B->id_card, $id_cc_package_offer, $billingtype, $startday);
							          if($number_calls_used < $freetimetocall){
								          $this -> freecall[$K] = true;
								          $package_selected = true;
								          $this ->package_to_apply [$K] =  array("id"=>$id_cc_package_offer,"label"=> "Number of Free calls","type"=>$packagetype);
							          }
								  }
						          break;
						//2 : FREE TIMES 
						case 2 : 
							// CHECK IF WE HAVE A FREETIME THAT CAN APPLY FOR THIS DESTINATION
							if  ( $freetimetocall>0){
								// WE NEED TO RETRIEVE THE AMOUNT OF USED MINUTE FOR THIS CUSTOMER ACCORDING TO BILLINGTYPE (Monthly ; Weekly) & STARTDAY
								$this -> freetimetocall_used = $A2B -> FT2C_used_seconds($A2B->DBHandle, $A2B->id_card, $id_cc_package_offer, $billingtype, $startday);
								$this -> freetimetocall_left[$K] = $freetimetocall - $this->freetimetocall_used;
								if ($this -> freetimetocall_left[$K] < 0) $this -> freetimetocall_left[$K] = 0;
								if ($this -> freetimetocall_left[$K] > 0) {
									$package_selected = true; 
									$this ->package_to_apply [$K] =  array("id"=>$id_cc_package_offer,"label"=> "Free minutes","type"=>$packagetype);
								}
				
							}
							if ($this -> debug_st) print_r($this -> freetimetocall_left);
							// ****************  END PACKAGE PARAMETERS ****************
						        break;
					}
					
					$idx_pack++;
				}
				
				
				
	
			}
		}

		$credit -= $connectcharge;
		if ($disconnectcharge_after==0){
	               $credit -= $disconnectcharge; 
	               //no disconnenct charge on timeout if disconnectcharge_after is set
               }

		//$credit -= ($initblock/60)*$rateinitial;

		$callbackrate = array();

		if(($A2B->mode == 'cid-callback') || ($A2B->mode == 'all-callback')){
			$callbackrate['rateinitial']=$rateinitial;
			$callbackrate['initblock']=$initblock;
			$callbackrate['billingblock']=$billingblock;
			$callbackrate['connectcharge']=$connectcharge;
			$callbackrate['disconnectcharge']=$disconnectcharge;
			$callbackrate['stepchargea']=$stepchargea;
			$callbackrate['timechargea']=$timechargea;
			$callbackrate['stepchargeb']=$stepchargeb;
			$callbackrate['timechargeb']=$timechargeb;
			$callbackrate['stepchargec']=$stepchargec;
			$callbackrate['timechargec']=$timechargec;
		}

		$this -> ratecard_obj[$K]['callbackrate']=$callbackrate;
		$this -> ratecard_obj[$K]['timeout']=0;
		$this -> ratecard_obj[$K]['timeout_without_rules']=0;

		// CHECK IF THE USER IS ALLOW TO CALL WITH ITS CREDIT AMOUNT
		/*
		Comment from <NAME>
		This following "if" statement used to verify the minimum credit to call can be improved.
		This mininum credit should be calculated based on the destination, and the minimum billing block.
		*/
		if ($credit < $A2B->agiconfig['min_credit_2call'] && !$this -> freecall[$K]){
			return "ERROR CT1";  //NO ENOUGH CREDIT TO CALL THIS NUMBER
		}

		// if ($rateinitial==0) return "ERROR RATEINITIAL($rateinitial)";
		$TIMEOUT = 0;
		$answeredtime_1st_leg = 0;

		if ($rateinitial<=0){
			$this -> ratecard_obj[$K]['timeout']= $A2B->agiconfig['maxtime_tocall_negatif_free_route'];
			$this -> ratecard_obj[$K]['timeout_without_rules'] =	$A2B->agiconfig['maxtime_tocall_negatif_free_route'];
			// 90 min
			if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
			return $TIMEOUT;
		}
		
		if ($this -> freecall[$K]){
			if(	$this -> package_to_apply [$K] ["type"] == 0){
				$this -> ratecard_obj[$K]['timeout']= $A2B->agiconfig['maxtime_tounlimited_calls']; // default : 90 min
				$this -> ratecard_obj[$K]['timeout_without_rules'] =$A2B->agiconfig['maxtime_tounlimited_calls'];
			}else{
				$this -> ratecard_obj[$K]['timeout']= $A2B->agiconfig['maxtime_tofree_calls'];
				$this -> ratecard_obj[$K]['timeout_without_rules'] = $A2B->agiconfig['maxtime_tofree_calls'];
			}
			
			if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
			return $TIMEOUT;
		}

		// IMPROVE THE get_variable AND TRY TO RETRIEVE THEM ALL SOMEHOW
		if($A2B->mode == 'callback'){
			$calling_party_rateinitial = $agi->get_variable('RATEINITIAL', true);
			$calling_party_initblock = $agi->get_variable('INITBLOCK', true);
			$calling_party_billingblock = $agi->get_variable('BILLINGBLOCK', true);
			$calling_party_connectcharge = $agi->get_variable('CONNECTCHARGE', true);
			$calling_party_disconnectcharge = $agi->get_variable('DISCONNECTCHARGE', true);
			$calling_party_stepchargea = $agi->get_variable('STEPCHARGEA', true);
			$calling_party_timechargea = $agi->get_variable('TIMECHARGEA', true);
			$calling_party_stepchargeb = $agi->get_variable('STEPCHARGEB', true);
			$calling_party_timechargeb = $agi->get_variable('TIMECHARGEB', true);
			$calling_party_stepchargec = $agi->get_variable('STEPCHARGEC', true);
			$calling_party_timechargec = $agi->get_variable('TIMECHARGEC', true);
		}

		// 2 KIND OF CALCULATION : PROGRESSIVE RATE & FLAT RATE
		// IF FLAT RATE
		if (empty($chargea) || $chargea==0 || empty($timechargea) || $timechargea==0 ){

			if($A2B->mode == 'callback'){
				/*
				Comment from <NAME>
				In all-callback or cid-callback mode, the number of minutes for the call must be calculated
				according to the rates of both legs of the call.
				*/

				$credit -= $calling_party_connectcharge;
				$credit -= $calling_party_disconnectcharge;
				$num_min = $credit/($rateinitial + $calling_party_rateinitial);
				//I think that the answered time is in seconds
				$answeredtime_1st_leg = intval($agi->get_variable('ANSWEREDTIME', true));
			} else {
				$num_min = $credit/$rateinitial;
			}

			if ($this -> debug_st) echo "num_min:$num_min ($credit/$rateinitial)\n";
			$num_sec = intval($num_min * 60) - $answeredtime_1st_leg;
			if ($this -> debug_st) echo "num_sec:$num_sec \n";

			if ($billingblock > 0) {
				$mod_sec = $num_sec % $billingblock;
				$num_sec=$num_sec-$mod_sec;
			}

			$TIMEOUT = $num_sec;

		// IF PROGRESSIVE RATE
		}else{
			if ($this -> debug_st) echo "CYCLE A	TIMEOUT:$TIMEOUT\n";
			// CYCLE A
			$credit -= $stepchargea;

			//if ($credit<=0) return "ERROR CT2";		//NO ENOUGH CREDIT TO CALL THIS NUMBER
			if ($credit<=0){
				if ($this -> freetimetocall_left[$K] > 0){
					$this -> ratecard_obj[$K]['timeout'] = $this -> freetimetocall_left[$K];
					if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
					return $this -> freetimetocall_left[$K];
				}else{
					return "ERROR CT2";		//NO ENOUGH CREDIT TO CALL THIS NUMBER
				}
			}
			if (!($chargea>0)) return "ERROR CHARGEA($chargea)";
			$num_min = $credit/$chargea;
			if ($this -> debug_st) echo "			CYCLEA num_min:$num_min ($credit/$chargea)\n";
			$num_sec = intval($num_min * 60);
			if ($this -> debug_st) echo "			CYCLEA num_sec:$num_sec \n";
			if ($billingblocka > 0) {
				$mod_sec = $num_sec % $billingblocka;
				$num_sec=$num_sec-$mod_sec;
			}


			if (($num_sec>$timechargea) && !(empty($chargeb) || $chargeb==0 || empty($timechargeb) || $timechargeb==0) ){
				$TIMEOUT += $timechargea;
				$credit -= ($chargea/60) * $timechargea;

				if ($this -> debug_st) echo "		CYCLE B		TIMEOUT:$TIMEOUT\n";
				// CYCLE B
				$credit -= $stepchargeb;
				if ($credit<=0){
					$this -> ratecard_obj[$K]['timeout'] = $TIMEOUT + $this -> freetimetocall_left[$K];
					return $TIMEOUT + $this -> freetimetocall_left[$K];		//NO ENOUGH CREDIT TO GO TO THE CYCLE B
				}

				if (!($chargeb>0)) return "ERROR CHARGEB($chargeb)";
				$num_min = $credit/$chargeb;
				if ($this -> debug_st) echo "			CYCLEB num_min:$num_min ($credit/$chargeb)\n";
				$num_sec = intval($num_min * 60);
				if ($this -> debug_st) echo "			CYCLEB num_sec:$num_sec \n";
				if ($billingblockb > 0) {
					$mod_sec = $num_sec % $billingblockb;
					$num_sec=$num_sec-$mod_sec;
				}

				if (($num_sec>$timechargeb) && !(empty($chargec) || $chargec==0 || empty($timechargec) || $timechargec==0) )
				{
					$TIMEOUT += $timechargeb;
					$credit -= ($chargeb/60) * $timechargeb;

					if ($this -> debug_st) echo "				CYCLE C		TIMEOUT:$TIMEOUT\n";
					// CYCLE C
					$credit -= $stepchargec;
					if ($credit<=0){
						$this -> ratecard_obj[$K]['timeout'] = $TIMEOUT + $this -> freetimetocall_left[$K];
						return $TIMEOUT + $this -> freetimetocall_left[$K];		//NO ENOUGH CREDIT TO GO TO THE CYCLE C
					}

					if (!($chargec>0)) return "ERROR CHARGEC($chargec)";
					$num_min = $credit/$chargec;
					if ($this -> debug_st) echo "			CYCLEC num_min:$num_min ($credit/$chargec)\n";
					$num_sec = intval($num_min * 60);
					if ($this -> debug_st) echo "			CYCLEC num_sec:$num_sec \n";
					if ($billingblockc > 0) {
						$mod_sec = $num_sec % $billingblockc;
						$num_sec=$num_sec-$mod_sec;
					}


					if (($num_sec>$timechargec)){
						if ($this -> debug_st) echo "		OUT CYCLE C		TIMEOUT:$TIMEOUT\n";
						$TIMEOUT += $timechargec;
						$credit -= ($chargec/60) * $timechargec;

						// IF CYCLE C IS FINISH USE THE RATEINITIAL
						$num_min = $credit/$rateinitial;
						if ($this -> debug_st) echo "			OUT CYCLEC num_min:$num_min ($credit/$rateinitial)\n";
						$num_sec = intval($num_min * 60);
						if ($this -> debug_st) echo "			OUT CYCLEC num_sec:$num_sec \n";
						if ($billingblock > 0) {
							$mod_sec = $num_sec % $billingblock;
							$num_sec=$num_sec-$mod_sec;
						}
						$TIMEOUT += $num_sec;
						// THIS IS THE END

					}else{
						$TIMEOUT += $num_sec;
					}

				}else{

					if (($num_sec>$timechargeb)){
						$TIMEOUT += $timechargeb;
						if ($this -> debug_st) echo "		OUT CYCLE B		TIMEOUT:$TIMEOUT\n";
						$credit -= ($chargeb/60) * $timechargeb;

						// IF CYCLE B IS FINISH USE THE RATEINITIAL
						$num_min = $credit/$rateinitial;
						if ($this -> debug_st) echo "			OUT CYCLEB num_min:$num_min ($credit/$rateinitial)\n";
						$num_sec = intval($num_min * 60);
						if ($this -> debug_st) echo "			OUT CYCLEB num_sec:$num_sec \n";
						if ($billingblock > 0) {
							$mod_sec = $num_sec % $billingblock;
							$num_sec=$num_sec-$mod_sec;
						}

						$TIMEOUT += $num_sec;
						// THIS IS THE END

					} else {
						$TIMEOUT += $num_sec;
					}
				}

			} else {

				if (($num_sec>$timechargea)) {
					$TIMEOUT += $timechargea;
					if ($this -> debug_st) echo "		OUT CYCLE A		TIMEOUT:$TIMEOUT\n";
					$credit -= ($chargea/60) * $timechargea;

					// IF CYCLE A IS FINISH USE THE RATEINITIAL
					$num_min = $credit/$rateinitial;
					if ($this -> debug_st) echo "			OUT CYCLEA num_min:$num_min ($credit/$rateinitial)\n";
					$num_sec = intval($num_min * 60);
					if ($this -> debug_st) echo "			OUT CYCLEA num_sec:$num_sec \n";;
					if ($billingblock > 0) {
						$mod_sec = $num_sec % $billingblock;
						$num_sec=$num_sec-$mod_sec;
					}

					$TIMEOUT += $num_sec;
					// THIS IS THE END

				} else {
					$TIMEOUT += $num_sec;
				}
			}
		}
		//Call time to speak without rate rules... idiot rules
		$num_min_WR = $initial_credit/$rateinitial;
		$num_sec_WR = intval($num_min_WR * 60);
		$this -> ratecard_obj[$K]['timeout_without_rules'] = $num_sec_WR+$this -> freetimetocall_left[$K];
		
		$this -> ratecard_obj[$K]['timeout']=$TIMEOUT + $this -> freetimetocall_left[$K];
		if ($this -> debug_st) print_r($this -> ratecard_obj[$K]);
		RETURN $TIMEOUT + $this -> freetimetocall_left[$K];
	}

	/*
	 * RATE ENGINE - CALCUL COST OF THE CALL
	 * - calcul the credit consumed by the call
	 */
	function rate_engine_calculcost (&$A2B, $callduration, $K=0)
	{
		global $agi;
		$K = $this->usedratecard;

		$buyrate 						= a2b_round(abs($this -> ratecard_obj[$K][9]));
		$buyrateinitblock 				= $this -> ratecard_obj[$K][10];
		$buyrateincrement 				= $this -> ratecard_obj[$K][11];

		$rateinitial 					= a2b_round(abs($this -> ratecard_obj[$K][12]));
		$initblock 						= $this -> ratecard_obj[$K][13];
		$billingblock 					= $this -> ratecard_obj[$K][14];
		$connectcharge 					= a2b_round(abs($this -> ratecard_obj[$K][15]));
		$disconnectcharge 				= a2b_round(abs($this -> ratecard_obj[$K][16]));
		$disconnectcharge_after				= $this->ratecard_obj[$K][60];
		$stepchargea 					= $this -> ratecard_obj[$K][17];
		$chargea 						= a2b_round(abs($this -> ratecard_obj[$K][18]));
		$timechargea 					= $this -> ratecard_obj[$K][19];
		$billingblocka 					= $this -> ratecard_obj[$K][20];
		$stepchargeb 					= $this -> ratecard_obj[$K][21];
		$chargeb 						= a2b_round(abs($this -> ratecard_obj[$K][22]),4);
		$timechargeb 					= $this -> ratecard_obj[$K][23];
		$billingblockb 					= $this -> ratecard_obj[$K][24];
		$stepchargec 					= $this -> ratecard_obj[$K][25];
		$chargec 						= a2b_round(abs($this -> ratecard_obj[$K][26]),4);
		$timechargec 					= $this -> ratecard_obj[$K][27];
		$billingblockc 					= $this -> ratecard_obj[$K][28];
		// Initialization rounding calltime and rounding threshold variables
		$rounding_calltime 				= $this->ratecard_obj[$K][54];
		$rounding_threshold 			= $this->ratecard_obj[$K][55];
		// Initialization additional block charge and additional block charge time variables
		$additional_block_charge 		= $this->ratecard_obj[$K][56];
		$additional_block_charge_time 	= $this->ratecard_obj[$K][57];
		$additional_grace_time			= $this->ratecard_obj[$K][58];
		$minimal_call_cost 				= $this->ratecard_obj[$K][59];

		
		if (!is_numeric($rounding_calltime))			$rounding_calltime = 0;
		if (!is_numeric($rounding_threshold))			$rounding_threshold = 0;
		if (!is_numeric($additional_block_charge))		$additional_block_charge = 0;
		if (!is_numeric($additional_block_charge_time))	$additional_block_charge_time = 0;

		if (!is_numeric($this->freetimetocall_used))	$this->freetimetocall_used = 0;
		if ($this -> debug_st)  echo "CALLDURATION: $callduration - freetimetocall_used=$this->freetimetocall_used\n\n";
		$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_CALCULCOST: K=$K - CALLDURATION:$callduration - freetimetocall_used=$this->freetimetocall_used]");

		$cost = 0;
		$cost -= $connectcharge;
		if (($disconnectcharge_after>=$callduration)||($disconnectcharge_after==0)){
                $cost -= $disconnectcharge;
	        }

		$this -> real_answeredtime = $callduration;
		$callduration =$callduration + $additional_grace_time;			
		
		/*
		 * In following condition callduration will be updated
		 * according to the the rounding_calltime and rounding_threshold
		 * Reference to the TODO : ADDITIONAL CHARGES ON REALTIME BILLING - 1
		 */
		if($rounding_calltime > 0 && $rounding_threshold > 0 && $callduration > $rounding_threshold && $rounding_calltime > $callduration) {
			$callduration = $rounding_calltime;
			// RESET THE SESSIONTIME FOR CDR
			$this -> answeredtime = $rounding_calltime;
		}

		/*
		 * Following condition will append cost of call
		 * according to the the additional_block_charge and additional_block_charge_time
		 * Reference to the TODO : ADDITIONAL CHARGES ON REALTIME BILLING - 2
		 */
		// If call duration is greater then block charge time
		if ($callduration >= $additional_block_charge_time) {
			$block_charge = intval($callduration / $additional_block_charge_time);
			$cost -= $block_charge * $additional_block_charge;
		}

		// #### 	CALCUL BUYRATE COST   #####
		$buyratecallduration = $this -> real_answeredtime + $additional_grace_time;

		$buyratecost =0;
		if ($buyratecallduration < $buyrateinitblock) $buyratecallduration = $buyrateinitblock;
		if ($buyrateincrement > 0 && $buyratecallduration > $buyrateinitblock) {
			$mod_sec = $buyratecallduration % $buyrateincrement; // 12 = 30 % 18
			if ($mod_sec>0) $buyratecallduration += ($buyrateincrement - $mod_sec); // 30 += 18 - 12
		}
		$buyratecost -= ($buyratecallduration/60) * $buyrate;
		if ($this -> debug_st)  echo "1. cost: $cost\n buyratecost:$buyratecost\n";

        // IF IT S A FREE CALL, WE CAN STOP HERE COST = 0
        if($this -> freecall[$K]){
        	$this -> lastcost = 0;
			$this -> lastbuycost = $buyratecost;
			if ($this -> debug_st)  echo "FINAL COST: $cost\n\n";
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_CALCULCOST: K=$K - BUYCOST: $buyratecost - SELLING COST: $cost]");
        	return; 
        }

		// #### 	CALCUL SELLRATE COST   #####
		if ($callduration < $initblock) $callduration = $initblock;
		if ($this -> freetimetocall_left[$K] >= $callduration) {
			$this -> freetimetocall_used = $callduration;
		}
		

		$callduration = $callduration - $this->freetimetocall_used;

		// 2 KIND OF CALCULATION : PROGRESSIVE RATE & FLAT RATE
		// IF FLAT RATE
		if (empty($chargea) || $chargea==0 || empty($timechargea) || $timechargea==0) {

			if ($billingblock > 0 && $callduration > $initblock) {
				$mod_sec = $callduration % $billingblock;
				if ($mod_sec>0) $callduration += ($billingblock - $mod_sec);
			}

			$cost -= ($callduration/60) * $rateinitial;
			if ($this -> debug_st)  echo "1.a cost: $cost\n";
			$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[TEMP - CC_RATE_ENGINE_CALCULCOST: 1. COST: $cost]:[ ($callduration/60) * $rateinitial ]");

		// IF PROGRESSIVE RATE
		} else {

			if ($this -> debug_st) echo "CYCLE A	COST:$cost\n";
			// CYCLE A
			$cost -= $stepchargea;
			if ($this -> debug_st)  echo "1.A cost: $cost\n\n";

			if ($callduration > $timechargea) {
				$duration_report = $callduration - $timechargea;
				$callduration = $timechargea;
			}

			if ($billingblocka > 0) {
				$mod_sec = $callduration % $billingblocka;
				if ($mod_sec>0) $callduration += ($billingblocka - $mod_sec);
			}
			$cost -= ($callduration/60) * $chargea;

			if (($duration_report>0) && !(empty($chargeb) || $chargeb==0 || empty($timechargeb) || $timechargeb==0)) {
				$callduration = $duration_report;
				$duration_report = 0;

				// CYCLE B
				$cost -= $stepchargeb;
				if ($this -> debug_st)  echo "1.B cost: $cost\n\n";

				if ($callduration > $timechargeb) {
					$duration_report = $callduration - $timechargeb;
					$callduration=$timechargeb;
				}

				if ($billingblockb > 0) {
					$mod_sec = $callduration % $billingblockb;
					if ($mod_sec>0) $callduration += ($billingblockb - $mod_sec);
				}
				$cost -= ($callduration/60) * $chargeb; // change chargea -> chargeb thanks to Abbas :D

				if (($duration_report>0) && !(empty($chargec) || $chargec==0 || empty($timechargec) || $timechargec==0) )
				{
					$callduration=$duration_report;
					$duration_report=0;

					// CYCLE C
					$cost -= $stepchargec;
					if ($this -> debug_st)  echo "1.C cost: $cost\n\n";

					if ($callduration > $timechargec){
						$duration_report = $callduration - $timechargec;
						$callduration=$timechargec;
					}

					if ($billingblockc > 0) {
						$mod_sec = $callduration % $billingblockc;
						if ($mod_sec>0) $callduration += ($billingblockc - $mod_sec);
					}
					$cost -= ($callduration/60) * $chargec;
				}
			}

			if ($duration_report > 0){

				if ($billingblock > 0) {
					$mod_sec = $duration_report % $billingblock;
					if ($mod_sec>0) $duration_report += ($billingblock - $mod_sec);
				}

				$cost -= ($duration_report/60) * $rateinitial;
				$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[TEMP - CC_RATE_ENGINE_CALCULCOST: 2. DURATION_REPORT:$duration_report - COST: $cost]");
			}

		}
		$cost = a2b_round($cost);
		if ($this -> debug_st)  echo "FINAL COST: $cost\n\n";
		$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_CALCULCOST: K=$K - BUYCOST: $buyratecost - SELLING COST: $cost]");
		
		if($cost> (0-$minimal_call_cost)){
			$this -> lastcost = 0 - $minimal_call_cost;
		}else{
			$this -> lastcost = $cost;
		}
		$this -> lastbuycost = $buyratecost;
		
	}


    /*
		SORT_ASC : Tri en ordre ascendant
      	SORT_DESC : Tri en ordre descendant
	*/
	function array_csort()
	{
		$args = func_get_args();
		$marray = array_shift($args);
		$i=0;
		$msortline = "return(array_multisort(";
		foreach ($args as $arg) {
			$i++;
			if (is_string($arg)) {
				foreach ($marray as $row) {
					$sortarr[$i][] = $row[$arg];
				}
			} else {
				$sortarr[$i] = $arg;
			}
			$msortline .= "\$sortarr[".$i."],";
		}
		$msortline .= "\$marray));";

		eval($msortline);
		return $marray;
	}


	/*
	 * RATE ENGINE - UPDATE SYSTEM (DURATIONCALL)
	 * Calcul the duration allowed for the caller to this number
	 */
	function rate_engine_updatesystem (&$A2B, &$agi, $calledstation, $doibill = 1, $didcall=0, $callback=0)
	{
		$K = $this->usedratecard;
		// ****************  PACKAGE PARAMETERS ****************
		//$freetimetocall_package_offer = $this -> ratecard_obj[$K][45];
		//$freetimetocall = $this -> ratecard_obj[$K][46];
		$id_cc_package_group= $this -> ratecard_obj[$K][45];
		$additional_grace_time= $this->ratecard_obj[$K][58];
		if ($A2B -> CC_TESTING){
			$sessiontime = 120;
			$dialstatus = 'ANSWERED';
		} else {
			$sessiontime = $this -> answeredtime;
			$dialstatus = $this -> dialstatus;
		}
		if ($sessiontime > 0) {
			// HANDLE FREETIME BEFORE CALCULATE THE COST
			$this->freetimetocall_used = 0;
			$A2B -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, "ft2c_package_offer : $freetimetocall_package_offer ; $freetimetocall ; ".$this -> freetimetocall_left[$K]);
			if ($this -> debug_st) print_r($this -> freetimetocall_left[$K]);

			if (($id_cc_package_group!=-1) && ($this ->package_to_apply[$K] !=null )){
			$id_package_offer =$this ->package_to_apply[$K]["id"];
	
				//$id_card_package_offer = $this ->package_to_apply[$K]["id"];
                switch($this ->package_to_apply[$K]["type"] ){
                	//Unlimited
                	case 0 : $this->freetimetocall_used = $sessiontime;break;
                	//free calls
                	case 1 : $this->freetimetocall_used = $sessiontime;break;
                	//free times
                	case 2 :
						if ($this -> freetimetocall_left[$K] >= $sessiontime){
							$this->freetimetocall_used = $sessiontime;
						}else{
							$this->freetimetocall_used = $this -> freetimetocall_left[$K];
						}
						break;
                	
                }

				$this -> rate_engine_calculcost ($A2B, $sessiontime, 0);
				// rate_engine_calculcost could have change the duration of the call
				$sessiontime = $this -> answeredtime;
				//add grace time
				if($sessiontime>0 && $additional_grace_time>0){
					$sessiontime = $sessiontime + $additional_grace_time;
				}

				$QUERY_FIELS = 'id_cc_card, id_cc_package_offer, used_secondes';
				$QUERY_VALUES = "'".$A2B -> id_card."', '$id_package_offer', '$this->freetimetocall_used'";
				$id_card_package_offer = $A2B -> instance_table -> Add_table ($A2B -> DBHandle, $QUERY_VALUES, $QUERY_FIELS, 'cc_card_package_offer', 'id');
				$A2B -> debug( VERBOSE | WRITELOG, $agi, __FILE__, __LINE__, ":[ID_CARD_PACKAGE_OFFER CREATED : $id_card_package_offer]:[$QUERY_VALUES]");
			} else {

				$this -> rate_engine_calculcost ($A2B, $sessiontime, 0);
				
				// rate_engine_calculcost could have change the duration of the call
				$sessiontime = $this -> answeredtime;
				if($sessiontime>0 && $additional_grace_time>0){
					$sessiontime = $sessiontime + $additional_grace_time;
				}
			}


		} else {
			$sessiontime = 0;
		}

		$calldestination = $this -> ratecard_obj[$K][5];
		$id_tariffgroup = $this -> ratecard_obj[$K][2];
		$id_tariffplan = $this -> ratecard_obj[$K][3];
		$id_ratecard = $this -> ratecard_obj[$K][6];
		
		$buycost = 0;
		if ($doibill==0 || $sessiontime < $A2B->agiconfig['min_duration_2bill']) {
			$cost = 0;
		} else{
			$cost = $this -> lastcost;
			$buycost = abs($this -> lastbuycost);
		}

		if ($cost<0) {
			$signe='-';
			$signe_cc_call ='+';
		} else {
			$signe='+';
			$signe_cc_call ='-';
		}

		$buyrateapply = $this -> ratecard_obj[$K][9];
		$rateapply = $this -> ratecard_obj[$K][12];

		$A2B -> debug( WRITELOG, $agi, __FILE__, __LINE__, "[CC_RATE_ENGINE_UPDATESYSTEM: usedratecard K=$K - (sessiontime=$sessiontime :: dialstatus=$dialstatus :: buycost=$buycost :: cost=$cost : signe_cc_call=$signe_cc_call: signe=$signe)]");

		// CALLTYPE -  0 = NORMAL CALL ; 1 = VOIP CALL (SIP/IAX) ; 2= DIDCALL + TRUNK ; 3 = VOIP CALL DID ; 4 = CALLBACK call
		if ($didcall) $calltype = 2;
		elseif ($callback) $calltype = 4;
		else $calltype = 0;
		
		if (strlen($this -> dialstatus_rev_list[$dialstatus])>0)
			$terminatecauseid = $this -> dialstatus_rev_list[$dialstatus];
		else
			$terminatecauseid = 0;
			
		$QUERY_COLUMN = "uniqueid, sessionid, card_id, nasipaddress, starttime, sessiontime, real_sessiontime, calledstation, ".
			" terminatecauseid, stoptime, sessionbill, id_tariffgroup, id_tariffplan, id_ratecard, " .
			" id_trunk, src, sipiax, buycost, id_card_package_offer, dnid, id_cc_prefix";
		$QUERY = "INSERT INTO cc_call ($QUERY_COLUMN) VALUES ('".$A2B->uniqueid."', '".$A2B->channel."', '".
			$A2B->id_card."', '".$A2B->hostname."', ";

		if ($A2B->config["global"]['cache_enabled']){
			$QUERY .= " datetime( strftime('%s','now') - $sessiontime, 'unixepoch','localtime')";	
		}else{
			if ($A2B->config["database"]['dbtype'] == "postgres"){
				$QUERY .= "CURRENT_TIMESTAMP - interval '$sessiontime seconds' ";
			}else{
				$QUERY .= "CURRENT_TIMESTAMP - INTERVAL $sessiontime SECOND ";
			}
		}

		$QUERY .= 	", '$sessiontime', '".$this->real_answeredtime."', '$calledstation', '$terminatecauseid', "; 
		if ($A2B->config["global"]['cache_enabled']){
			$QUERY .= "datetime('now','localtime')"; 
		}else{
			$QUERY .= "now()";
		}
		
		$QUERY .= " , '$signe_cc_call".a2b_round(abs($cost))."', ".
					" '$id_tariffgroup', '$id_tariffplan', '$id_ratecard', '".$this -> usedtrunk."', '".$A2B->CallerID."', '$calltype', ".
					" '$buycost', '$id_card_package_offer', '".$A2B->dnid."', '".$calldestination."')";
{'fraction_non_alphanumeric': 0.1621249013263956, 'fraction_numerical': 0.013097183771944453, 'mean_word_length': 3.8832184157663283, 'pattern_counts': {'":': 6, '<': 75, '<?xml version=': 0, '>': 805, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3721763', 'n_tokens_mistral': 38667, 'n_tokens_neox': 33151, 'n_words': 6737}