<?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.
 *
 *
 * This file handles the connection and actions for general reserve table
 *
 *
 *
 *
 *
 **/

include("GnutecaWebServices.class.php");

class gnuteca3WebServicesReservesWSDL
{

    private $webService;
    private $MIOLO;
    
    private $busReserve;
    private $busMaterial;
    private $busSearchFormat;
    private $busMaterialControl;
    private $busOperationReserve;
    private $busExemplaryControl;

    public function __construct()
    {
        $this->webService = new GWebServices();
        $this->MIOLO = MIOLO::getInstance();

        $this->busReserve = $this->MIOLO->getBusiness('gnuteca3', 'BusReserve');
        $this->busMaterial = $this->MIOLO->getBusiness('gnuteca3', 'BusMaterial');
        $this->busSearchFormat = $this->MIOLO->getBusiness('gnuteca3', 'BusSearchFormat');
        $this->busMaterialControl = $this->MIOLO->getBusiness('gnuteca3', 'BusMaterialControl');
        $this->busOperationReserve = $this->MIOLO->getBusiness('gnuteca3', 'BusOperationReserve');
        $this->busExemplaryControl = $this->MIOLO->getBusiness('gnuteca3', 'BusExemplaryControl');
    }

    /**
     * Webservice para listar histórico de reservas de um usuário.
     * @param int $clientId
     * @param string $clientPassword
     * @param int $personId
     * @param int $libraryUnitId
     * @param string $beginRequestedDate
     * @param string $endRequestedDate
     * @param string $beginLimitDate
     * @param string $endLimitDate
     * @param int $reserveStatusId
     * @return bool|string
     */
    public function listReserves(
        $clientId,
        $clientPassword,
        $personId,
        $beginRequestedDate = null,
        $reserveStatusId = null,
        $pageNumber = 1
    ) {
        $retorno = new stdClass();
        // Define acesso ao método e define a classe do WebService
        $this->webService->__setClient($clientId, $clientPassword);
        $this->webService->setClassName(get_class($this));
        
        // Verifica se o usuário possui acesso ao WebService
        if (!$this->webService->__checkMethod("listReserves", false))
        {
            $retorno->message = $this->webService->__getErrorStr();
            return json_encode($retorno);
        }

        // Se as variáveis forem nulas, não serão consideradas na consulta pelo BusReserve com exceção do personId
        $this->busReserve->personId = $personId;
        $this->busReserve->personIdS = $personId;
        $this->busReserve->requestedDateS = $beginRequestedDate;
        $this->busReserve->reserveStatusIdS = $reserveStatusId;
        
        // Particiona o retorno da consulta com base no número de registros por página
        $userReserveData = array_chunk($this->busReserve->searchReserve('R.requestedDate DESC', true, false), LISTING_NREGS);

        if (empty($userReserveData) && (isset($beginRequestedDate) || isset($reserveStatusId))) 
        {
            $retorno->message = 'Nenhuma reserva encontrada.';
            return json_encode($retorno);
        }

        $reserveData = [];

        // Obtém apenas os registros de uma determinada página
        foreach ($userReserveData[$pageNumber - 1] as $reserveMaterial) 
        {
            $itemNumber = $this->busExemplaryControl->getItemNumberByReserve($reserveMaterial->reserveId);
            $controlNumber = $this->busExemplaryControl->getControlNumber($itemNumber);

            $reserveEntry = [
                'reserveId' => $reserveMaterial->reserveId,
                'requestedDate' => $reserveMaterial->requestedDate ? explode(' ', $reserveMaterial->requestedDate)[0] : '-',
                'limitDate' => $reserveMaterial->limitDate ? explode(' ', $reserveMaterial->limitDate)[0] : '-',
                'reserveStatus' => $reserveMaterial->reserveStatus,
                'reserveType' => $reserveMaterial->reserveType,
                'searchFormat'=> $controlNumber ? $this->busSearchFormat->getFormatedString($controlNumber, 5) : 'Exemplar excluído',
                'controlNumber' => $controlNumber,
            ];

            $reserveData[] = $reserveEntry;
        }
        
        // Dados de retorno
        $retorno->reserveHistory = $reserveData;
        $retorno->numberOfPages = count($userReserveData);

        return json_encode($retorno);
    }

    /**
     * Webservice para listar os status de reserva.
     * @param int $clientId
     * @param string $clientPassword
     * @return string JSON com os status de reserva
     */
    public function listReserveStatus($clientId, $clientPassword)
    {
        $retorno = new stdClass();
        $retorno->reserveStatus = [];

        // Define acesso ao método e define a classe do WebService
        $this->webService->__setClient($clientId, $clientPassword);
        $this->webService->setClassName(get_class($this));

        // Verifica se o usuário possui acesso ao WebService
        if (!$this->webService->__checkMethod("listReserveStatus", false))
        {
            $retorno->message = $this->webService->__getErrorStr();
            return json_encode($retorno);
        }

        // Obtém os status de reserva
        $busReserveStatus = $this->MIOLO->getBusiness('gnuteca3', 'BusReserveStatus');
        $reserveStatusList = $busReserveStatus->listReserveStatus();

        $retorno->reserveStatus[] = ['statusId' =>'', 'description' => 'Todos estados'];
        
        foreach ($reserveStatusList as $status) {
            $retorno->reserveStatus[] = [
                'statusId' => $status[0],
                'description' => $status[1],
            ];
        }

        return json_encode($retorno);
    }

    /**
     * Webservice para listar reservas abertas de um usuário.
     *
     * @param int $clientId Codigo do cliente SOAP.
     * @param string $clientPassword Senha do cliente SOAP.
     * @param int $personId Codigo do usuario.
     *
     * @return string
     */
    public function listOpenReserves($clientId, $clientPassword, $personId)
    {
        $retorno = new stdClass();

        // Define acesso ao método e define a classe do WebService
        $this->webService->__setClient($clientId, $clientPassword);
        $this->webService->setClassName(get_class($this));

        // Verifica se o usuário possui acesso ao WebService
        if (!$this->webService->__checkMethod("listOpenReserves", false))
        {
            $retorno->message = $this->webService->__getErrorStr();
            return json_encode($retorno);
        }
        
        $busMyReserves = $this->MIOLO->getBusiness('gnuteca3', 'BusMyReserves');
        $myReserves = $busMyReserves->getMyReserves($personId);
        
        $columns = [
            'reserveId',
            'searchFormat',
            '', // Por algum motivo não funciona se retirar este campo
            'requestedDate',
            'limitDate',
            'reserveStatus',
            'positionQueue',
            'returnForecastDate',
            'libraryName',
        ];
        
        // Convertendo para array associativo
        $dadosFormatados = [];

        foreach ($myReserves as $item) {
            $assoc = array_combine($columns, $item);
            
            // Remove campos com valor ''
            $assoc = array_filter($assoc, function($value) {
                return $value !== '';
            });
            
            // Busca dados adicionais
            $itemNumber = $this->busExemplaryControl->getItemNumberByReserve($assoc['reserveId']);
            $controlNumber = $this->busExemplaryControl->getExemplaryControl($itemNumber)->controlNumber;
            
            $assoc['searchFormat'] = $controlNumber ? $this->busSearchFormat->getFormatedString($controlNumber, 5) : 'Exemplar excluído';
            $assoc['title'] = $this->busMaterial->getMaterialTitleByItemNumber($itemNumber);
            $assoc['controlNumber'] = $controlNumber;
            
            // Substitui null por '-'
            $assoc = array_map(function($value) {
                return $value === null ? '-' : $value;
            }, $assoc);
            
            $dadosFormatados[] = $assoc;
        }
        
        return json_encode($dadosFormatados);
    }

    /**
     * Webservice para cancelar reserva.
     *
     * @param int $clientId Codigo do cliente SOAP.
     * @param string $clientPassword Senha do cliente SOAP.
     * @param int $personId Codigo do usuario.
     * @param int $reserveId Codigo da reserva.
     * @param string $validate Quando "t" verifica somente se reserva existe e valor da multa.
     *
     * @return string
     */
    public function cancelReserve($clientId, $clientPassword, $personId, $reserveId, $validate)
    {
        $retorno = new stdClass();
        $retorno->return = FALSE;
        $retorno->message = '';
        $retorno->fine = '0';

        // Checa acesso ao método.
        $this->webService->__setClient($clientId, $clientPassword);

        // Define classe Werbservice.
        $this->webService->setClassName(get_class($this));

        // Verifica se usuário possuí acesso ao WebService.
        if ( !$this->webService->__checkMethod("cancelReserve", false) )
        {
            $retorno->message = $this->webService->__getErrorStr();
        }
        else
        {

            $reserve = $this->busReserve->getReserveOpenOfPerson($reserveId, $personId);
            

            if ( $reserve )
            {
                $retorno->fine = $this->busReserve->calculatesFine($reserveId);

                if ( $validate == 'f' )
                {
                    $ok = $this->busOperationReserve->cancelReserve($reserveId);

                    if ( $ok )
                    {
                        $retorno->return = true;
                        $retorno->message = 'Reserva cancelada!';
                    }
                    else
                    {
                        $retorno->message = $this->busOperationReserve->messagesToString($retorno->return ? GMessages::TYPE_INFORMATION : GMessages::TYPE_ERROR, ' ', true);
                    }
                }
            }
            else
            {
                $retorno->message = 'Reserva não encontrada.';
            }

        }

        return json_encode($retorno);
    }

    /**
     * Webservice para reserva de obra.
     *
     * @param int $clientId Codigo do cliente SOAP.
     * @param string $clientPassword Senha do cliente SOAP.
     * @param int $personId Codigo do usuario.
     * @param int $controlNumber Numero de controle do material.
     * @param int $libraryUnitId Codigo da unidade de biblioteca.
     *
     * @return string
     */
    public function reserveMaterial($clientId, $clientPassword, $personId, $controlNumber, $libraryUnitId)
    {
        $retorno = new stdClass();
        $retorno->return = FALSE;
        $retorno->message = '';

         // Checa acesso ao método.
        $this->webService->__setClient($clientId, $clientPassword);

        // Define classe Werbservice.
        $this->webService->setClassName(get_class($this));

        // Verifica se usuário possuí acesso ao WebService.
        if ( !$this->webService->__checkMethod("reserveMaterial", false) )
        {
            $retorno->message = $this->webService->__getErrorStr();
        }
        else
        {
            $this->MIOLO->uses('classes/controls/GMaterialDetail.class.php', 'gnuteca3');

            // Verifica se obra é um artigo.
            $isArticle = $this->busMaterialControl->isColletionArticle($controlNumber);

            // Caso seja um artigo de fascículo, obtém o número de controle do fascículo.
            if ( $isArticle )
            {
                $controlNumber = $this->busMaterialControl->getControlNumberFather($controlNumber);
            }

            if ( $this->busExemplaryControl->verifyExemplaryInInitialStateOfMaterial($controlNumber, $libraryUnitId) )
            {
                $this->busOperationReserve->setReserveType(ID_RESERVETYPE_WEB_ANSWERED);
            }
            else
            {
                // Define o tipo de reserva.
                $this->busOperationReserve->setReserveType(ID_RESERVETYPE_WEB);
            }

            // Define a unidade de biblioteca.
            $this->busOperationReserve->setLibraryUnit($libraryUnitId);

            // Define o usuário.
            $personOk = $this->busOperationReserve->setPerson($personId);

            $exemplarys = $this->busExemplaryControl->getExemplaryOfMaterial($controlNumber, $libraryUnitId, true, null, false, true);
            $gReserveWeb = new GReserveWeb();
            $exemplarys = $gReserveWeb->getMeetReserveExemplaries($exemplarys);

            if ( !$exemplarys )
            {
                $this->busOperationReserve->addError(_M('Não é possível reservar materiais neste estado.', 'gnuteca3'));
            }

            if ( $personOk && $exemplarys )
            {
                // Adiciona material.
                $add = $this->busOperationReserve->addMaterial($controlNumber, null, true);

                if ( $add )
                {
                    $ok = $this->busOperationReserve->finalize();
                    $retorno->return = TRUE;
                }
            }

            $retorno->message = $this->busOperationReserve->messagesToString($retorno->return ? GMessages::TYPE_INFORMATION : GMessages::TYPE_ERROR, ' ', true);
        }

        return json_encode($retorno);
    }

    /**
     * Webservice para reserva de exemplares.
     *
     * @param int $clientId Codigo do cliente SOAP.
     * @param string $clientPassword Senha do cliente SOAP.
     * @param int $personId Codigo do usuario.
     * @param array $exemplars Lista de exemplares.
     * @param int $libraryUnitId Codigo da unidade de biblioteca.
     *
     * @return string
     */
    public function reserveExemplaries($clientId, $clientPassword, $personId, $exemplars, $libraryUnitId)
    {
        $retorno = new stdClass();
        $retorno->return = FALSE;
        $retorno->message = '';
        
        // Checa acesso ao método.
        $this->webService->__setClient($clientId, $clientPassword);

        // Define classe Werbservice.
        $this->webService->setClassName(get_class($this));

        // Verifica se usuário possuí acesso ao WebService.
        if ( !$this->webService->__checkMethod("reserveExemplaries", false) )
        {
            $retorno->message = $this->webService->__getErrorStr();
        }
        else
        {
            $this->MIOLO->uses('classes/controls/GMaterialDetail.class.php', 'gnuteca3');

            // Define o tipo de reserva.
            if ( $this->busExemplaryControl->verifyExemplaryInInitialStateOfExemplar($exemplars) )
            {
                $this->busOperationReserve->setReserveType(ID_RESERVETYPE_WEB_ANSWERED);
            }
            else
            {
                $this->busOperationReserve->setReserveType(ID_RESERVETYPE_WEB);
            }
            
            // Define a unidade de biblioteca.
            $this->busOperationReserve->setLibraryUnit($libraryUnitId);
            
            // Define o usuário.
            $personOk = $this->busOperationReserve->setPerson($personId);
            if ( $personOk )
            {
                if ( is_array($exemplars) )
                {
                    foreach ( $exemplars as $itemNumber )
                    {
                        $this->busOperationReserve->addItemNumber($itemNumber, true);
                    }
                    
                    $ok = $this->busOperationReserve->finalize();
                    // return $ok;
                    if ( $ok )
                    {
                        $retorno->return = true;
                    }
                }
            }
            
            $retorno->message = trim($this->busOperationReserve->messagesToString($retorno->return ? GMessages::TYPE_INFORMATION : GMessages::TYPE_ERROR, ' ', true));
        }

        return json_encode($retorno);
    }

    /**
     * Obtém os exemplares reserváveis de uma obra.
     * @param int $clientId Código do cliente SOAP.
     * @param string $clientPassword Senha do cliente SOAP.
     * @param int $controlNumber Número de controle do material.
     * @param int|null $libraryUnitId Código da unidade de biblioteca (opcional).
     * @return string JSON com os exemplares agrupados por unidade de biblioteca, tomo e volume.
     */
    public function getReservableExemplaries($clientId, $clientPassword, $controlNumber, $libraryUnitId = null)
    {
        $this->MIOLO->getClass('gnuteca3', 'controls/GMaterialDetail');
        $retorno = new stdClass();

        // Define acesso ao método e define a classe do WebService
        $this->webService->__setClient($clientId, $clientPassword);
        $this->webService->setClassName(get_class($this));

        // Verifica se o usuário possui acesso ao WebService
        if (!$this->webService->__checkMethod("getReservableExemplaries", false))
        {
            $retorno->message = $this->webService->__getErrorStr();
            return json_encode($retorno);
        }

        // Obtém os exemplares e os seus estados
        $exemplaries = $this->busExemplaryControl->getExemplaryOfMaterial($controlNumber, $libraryUnitId, true, true, true);

        $grouped = [];
        foreach ($exemplaries as $exemplar) {
            if (
                $exemplar->exemplaryStatus->executeReserve === 't' &&
                $exemplar->exemplaryStatus->meetReserve === 't'
            ) {
                $libraryUnitId = $exemplar->libraryUnitId;
                $tomo = $exemplar->tomo ?? '-';
                $volume = $exemplar->volume ?? '-';
                
                // Cria uma chave única para o agrupamento
                $groupKey = $libraryUnitId . '|' . $tomo . '|' . $volume;

                if (!isset($grouped[$groupKey])) {
                    $grouped[$groupKey] = [
                        'searchFormat' => $this->busSearchFormat->getFormatedString($exemplar->controlNumber, 5),
                        'materialType' => $exemplar->materialTypeDescription,
                        'materialPhysicalType' => $exemplar->materialPhysicalTypeDescription,
                        'tomo' => $tomo,
                        'volume' => $volume !== '-' ? ('v. ' . $volume) : '-',
                        // Disponíveis para a renovação
                        'libraryName' => $exemplar->libraryName,
                        'libraryUnitId' => $libraryUnitId,
                        'itemNumbers' => [],
                    ];
                }

                $grouped[$groupKey]['itemNumbers'][] = $exemplar->itemNumber;
            }
        }

        return json_encode($grouped);
    }
}