<?php
/**
 * @class JobsMgr
 * @brief Manage all batch jobs
 * @version $Id: JobsMgr.php 2757 2015-02-19 12:15:41Z elena $
 *    
 */

  class JobsMgr {

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

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

    protected $jobPrefix = array( 'condition' => 'bkgrd_cond_', 
                                'request' => 'bkgrd_req_',
                                'download' => 'bkgrd_down_');

    public $success = true;

    function __construct() {

        $this->jobXmlName = USERJOBDIR."jobs.xml";
        $this->jobXml = new DomDocument("1.0");

        if (!file_exists($this->jobXmlName)){
            $status = $this->generateXml(); 
            if (!$status) {
                error_log('Cannot create jobs.xml: Fatal Error',1,email); 
                $this->success = false;
            }
        }
        else {
            $status = $this->jobXml->load($this->jobXmlName); 
            if (!$status) {
                $status = $this->generateXml(); 
                $msg = $status ? 'Cannot load jobs.xml. New jobs.xml was created' : 
                    'Cannot load jobs.xml. Cannot create jobs.xml: Fatal Error';
                error_log($msg,1,email);
                if (!$status) $this->success = false;
            }
        }           
    } 

/*
*  Create jobs.xml if it doesn't exist or in case of loading problems
*/
    private function generateXml() {

        $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);

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

        return $status;
    }


    //TODO procedure to estimate STATUS
    protected function getStatus($pid) {

        return '20';
    }

/*
*
*/      
    protected function updateJobStatus($pid) {

        if ($this->isFinished($pid)) return 'done';
        $status = $this -> getStatus($pid);
        return $status;
    }
      
/*
*
*/
    protected function saveResource($obj, $name) {   

        $file = fopen(USERJOBDIR.$name, 'w');
        //TODO change id and name to job ones?	
        fwrite($file, json_encode($obj));
        fclose($file);       
    }

    // $obj => XML tag
    protected function deleteResource($obj) {   
        // Delete request	
        $request_name = $obj->getAttribute('xml:id');
        if (file_exists(USERJOBDIR.$request_name)) unlink(USERJOBDIR.$request_name);

        // Delete result Time Table
        if ($obj->getAttribute('jobType') == 'condition') {
            foreach (glob(USERTTDIR.'*'.$obj->getAttribute('name').'*.xml') as $filename)  
                                                            unlink($filename);
        }

        // Delete result PLOT : PDF and PS 
        if ($obj->getAttribute('jobType') == 'request' || $obj->getAttribute('jobType') == 'download') {
            $resultDir = USERWORKINGDIR.$obj->getAttribute('rawname').'_/';
            if (is_dir($resultDir)) {
                foreach (glob($resultDir.'*') as $filename)  
                                                    unlink($filename);
                rmdir($resultDir);
            }
        }
    }

    // $obj => XML tag   
    protected function updateResultName($obj) {  

        switch ($obj->getAttribute('jobType')) 
        {
            case 'condition':

                //TODO temporary dir? 
                    $old_tt =  USERTTDIR.$obj->getAttribute('rawname').'.xml';
                    $new_tt =  USERTTDIR.$obj->getAttribute('name').'.xml';
                    if (file_exists($old_tt)) rename($old_tt, $new_tt); 

                    $old_tt =  USERTTDIR.'Gaps_'.$obj->getAttribute('rawname').'.xml';	    
                    $new_tt =  USERTTDIR.'Gaps_'.$obj->getAttribute('name').'.xml';
                    if (file_exists($old_tt)) rename($old_tt, $new_tt);  
                break;

            case 'request' :

                    $resultDir = USERWORKINGDIR.$obj->getAttribute('rawname').'_/';	 	
                    $reqFile = USERJOBDIR.$obj->getAttribute('xml:id');
                    if (file_exists($reqFile)) {
                        $object = json_decode(file_get_contents($reqFile));
                        $format = strtolower($object->format);
                        
                        $newName = $obj->getAttribute('name');
                        switch ($format) {
                                case 'ps':   
                                        if (file_exists($resultDir.'idl.ps')){
                                            rename($resultDir.'idl.ps',$resultDir.$newName.'.ps');
                                            exec('gzip '.$resultDir.$newName.'.ps'); 
                                        }
                                        //PS for time table
                                        else {
                                            $filenames = glob($resultDir."*.ps.gz");
                                            if ($filenames && count($filenames) == 1) {
                                                    rename($filenames[0],$resultDir.$newName.'.ps.gz');
                                            }
                                        }
                                    break;
                                case 'pdf': 
                                        if (file_exists($resultDir.'idl.ps')){
                                            exec('ps2pdf '.$resultDir.'idl.ps '.$resultDir.'idl.pdf');
                                            unlink($resultDir.'idl.ps');
                                            rename($resultDir.'idl.pdf',$resultDir.$newName.'.pdf');
                                        }
                                        // PDF for time tables
                                        if (file_exists($resultDir.'idl.pdf')){
                                            rename($resultDir.'idl.pdf',$resultDir.$newName.'.pdf');
                                        }
                                    break;
                                    case 'png' :
                                            rename($resultDir.$obj->getAttribute('rawname').'.png', $resultDir.$newName.'.png');
                                        break;
                                    default :
                                    } 			     		  
                                }  
                            break;

            case 'download' : 

                    $rawId = $obj->getAttribute('rawname');
                    $name = $obj->getAttribute('name');

                    $reqMgr = new RequestMgr('download');
                    
                    $resultDir = USERWORKINGDIR.$rawId.'_/';
                    
                    $res = $reqMgr->initResDir($rawId);

                    if (!$res['success'])
                                return $res;
                    $resultDir = $res['resdir'];
                    
                    //get options
                    $opts = $reqMgr->getPrintOptions($rawId);

                    // Start PostProcessing - lock dir from Client Requests
                    if (!file_exists($resultDir."/POSTPROCESSING"))
                                       touch($resultDir."/POSTPROCESSING");
                    // Is still PostProcessing - return 
                    else return true;

                    $reqMgr->postProcessing($rawId,$name);
                    if (file_exists($resultDir."/POSTPROCESSING"))
                                        unlink($resultDir."/POSTPROCESSING");
                break;

            default :

        }  
        return false;  
    } 

/*****************************************************************
*                           PUBLIC FUNCTIONS
*****************************************************************/
  
    public function isFinished($PID){

        exec("ps $PID", $ProcessState);	
        if (count($ProcessState) >= 2 ) return false;
                
        return true;
    }

/*
*
*/  
    public function addJob($obj, $pid, $tmpname, $newname) {

 /* [id] => cond_2
    [name] => longtest
    [sampling] => 60
    [gap] => 5
    [expression] => imf(0) > 0
    [startDate] => 2000-09-27T13:50:51
    [durationDay] => 0665
    [durationHour] => 12
    [durationMin] => 00
    [durationSec] => 00
    [leaf] => 1
    [nodeType] => condition
 */

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

            $newJob->setAttribute('jobType', $obj->nodeType);
            $newJob->setAttribute('pid', $pid); 
            $newJob->setAttribute('start', date('d-m-Y H:i:s'));
            if ($pid) {
                $newJob->setAttribute('status', $this->getStatus($pid));
                $newJob->setAttribute('name', 'job_'.$pid);
            }
            else {
                $newJob->setAttribute('status', 'done');
                $newJob->setAttribute('name', $newname);
            }

            $newJob->setAttribute('rawname', $tmpname);

            switch ($obj->nodeType) 
            {         
                case 'condition':            	
                    $newJob->setAttribute('info', $obj->expression);
                    break;
                case 'download' :
                    $info = '';
                    foreach ($obj->list as $param)
                    {
                                if ($obj->downloadSrc == '2') //fits image
                                                        $info = $info.' '.$param->url;
                                                        else
                                                        $info = $info.' '.$param; //data
                    }
                                    $newJob->setAttribute('info', $info);
                    break;	
                case 'request'	 :	  
                    $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;
                                            }
                                    }
                    $newJob->setAttribute('info', $info);
                    break;
                default: 	
            }

            if ($pid) {
                $newJob->setAttribute('xml:id',$this->jobPrefix[$obj->nodeType].$pid);
                $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 {
                $newJob->setAttribute('xml:id', $tmpname);
                $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]);
                    $jobsInProgress = $this->jobXml->getElementsByTagName('jobsFinished')->item(0);
                    $jobsInProgress->appendChild($rootJobNode);
                }
            }

            //TODO if status  'error'...                    
            $rootJobNode->appendChild($newJob);	                 	 
            $ok = $this->jobXml->save($this->jobXmlName);

            if ($pid) 
                    $this->saveResource($obj, $newJob->getAttribute('xml:id'));

        return  $newJob->getAttribute('xml:id');

    }
 
/*
*  object : id, leaf, nodeType
*/
    public function deleteObject($obj) {
           
        $job = $this->jobXml->getElementById($obj->id);

        if (!$job) return array('id' => $obj->id);

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

        // Check if it is temporary result 
        if ($pid == '0') {

            $resultDir = USERWORKINGDIR.$obj->id.'_';
            if (is_dir($resultDir)) {
                foreach (glob($resultDir.'/*') as $filename)  
                                        unlink($filename);
                rmdir($resultDir);
            }
            //TT temporary
            foreach (glob(USERTTDIR.'*'.$obj->id.'*.xml') as $filename)  
                                                unlink($filename);

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

            return array('id' => $obj->id);             
         }
                
        if (!$this->isFinished($pid)) { 
            $cmd = 'kill -9 '.$pid;
            exec($cmd);
        }

        $this->deleteResource($job);

        $job->parentNode->removeChild($job);
        $this->jobXml->save($this->jobXmlName);
            
        return array('id' => $obj->id);
    }

/*
*
*/
    public function getObject($id) {
        
            $job = $this->jobXml->getElementById($id);
            $format = 'unknown';
            $compression = 'unknown';
            if($job) {
                $rawname = $job->getAttribute('rawname');
                if ($job->getAttribute('jobType') ==  'request') {
                    if (file_exists(USERJOBDIR.$id)) {
                        $obj = json_decode(file_get_contents(USERJOBDIR.$id));
                        $format = strtolower($obj->format);
                    };
                }
                if ($job->getAttribute('jobType') ==  'download') {
                    if (file_exists(USERJOBDIR.$id)) {
                        $obj = json_decode(file_get_contents(USERJOBDIR.$id));
                        $compression = strtolower($obj->compression);
                    };
                }
            }
            return array('id' => $id, 'name' => $rawname, 'format' => $format,  'compression' => $compression);
    }

/*
*      Real Update
*/
    public function getCurrentJobsStatus() {

        $jobs = $this->jobXml->getElementsByTagName('job'); 
      
        foreach ($jobs as $job) {
            $pid = $job->getAttribute('pid');
            $status = $job->getAttribute('status');
 
            // Already in Result => do not process
            if ($status == 'error' ||  $status == 'done') {
                $stop =  $job->getAttribute('stop');
                $jobType = $job->getAttribute('jobType');
                $status = 'old';
             }
             else {
                $status =  $this -> updateJobStatus($pid);
                $stop = 'unknown';  
                $job->setAttribute('status',$status); 
                $jobType = $job->getAttribute('jobType');

                if ($status ==  'error' || $status == 'done') {
                    
                    $stop =  date('d-m-Y H:i:s');
                    $job->setAttribute('stop', $stop);
                    // new array !!! otherwise $jobs array decreased
                    if ($jobType != null) {
                        $jobsToMove[] = $job;                       
                    }
                    if ($status == 'done') {
                            // Could take long time - special truck to avoid interference of TaskMgr requests
                            // check if ResDir is locked
                            $postProccesing = $this->updateResultName($job);
                            if ($postProccesing) {  
                                // is already  PostProcessing -> return something not-used
                                return  array('id' => 'postprocessing'); 
                            }			   
                        }
                    }
            }
                                            
            $allJobs[] = array('name' => $job->getAttribute('name'), 
                                'id' => $job->getAttribute('xml:id'),
                                'nodeType' => 'bkgWorks', 'leaf' => true,
                                'pid' => $pid, 'status' => $status,
                                'start' =>  $job->getAttribute('start'),
                                'stop' => $stop, 'jobType' =>  $jobType);
 
        }
 
        foreach ($jobsToMove as $job) 
                    $this->jobXml->getElementById($this->resRootNode[$job->getAttribute('jobType')])->appendChild($job);

        $this->jobXml->save($this->jobXmlName);
 
     return  $allJobs;
    }

/*
*    Delete all temporary products
*/
    public function deleteTmp() 
    {
            // Clean up temporary directory
            if (is_dir(USERTEMPDIR)) 
                    foreach ( glob(USERTEMPDIR.'*') as $filename ) unlink($filename);

            // Clean up immediate jobs	    
            $xp = new domxpath($this->jobXml);
            $tmps = $xp->query("//job[@pid='0']");
            if ($tmps->length == 0) return;

            foreach ($tmps as $tmp) {
                    $this->deleteResource($tmp);
                    $tmp->parentNode->removeChild($tmp);
            }

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

        return;
    }

}
?>