<?php
/**
 * Copyright 2005-2017 de Solis Soluções Livres Ltda.
 *
 * Este arquivo é parte do programa Gnuteca.
 *
 * O Gnuteca é um software de propriedade da SOLIS, sendo desenvolvido
 * e mantido exclusivamente por esta empresa.
 *
 * A licença de uso está disponível mediante aquisição exclusiva junto à
 * SOLIS. A licença é concedida sem caráter de exclusividade ao licenciado.
 * Os direitos de uso são perpétuos.
 *
 * Embora os códigos fontes sejam fornecidos, o software é de propriedade
 * da SOLIS, não sendo permitido ao adquirente da licença a sua revenda,
 * empréstimo ou cessão (onerosa ou não) à terceiros. Também não é permitido,
 * a qualquer título e tempo, promover no software qualquer tipo de alienação,
 * reprodução, distribuição, divulgação, registro, licenciamento, transferência
 * ou qualquer outro ato que prejudique ou comprometa os direitos de propriedade
 * de software, o nome e a imagem da sua proprietária e do próprio software,
 * além de configurar concorrência à SOLIS.
 *
 * O licenciado, com o acesso ao código fonte do software, terá o direito de
 * promover mudanças no respectivo código. No entanto, nas situações em que ele
 * contar com o suporte oficial prestado pela SOLIS, não poderá promover mudanças
 * no código fonte, sob pena de perda do referido suporte.
 *
 * Para conhecer em detalhes o Termo de Licenciamento do Software Gnuteca
 * leia o arquivo “LICENCA.txt” disponível junto ao código deste software.
 *
 *
 *
 *
 *
 *
 **/
interface GBackgroundTaskTemplate
{
    public function execute();
}

/*
 * Exige que deve ser implementado a função execute
 *
 */
class GBackgroundTask
{
    public $args;
    public $MIOLO;
    public $business;
    public $label;
    public $message;
    const STATUS_EXECUTION   = 1;
    const STATUS_SUCESS      = 2;
    const STATUS_ERROR       = 3;
    const STATUS_REEXECUTION = 4;
    const STATUS_RESUCESS    = 5;
    const STATUS_RERROR      = 6;

    public function __construct($args)
    {
        $this->args     = $args;
        $this->MIOLO    = MIOLO::getInstance();
        $this->business = $this->MIOLO->getBusiness('gnuteca3', 'BusBackgroundTaskLog');
        $this->business = new BusinessGnuteca3BusBackgroundTaskLog(); //para funcionar o autocomplete
    }

    public function setLabel($label)
    {
        $this->label = $label;
    }

    public function getLabel()
    {
        return $this->label ? $this->label : $this->args->task;
    }

    public function setMessage($message)
    {
        $this->message = $message;
    }

    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Executa um tarefa em segundo plano
     */
    public static function executeTask($task, $args)
    {
        $MIOLO      = MIOLO::getInstance();
        $business   = $MIOLO->getBusiness('gnuteca3', 'BusBackgroundTaskLog');
        $business   = new BusinessGnuteca3BusBackgroundTaskLog(); //para funcionar o autocomplete
        $taskObj    = new $task($args);

        $data = new stdClass();
        $data->beginDate        = GDate::now()->getDate(GDate::MASK_TIMESTAMP_DB);
        $data->task             = $task;
        $data->label            = $taskObj->getLabel();
        $data->status           = GBackgroundTask::STATUS_EXECUTION;
        $data->operator         = GOperator::getOperatorId();
        $data->args             = serialize($args);
        $data->libraryUnitId    = $args->libraryUnitId;

        $business->setData($data);

        $business->insertBackgroundTaskLog();

        $execute = $taskObj->execute();

        $data->status = $execute ? GBackgroundTask::STATUS_SUCESS : GBackgroundTask::STATUS_ERROR;
        $data->endDate = GDate::now()->getDate(GDate::MASK_TIMESTAMP_DB);
        $data->message = $taskObj->getMessage();

        $business->setData($data);

        $business->updateBackgroundTaskLog();

        $date = Gdate::now();
        $time = $date->getDate(GDate::MASK_TIMESTAMP_USER);

        return "$time - $task - '{$data->status} - {$taskObj->getMessage()}' \n";
    }


    /**
     * Re-executauma tarefa em segundo plano, passando o código do log
     * 
     * @param int $backgroundTaskLogId códigodo log de tarefa em segundo plano
     * @return boolean
     */
    public static function reExecuteTask( $backgroundTaskLogId )
    {
        if ( !$backgroundTaskLogId )
        {
            throw new Exception( _M('É necessário informar o código de registro da terafa!', 'gnuteca3') );
        }

        $MIOLO = MIOLO::getInstance();
        $business = $MIOLO->getBusiness('gnuteca3','BusBackgroundTaskLog');
        $business = new BusinessGnuteca3BusBackgroundTaskLog(); //para funcionar o autocomplete
        $data = $business->getBackgroundTaskLog( $backgroundTaskLogId );
        $data->status = GBackgroundTask::STATUS_REEXECUTION;
        $data->operator = GOperator::getOperatorId();
        $data->libraryUnitId = GOperator::getLibraryUnitLogged();

        //separa e inclui a tarefa
        $task = $data->task;
        $MIOLO->uses("classes/backgroundTasks/$task.class.php",'gnuteca3');

        $taskObj = new $task( unserialize( $data->args ) );
        //atualiza dados
        $business->setData($data);
        $business->updateBackgroundTaskLog();

        $execute =  $taskObj->execute(); //executa

        $data->status = $execute ? GBackgroundTask::STATUS_RESUCESS : GBackgroundTask::STATUS_RERROR;
        $data->endDate = GDate::now()->getDate(GDate::MASK_TIMESTAMP_DB);
        $data->message = $taskObj->getMessage();

        $business->setData($data);
        $business->updateBackgroundTaskLog();

        //caso não tenha executado com sucesso, retorna error
        if ($data->status == GBackgroundTask::STATUS_RERROR )
        {
            throw new Exception( $data->message );
        }

        return $data->status;
    }

     /**
     * Executa uma tarefa em segundo plano. As tarefas em segundo plano devem estar na pasta backbagroundTasks.
     *
     * Será gerado um arquivo de log com o mesmo nome da tarefa.
     *
     * @param <string> $task passe somente o nome do script, sem a extensão .php
     * @param <stdClass> argumentos a serem passados para o outro lado
     * @param <boolean> se true, não roda se uma tarefa deste tipo já estiver rodando
     * @param <boolean> se true, somente roda em background, não executando caso o usuário tenha que esperar
     * @return <boolean> verdadeira se o script existir
     *
     */
    public static function executeBackgroundTask($task, $args, $runningOnce = false , $onlyInBackground = false, $informLibraryUnit = true )
    {
        $MIOLO      = MIOLO::getInstance();
        $fileName   =  "modules/gnuteca3/classes/backgroundTasks/$task.class.php";
        $script     =  "modules/gnuteca3/classes/backgroundTasks/getMioloConsole.php";
        $fullPath   = $MIOLO->getConf('home.miolo').'/';
        $logFile = $fullPath . '/var/log/backgroundTask.log';

        //se o script não existir retorna falso
        if ( !file_exists( $fullPath .$fileName ) )
        {
            return false;
        }

        $args->mioloPath = $MIOLO->getAbsolutePath();
        $args->task = $task;

        if ( $informLibraryUnit )
        {
            $args->libraryUnitId  = GOperator::getLibraryUnitLogged();
        }
        
        $args = serialize($args);

        //executa em segundo plano
        if ( GBackgroundTask::isPossible() )
        {
            //é possível que este código não funcione em servidores Windows
            exec( "php ../$script '$args' >> $logFile &"); //>> para adicionar ao log
            chmod( $logFile, 0777 );
        }
        //caso não seja possível executar em segundo plano faz a chamada da tarefa manualmente, forçando o usuário a esperar
        else if ( !$onlyInBackground ) 
        {
            $MIOLO->uses("classes/backgroundTasks/$task.class.php",'gnuteca3');

            return GBackgroundTask::executeTask($task, unserialize( $args ) ) ;
        }
        else
        {
            return false;
        }

        return true;
    }


    /**
     * Verifica se existe a possibilidade de executar tarefas em segundo plano.
     *
     * 1. Verifica se a função exec existe.
     * 2. Verifica se o php cli existe
     * 3. Verifica se não esta em Modo Seguro
     * 4. Verifica se a preferência de execução em segundo plano esta liberada.
     *
     * @return <boolean>
     *
     */
    public static function isPossible()
    {
        if (function_exists('exec'))
        {
            exec('php --version', $message); //executa o comando para obter a versão do php_cli
            if ( stripos( $message[1], 'The PHP Group') > 0  && !ini_get('safe_mode') && MUtil::getBooleanValue( EXECUTE_BACKGROUND_TASK ))
            {
                return true;
            }
        }

        return false;
    }
}
?>