<?php
/************************************************************
 * InfiniteWP Admin panel									*
 * Copyright (c) 2012 Revmakx								*
 * www.revmakx.com											*
 *															*
 ************************************************************/

$settings = Reg::get('settings');
define('MAX_SIMULTANEOUS_REQUEST_PER_IP', $settings['MAX_SIMULTANEOUS_REQUEST_PER_IP'] > 0 ? $settings['MAX_SIMULTANEOUS_REQUEST_PER_IP'] : 2 );

define('MAX_SIMULTANEOUS_REQUEST', $settings['MAX_SIMULTANEOUS_REQUEST'] > 0 ? $settings['MAX_SIMULTANEOUS_REQUEST'] : 3 );
//define('MAX_SIMULTANEOUS_REQUEST_PER_SERVERGROUP', $settings['MAX_SIMULTANEOUS_REQUEST_PER_SERVERGROUP']);

define('TIME_DELAY_BETWEEN_REQUEST_PER_IP', $settings['TIME_DELAY_BETWEEN_REQUEST_PER_IP'] >= 0 ? $settings['TIME_DELAY_BETWEEN_REQUEST_PER_IP'] : 200 );

function executeJobs($callCount = 1){
	
	if(isset($GLOBALS['IS_EXECUTE_JOBS_OPEN']) && $GLOBALS['IS_EXECUTE_JOBS_OPEN']){
		echo 'recurrsive execute jobs call';
		return false;//recurrsive call
	}
	$GLOBALS['IS_EXECUTE_JOBS_OPEN'] = true;
	
	$settings = Reg::get('settings');
        $connectionMethod = getOption('connectionMethod');
        $connectionMode = getOption('connectionMode');
        $upperConnectionLevel = getOption('upperConnectionLevel');
	  $noRequestRunning = true;
	  $requestInitiated = 0;
	  $requestPending 	= 0;
	  $isExecuteRequest = false;
	  static $lastIPRequestInitiated = '';
	  

	  $totalCurrentRunningRequest = DB::getField("?:history H LEFT JOIN ?:sites S ON H.siteID = S.siteID", "COUNT(H.historyID)", "H.status IN ('initiated', 'running')");
	  
	  if($totalCurrentRunningRequest >= MAX_SIMULTANEOUS_REQUEST){ echo 'MAX_SIMULTANEOUS_REQUEST'; $GLOBALS['IS_EXECUTE_JOBS_OPEN'] = false; return false; }//dont execute any request
			  
	  $runningRequestByIP = DB::getFields("?:history H LEFT JOIN ?:sites S ON H.siteID = S.siteID", "COUNT(H.historyID), S.IP", "H.status IN ('initiated', 'running') AND H.isPluginResponse = '1' GROUP BY S.IP", "IP");//H.isPluginResponse = 1 only WP sites call
	  
	  if(!empty($runningRequestByIP)){ //some request(s) are running
		  $noRequestRunning = false;
		  $runningRequestByServer = DB::getFields("?:history H LEFT JOIN ?:sites S ON H.siteID = S.siteID", "COUNT(H.historyID), S.serverGroup", "H.status IN ('initiated', 'running') GROUP BY S.serverGroup", "serverGroup");			
	  }
	  
	  //get pending request
	  $where = array(
		      		'query' =>  "(H.status = 'pending' OR (H.status = ':status' AND H.timescheduled <= :timescheduled AND H.timescheduled > 0) OR H.status = 'retry') ORDER BY H.historyID",
		      		'params' => array(
		               ':status'=>'scheduled',
		               ':timescheduled'=>time()
	   				)
				); 
	  $pendingRequests = DB::getArray("?:history H LEFT JOIN ?:sites S ON H.siteID = S.siteID", "H.historyID, S.IP, S.serverGroup, H.actionID, H.status, H.runCondition, H.isPluginResponse, H.retried", $where);
	  
	  if($noRequestRunning){		
		  $runningRequestByIP 	= array();
		  $runningRequestByServer = array();				
	  }
	  
	  
	  if(!empty($runningRequestByIP) && $settings['CONSIDER_3PART_IP_ON_SAME_SERVER'] == 1){//running IP information
		  $tempRunningRequestByIP = $runningRequestByIP;
		  $runningRequestByIP 	= array();
		  foreach($tempRunningRequestByIP as $tempIP => $tempCount){//only for IPv4
			  $IP3Part = explode('.', $tempIP);
			  array_pop($IP3Part);
			  $newTempIP = implode('.', $IP3Part);			  
			  $runningRequestByIP[$newTempIP] = $tempCount;
			  
		  }
	  }
	  
	if(!empty($pendingRequests) && is_array($pendingRequests)){
	  foreach($pendingRequests as $request){
		  $checkIPRestriction = true;
		  
		  $IPConsidered = $request['IP'];
		  
		  if($request['isPluginResponse'] === '0'){
			  $request['IP'] = '';
			  $checkIPRestriction = false;
		  }
		  
		  if($checkIPRestriction && $settings['CONSIDER_3PART_IP_ON_SAME_SERVER'] == 1){//only for IPv4
			  $IP3Part = explode('.', $IPConsidered);
			  array_pop($IP3Part);
			  $IP3Part = implode('.', $IP3Part);			  
			  $IPConsidered = $IP3Part;
		  }
		  
		 
		  if(!empty($request['runCondition']) && !isTaskRunConditionSatisfied($request['runCondition'])){
			   continue;  
		  }
		  
		  if($checkIPRestriction && !isset($runningRequestByIP[ $IPConsidered ])) $runningRequestByIP[ $IPConsidered ] = 0;
		 // if(!isset($runningRequestByServer[ $request['serverGroup'] ])) $runningRequestByServer[ $request['serverGroup'] ] = 0;
		  
		  if($totalCurrentRunningRequest >= MAX_SIMULTANEOUS_REQUEST){ echo 'MAX_SIMULTANEOUS_REQUEST';  $GLOBALS['IS_EXECUTE_JOBS_OPEN'] = false; return false; }
		  
		  //check already request are running in allowed level 
		  if($checkIPRestriction && $runningRequestByIP[ $IPConsidered ] >= MAX_SIMULTANEOUS_REQUEST_PER_IP /*|| $runningRequestByServer[ $request['serverGroup'] ] >=  MAX_SIMULTANEOUS_REQUEST_PER_SERVERGROUP*/){
			 
			  
			  if($runningRequestByIP[ $IPConsidered ] >= MAX_SIMULTANEOUS_REQUEST_PER_IP )
			  echo 'MAX_SIMULTANEOUS_REQUEST_PER_IP<br>';
			 /* if($runningRequestByServer[ $request['serverGroup'] ] >=  MAX_SIMULTANEOUS_REQUEST_PER_SERVERGROUP)
			  echo 'MAX_SIMULTANEOUS_REQUEST_PER_SERVERGROUP<br>';*/
			  continue; //already request are running on the limits
		  }

		  isIPReqAndTotalRunReqOne($callCount, $runningRequestByIP[ $IPConsidered ], $totalCurrentRunningRequest, $request);
		  
		  $updateRequest = array('H.status' => 'initiated', 'H.microtimeInitiated' => microtime(true));
		  
		  $where = array(
		      		'query' =>   "(H.status = ':pending' OR (H.status = ':scheduled' AND H.timescheduled <= :timescheduled AND H.timescheduled > 0)) AND H.historyID = :historyID",
		      		'params' => array(
		               ':pending'=>'pending',
		               ':scheduled'=>'scheduled',
		               ':timescheduled'=>time(),
		               ':historyID'=>$request['historyID']
	   				)
				);
		  $isUpdated = DB::update("?:history H", $updateRequest, $where);
		  //panelRequestManager::addSyatemActiveJobs($request['historyID']); For now this functionality no need 			 	
		  $isUpdated = DB::affectedRows();
		  
		  if($isUpdated){
			  //ready to run a child php to run the request
			  
			  if($lastIPRequestInitiated == $IPConsidered){
				  usleep((TIME_DELAY_BETWEEN_REQUEST_PER_IP * 1000));
			  }
			  //(defined('CRON_MODE')  && ( CRON_MODE == 'systemCronShortTime'  || CRON_MODE == 'systemCronDefault') ) //need to avoid balance idle time when systemCron is triggered. so that new trigger call(multiCall) can be called soon
			 // echo '<br>executing child process';
			  if(/*defined('IS_EXECUTE_FILE') || */(defined('CRON_MODE')  && ( CRON_MODE == 'systemCronShortTime'  || CRON_MODE == 'systemCronDefault') ) || /*$settings['executeUsingBrowser'] == 1*/ $connectionMode == 'curlMode' || ($connectionMethod=='auto' && $upperConnectionLevel=='curlMode')){//this will also statisfy Reg::get('settings.executeUsingBrowser') == 1
                  $curlConnection = array('mode'=>'curl');
				  //echo '<br>executing_directly';
				  executeRequest($request['historyID']);
                                  updateConnectionMode($curlConnection,$request['historyID']);
				  $isExecuteRequest = true;
				  $requestPending++;
			  }
			  else{
				 // echo '<br>executing_async';
				 //$callAsyncInfo = callURLAsync(APP_URL.EXECUTE_FILE, array('historyID' =>  $request['historyID'], 'actionID' => $request['actionID']));					 
                                 $callAsyncInfo = nonBlockingBackgroundJob(array('historyID' =>  $request['historyID'], 'actionID' => $request['actionID']));
                                 if(!$callAsyncInfo['status'] && $connectionMethod==='auto'){
                                 	$curlConnection = array('mode'=>'curl');
                                 	Reg::set('settings.executeUsingBrowser', true);
                                    executeRequest($request['historyID']);
                                    updateConnectionMode($curlConnection,$request['historyID']);
                                    $isExecuteRequest = true;
                                    $requestPending++;
                                 }else{
                                    onAsyncFailUpdate($request['historyID'], $callAsyncInfo);
                                 }
				 // echo '<pre>callExecuted:'; echo'</pre>';
			  }

			  $requestInitiated++; 
			  
			  if($checkIPRestriction) $runningRequestByIP[ $IPConsidered ]++;
			 // $runningRequestByServer[ $request['serverGroup'] ] ++;
			  $totalCurrentRunningRequest++;
			  
			  
			  if($checkIPRestriction) $lastIPRequestInitiated = $IPConsidered;
			  
			  if($isExecuteRequest){ break; }//breaking here once executeRequest runs(direct call) next forloop job might be executed by other instance because that job loaded in array which already loaded from DB, still only the job inititated here will run  $isUpdated = DB::affectedRows();
		  }
		  else{
			echo 'update error, this request might be executed by someother instance.';  
		  }
	  }
	 } 
	  //return process
	  $GLOBALS['IS_EXECUTE_JOBS_OPEN'] = false;
	  return array('requestInitiated' => $requestInitiated, 'requestPending' => $requestPending);
}


function retryFailedTasks($historyID, $actionID){
	$where = array(
		      		'query' =>  "actionID = ':actionID' AND status NOT IN ('retry', 'completed', 'netError', 'error')",
			      	'params' => array(
		               ':actionID'=>$actionID
					)
				);
	$runningTask = DB::getField("?:history", "COUNT(historyID)", $where);
	if($runningTask == 0) {
		$updateRequest = array('H.status' => 'pending', 'H.retried' => '1', 'H.microtimeInitiated' => microtime(true));
		$where = array(
		      		'query' =>  "H.historyID=':historyID'",
			      	'params' => array(
		               ':historyID'=>$historyID
					)
				);
		$isUpdated = DB::update("?:history H", $updateRequest, $where);
	}
}

function isIPReqAndTotalRunReqOne($callCount, $runningRequestByIP, $totalCurrentRunningRequest, $request){
	if ($callCount == 1 && $runningRequestByIP <= 1 && $totalCurrentRunningRequest <= 1) {
		if ($request['retried'] == '0'){
		 	if($request['status'] == 'retry') {
				retryFailedTasks($request['historyID'], $request['actionID']);
			} 
		}
	}
}

function isTaskRunConditionSatisfied($runCondition){
	
	if(empty($runCondition)){ return true; }
	
	$runCondition = unserialize($runCondition);
	
	if(empty($runCondition['satisfyType'])){
		$runCondition['satisfyType'] = 'OR';
	}
	
	if($runCondition['satisfyType'] != 'AND' && $runCondition['satisfyType'] != 'OR'){
		return ;
	}
	
	$OK = true;
	
	
	if(!empty($runCondition['query'])){
		$query = $runCondition['query'];
		$tempResult = DB::getExists('?:'.$query['table'], $query['select'], $query['where']);
		if($runCondition['satisfyType'] == 'OR' && !empty($tempResult)){
			return true;
		}
		elseif($runCondition['satisfyType'] == 'AND' && empty($tempResult)){
			$OK = false;
		}
	}
	if(!empty($runCondition['maxWaitTime'])){
		$tempResult = ($runCondition['maxWaitTime'] <= time());
		if($runCondition['satisfyType'] == 'OR' && !empty($tempResult)){
			return true;
		}
		elseif($runCondition['satisfyType'] == 'AND' && empty($tempResult)){
			$OK = false;
		}
	}
	
	if($runCondition['satisfyType'] == 'OR'){
		return false;
	}
	elseif($runCondition['satisfyType'] == 'AND'){
		return $OK;
	}
	return ;	
}

?>