<?php
/**
 * @class IHMJobsManagerClass
 * @brief Jobs manager
 * @details
 */
class IHMJobsManagerClass {

	protected $jobXml, $jobXmlName;

	protected $bkgRootNode = array('condition' => 'bkgSearch-treeRootNode',
			'request' => 'bkgPlot-treeRootNode',
			'download' => 'bkgDown-treeRootNode',
			'statistics' => 'bkgStatistics-treeRootNode');

	protected $resRootNode = array('condition' => 'resSearch-treeRootNode',
			'request' => 'resPlot-treeRootNode',
			'download' => 'resDown-treeRootNode',
			'statistics' => 'resStatistics-treeRootNode');

	/*
	 * @brief Constructor
	*/
	function __construct()
	{
	}

	/*
	 * @brief Load jobs file and create it if needed
	*/
	private function init()
	{
		$this->jobXmlName = IHMConfigClass::getUserJobsFile();
		$this->jobXml = new DomDocument("1.0");

		if (!file_exists($this->jobXmlName))
		{
			$res = $this->createJobsFile();
			if (!$res['success'])
				return $res;
		}

		$res = $this->jobXml->load($this->jobXmlName);
		if (!$res)
			return array(
					"success" => false,
					"message" => "Cannot load jobs file");

		return array("success" => true);
	}

	/*
	 * @brief Create a new jobs file
	*/
	private function createJobsFile()
	{
		$rootElement = $this->jobXml->createElement('jobs');
		$jobsInProgress = $this->jobXml->createElement('jobsInProgress');

		foreach ($this->bkgRootNode as $key => $value)
		{
			$element = $this->jobXml->createElement("$key");
			$element->setAttribute('xml:id',$value);
			$jobsInProgress->appendChild($element);
		}
		$jobsFinished = $this->jobXml->createElement('jobsFinished');

		foreach ($this->resRootNode as $key => $value)
		{
			$element = $this->jobXml->createElement("$key");
			$element->setAttribute('xml:id',"$value");
			$jobsFinished->appendChild($element);
		}

		$rootElement->appendChild($jobsInProgress);
		$rootElement->appendChild($jobsFinished);
		$this->jobXml->appendChild($rootElement);

		$res = $this->jobXml->save($this->jobXmlName);

		if (!$res)
			return array(
					"success" => false,
					"message" => "Cannot create new jobs file");

		return array("success" => true);
	}

	/*
	 * @brief Get the path of the request object file
	*/
	protected function getRequestObjectFilePath($id)
	{
		return IHMConfigClass::getUserJobsPath().$id.".request";
	}

	/*
	 * @brief Save a request object file
	*/
	protected function saveRequestObjectFile($obj, $id)
	{
		$file = fopen($this->getRequestObjectFilePath($id), 'w');
		fwrite($file, json_encode($obj));
		fclose($file);
	}

	/*
	 * @brief Decode a request object file
	*/
	protected function getRequestObjectFile($id)
	{
		if (!file_exists($this->getRequestObjectFilePath($id)))
			return NULL;
		return json_decode(file_get_contents($this->getRequestObjectFilePath($id)));
	}

	/*
	 * @brief Delete a request object file
	*/
	protected function deleteRequestObjectFile($id) {
		if (file_exists($this->getRequestObjectFilePath($id)))
			unlink($this->getRequestObjectFilePath($id));
	}

	/*
	 * @brief get a job status from process info
	*/
	protected function getJobStatus($running,$exitcode)
	{
		if ($running)
			return 'in_progress';

		if ($exitcode == 0)
			return 'done';

		return 'error';
	}

	/*
	 * @brief delete a job
	*/
	public function deleteJob($id)
	{
		$res = $this->init();
		if (!$res['success'])
			return $res;

		$job = $this->jobXml->getElementById($id);

		//delete job
		if (!$job)
			return array('success' => false, 'message' => "Job not reachable");

		$folder = $job->getAttribute('folder');

		//be sure that it's an AMDA working dir before deletion...
		$fullFolderPath = IHMConfigClass::getRequestPath().$folder.'/';
		if ((isset($folder)) &&
		($folder != "") &&
		is_dir($fullFolderPath) &&
		(preg_match("/DD[0-9A-Za-z]*_/",$folder) ||
		 preg_match("/Plot[0-9]*_/",$folder)))
		{
			foreach (glob($fullFolderPath.'*') as $filename)
			{
				if (is_dir($filename) && (basename($filename) == 'params'))
				{
					//recursive deletion only for "params" dir (a full recursive deletion is probably too dangerous...)
					foreach (glob($filename.'/*') as $paramname)
						unlink($paramname);
					rmdir($filename);
				}
				else
					unlink($filename);
			}
			rmdir($fullFolderPath);
		}

		$this->deleteRequestObjectFile($id);

		$job->parentNode->removeChild($job);
		$res = $this->jobXml->save($this->jobXmlName);

		if (!$res)
			return array(
					'success' => false,
					'message' => "Cannot save jobs file");
			
		return array('success' => true, 'id' => $id);
	}

	/*
	 * @brief get job info about a job
	*/
	public function getJobInfo($id)
	{
		$res = $this->init();
		if (!$res['success'])
			return $res;

		$job = $this->jobXml->getElementById($id);
		$format = 'unknown';
		$compression = 'unknown';
		if($job)
		{
			$name = $job->getAttribute('name');
			$status = $job->getAttribute('status');
			$jobType = $job->getAttribute('jobType');
			$info = $job->getAttribute('info');
			$start = $job->getAttribute('start');
			$stop = $job->getAttribute('stop');
			$result = $job->getAttribute('result');
			$folder = $job->getAttribute('folder');
			$request_obj = $this->getRequestObjectFile($id);
			if (isset($request_obj))
			{
				if (isset($request_obj->format))
				{
					$format = strtolower($request_obj->format);
					if (($format == "pdf") || ($format == "ps"))
						//auto compression for plot request
						$compression = ".tar.gz";
				}
				if (isset($request_obj->compression))
					$compression = strtolower($request_obj->compression);
			}
		}
		return array(
				'success'     => true,
				'id'          => $id,
				'name'        => $name,
				'status'      => $status,
				'jobType'     => $jobType,
				'info'        => $info,
				'start'       => $start,
				'stop'        => $stop,
				'folder'      => $folder,
				'result'      => $result,
				'format'      => $format,
				'compression' => $compression);
	}

	/*
	 * @brief Add a new job
	*/
	public function addJob($obj, $id, $folder,
			$running, $start, $result, $exitcode)
	{
		$res = $this->init();
		if (!$res['success'])
			return $res;

		$newJob = $this->jobXml->createElement('job');

		$newJob->setAttribute('xml:id', $id);
		$newJob->setAttribute('jobType', $obj->nodeType);

		switch ($obj->nodeType)
		{
			case 'condition' :
				$name = 'datamining_'.time();
				$info = $obj->expression;
				break;
			case 'statistics' :
				$name = 'statistics_'.time();
				$info = $obj->expression;
				break;
			case 'download' :
				if ($obj->downloadSrc == '2') //fits image
					$name = "download_fits_".time();
				else
					$name = "download_data_".time();
				$info = '';
				foreach ($obj->list as $param)
				{
					if ($obj->downloadSrc == '2') //fits image
						$info = $info.' '.$param->url;
					else
						$info = $info.' '.$param->name; //data
				}
				break;
			case 'request'	 :
				$name = "request_".time();
				$info = '';
				for ($i=0; $i < count($obj->children); $i++) {
					for ($j=0; $j < count($obj->children[$i]->children); $j++) {
						$info = $info.' '.$obj->children[$i]->children[$j]->name;
					}
				}
				break;
			default:
				$name = "unknown_".time();
				$info = '';
		}

		$newJob->setAttribute('name', $name);
		$newJob->setAttribute('info', $info);

		$newJob->setAttribute('folder', $folder);

		$newJob->setAttribute('result', $result);

		$newJob->setAttribute('start', date('d-m-Y H:i:s', $start));

		$newJob->setAttribute('result', $result);

		//to know if know if it's an immediate job or not
		$newJob->setAttribute('immediate', !$running);

		if ($running)
		{
			$rootJobNode = $this->jobXml->getElementById($this->bkgRootNode[$obj->nodeType]);
			if (!$rootJobNode)
			{
				$key = $obj->nodeType;
				$rootJobNode =  $this->jobXml->createElement("$key");
				$rootJobNode->setAttribute('xml:id', $this->bkgRootNode[$obj->nodeType]);
				$jobsInProgress = $this->jobXml->getElementsByTagName('jobsInProgress')->item(0);
				$jobsInProgress->appendChild($rootJobNode);
			}
		}
		else
		{
			$rootJobNode = $this->jobXml->getElementById($this->resRootNode[$obj->nodeType]);
			if (!$rootJobNode)
			{
				$key = $obj->nodeType;
				$rootJobNode =  $this->jobXml->createElement("$key");
				$rootJobNode->setAttribute('xml:id', $this->resRootNode[$obj->nodeType]);
				$jobsFinished = $this->jobXml->getElementsByTagName('jobsFinished')->item(0);
				$jobsFinished->appendChild($rootJobNode);
			}
		}

		$rootJobNode->appendChild($newJob);
		 
		if (!$this->jobXml->save($this->jobXmlName))
			return  array("success" => false, "message" => "Cannot save job manager file");

		$this->saveRequestObjectFile($obj, $id);

		$this->updateJobStatus($id, $running, $exitcode);

		return  $this->getJobInfo($id);
	}

	/*
	 * @brief Update the status of a job
	*/
	public function updateJobStatus($id, $running, $exitcode)
	{
		$res = $this->init();
		if (!$res['success'])
			return $res;

		$job = $this->jobXml->getElementById($id);

		if (!isset($job))
			return array("success" => false, "message" => "Cannot found job");
			
		$jobstatus = $this->getJobStatus($running,$exitcode);
		$job->setAttribute('status', $jobstatus);

		if ($running)
			$job->setAttribute('stop', 'unknown');
		else if ($job->getAttribute('stop') == '' || $job->getAttribute('stop') == 'unknown')
		{
			$job->setAttribute('stop', date('d-m-Y H:i:s', time()));
			$this->jobXml->getElementById($this->resRootNode[$job->getAttribute('jobType')])->appendChild($job);
		}
			
		$res = $this->jobXml->save($this->jobXmlName);

		if (!$res)
			return array(
					'success' => false,
					'message' => "Cannot save jobs status file");
			
		return $this->getJobInfo($id);
	}

	/*
	 * @brief Get the list of jobs with a specific status
	*/
	public function getJobsByStatus($status)
	{
		$res = $this->init();
		if (!$res['success'])
			return $res;
			
		$xp = new domxpath($this->jobXml);
		$jobs = $xp->query("//job[@status='".$status."']");

		$jobsId = array();
		foreach ($jobs as $job)
			$jobsId[] = $job->getAttribute('xml:id');

		return array('success' => true, 'jobs' => $jobsId);
	}
	
	/*
	 * @brief Get jobs that use a specific working dir
	*/
	public function getJobsByWorkingDir($folder)
	{
		$res = $this->init();
		if (!$res['success'])
			return $res;
			
		$xp = new domxpath($this->jobXml);
		$jobs = $xp->query("//job[@folder='".$folder."']");
	
		$jobsId = array();
		foreach ($jobs as $job)
			$jobsId[] = $job->getAttribute('xml:id');
	
		return array('success' => true, 'jobs' => $jobsId);
	}

	/*
	 * @brief Get all jobs to clean (immediate result jobs)
	*/
	public function getJobsToClean()
	{
		$res = $this->init();
		if (!$res['success'])
			return $res;

		// Get immediate jobs
		$xp = new domxpath($this->jobXml);
		$jobs = $xp->query("//job[@immediate='1']");

		$jobsId = array();
		foreach ($jobs as $job)
			$jobsId[] = $job->getAttribute('xml:id');

		return array("success" => true, "jobs" => $jobsId);
	}
}
?>