<?php
namespace Numenor\Html;

use Numenor\Php\ArrayWrapper;
use Numenor\Php\StringWrapper;

/**
 * Classe abstrata de controle para inclusão de assets em páginas HTML.
 *
 * Além de gerar o snippet de código adequado para a inclusão dos assets em uma página HTML, as subclasses desta classe
 * providenciam métodos para minificação e concatenação de assets de texto (como JS e CSS), bem como a geração de
 * sprites para imagens.
 *
 * @abstract
 * @author Darke M. Goulart <darkemg@users.noreply.github.com>
 * @package Numenor\Html
 */
abstract class Controle
{
	
	/**
	 * Define que o comportamento padrão da inclusão de assets será de ambiente de desenvolvimento.
	 * 
	 * @var string
	 */
	const COMPORTAMENTO_PADRAO_DEV = 'dev';
	/**
	 * Define que o comportamento padrão da inclusão de assets será de ambiente de homologação.
	 *
	 * @var string
	 */
	const COMPORTAMENTO_PADRAO_HOMOLOG = 'homolog';
	/**
	 * Define que o comportamento padrão da inclusão de assets será de ambiente de produção.
	 * 
	 * @var string
	 */
	const COMPORTAMENTO_PADRAO_PROD = 'prod';
	
	/**
	 * Instância do objeto de encapsulamento das operações de array.
	 * 
	 * @access protected
	 * @var \Numenor\Php\ArrayWrapper;
	 */
	protected $arrayWrapper;
	/**
	 * Instância do objeto de encapsulamento das operações de string.
	 * 
	 * @access protected
	 * @var \Numenor\Php\StringWrapper;
	 */
	protected $stringWrapper;
	/**
	 * Diretório onde os arquivos processados (minificados, concatenados, gzipados, etc.) serão salvos.
	 * 
	 * Este diretório DEVE ter o acesso público liberado (normalmente, algo como [DIR_ROOT]/public/assets/[TIPO]/), ou
	 * os arquivos não funcionarão ao serem incluídos como assets na página.
	 * 
	 * @access protected
	 * @var string
	 */
	protected $diretorioOutput;
	/**
	 * URL base de inclusão dos assets.
	 * 
	 * Este valor será utilizado para gerar os snippets de inclusão dos assets (tags [script], [link], [img], etc.), e
	 * normalmente reflete o diretório de output dos assets processados.
	 * 
	 * @access protected
	 * @var string
	 */
	protected $urlBase;
	/**
	 * Comportamento padrão do controlador de assets.
	 * 
	 * Por padrão, os arquivos de assets processados pelo controlador são tratados conforme suas configurações 
	 * (minificados e concatenados em um só arquivo, minificados separadamente, concatenados sem minificação, ou 
	 * adicionados sem nenhum processamento adicional). Esta configuração permite que este comportamento padrão seja
	 * sobrescrito de forma a facilitar o controle dos arquivos em determinados ambientes da aplicação:
	 * 
	 * - Ambiente de desenvolvimento: nesse tipo de ambiente, todos os arquivos são adicionados como se tivessem sido
	 * configurados sem processamento adicional, de modo a facilitar o debug desses arquivos.
	 * - Ambiente de homologação: neste tipo de ambiente, os arquivos são processados conforme suas configurações,
	 * porém o sistema sempre gera novamente e sobrescreve os arquivos concatenados/minificados. Dessa maneira, não é
	 * necessário limpar os arquivos gerados a cada atualização. A penalidade de desempenho incorrida normalmente não é
	 * problema para ambientes de homologação.
	 * - Padrão/Ambiente de produção: neste tipo de ambiente, os arquivos são processados de acordo com suas
	 * configurações e é feita a verificação da existência dos arquivos resultantes antes de efetuar o processamento.
	 * Caso um ou mais assets sejam alterados, é necessário apagar os arquivos resultantes manualmente para que eles
	 * sejam regenerados. 
	 * 
	 * @access protected
	 * @var string
	 */
	protected $comportamentoPadrao;
	
	/**
	 * Método construtor da classe.
	 * 
	 * @access public
	 * @param \Numenor\Php\ArrayWrapper $arrayWrapper Instância do objeto de encapsulamento das operações de array.
	 * @param \Numenor\Php\StringWrapper $stringWrapper Instância do objeto de encapsulamento das operações de string.
	 * @param string $diretorioOutput Diretório onde os arquivos processados serão salvos.
	 * @param string $urlBase URL base de inclusão dos assets.
	 */
	public function __construct(ArrayWrapper $arrayWrapper, StringWrapper $stringWrapper, string $diretorioOutput, string $urlBase)
	{
		$this->arrayWrapper = $arrayWrapper;
		$this->stringWrapper = $stringWrapper;
		$this->diretorioOutput = $diretorioOutput;
		$this->urlBase = $urlBase;
		$this->comportamentoPadrao = self::COMPORTAMENTO_PADRAO_PROD;
	}
	
	/**
	 * Gera a lista de arquivos de assets que devem ser minificados e concatenados.
	 *
	 * @access protected
	 * @param array $listaAssets Lista de assets analisados.
	 * @return array Lista de arquivos de assets que devem ser minificados e concatenados.
	 */
	protected function gerarListaConcatCompact(array $listaAssets) : array
	{
		$lista = [];
		foreach ($listaAssets as $asset) {
			if ($asset->isCompactavel() && $asset->isConcatenavel()) {
				$lista[] = (string) $asset;
			}
		}
		return $lista;
	}
	
	/**
	 * Gera a lista de arquivos de assets que devem ser apenas concatenados, sem minificação.
	 *
	 * @access protected
	 * @param array $listaAssets Lista de assets analisados.
	 * @return array Lista de arquivos de assets que devem ser concatenados.
	 */
	protected function gerarListaConcat(array $listaAssets) : array
	{
		$lista = [];
		foreach ($listaAssets as $asset) {
			if ($asset->isConcatenavel() && !$asset->isCompactavel()) {
				$lista[] = (string) $asset;
			}
		}
		return $lista;
	}
	
	/**
	 * Gera a lista de arquivos de assets que devem ser apenas minificados, sem concatenação.
	 *
	 * @access protected
	 * @param array $listaAssets Lista de assets analisados.
	 * @return array Lista de arquivos de assets que devem ser minificados.
	 */
	protected function gerarListaCompact(array $listaAssets) : array
	{
		$lista = [];
		foreach ($listaAssets as $asset) {
			if ($asset->isCompactavel() && !$asset->isConcatenavel()) {
				$lista[] = (string) $asset;
			}
		}
		return $lista;
	}
	
	/**
	 * Gera a lista de arquivos de assets que devem ser incluídos sem nenhum processamento adicional.
	 *
	 * @access protected
	 * @param array $listaAssets Lista de assets analisados.
	 * @return array Lista de arquivos de assets que devem ser incluídos sem nenhum processamento adicional.
	 */
	protected function gerarListaNormal(array $listaAssets) : array
	{
		$lista = [];
		foreach ($listaAssets as $asset) {
			// Inclui os assets marcados como não concatenáveis, não compactáveis, e que não são remotos.
			$traits = class_uses($asset);
			try {
				$isRemoto = $this->arrayWrapper->encontrarItem($traits, Remoto::class);
			} catch (\Throwable $e) {
				$isRemoto = false;
			}
			if (!$isRemoto && !$asset->isCompactavel() && !$asset->isConcatenavel()) {
				$lista[] = (string) $asset;
			}
		}
		return $lista;
	}
	
	/**
	 * Gera um nome único para uma lista de assets, criando um hash a partir da concatenação dos nomes individuais.
	 * 
	 * @access protected
	 * @param array $listaAssets Lista de nomes dos assets.
	 * @return string Hash gerado a partir dos nomes dos assets listados.
	 */
	protected function gerarNome(array $listaAssets) : string
	{
		return hash('sha1', $this->stringWrapper->unir('', $listaAssets));
	}
	
	/**
	 * Método setter do comportamento padrão do controlador de assets. 
	 * 
	 * @access public
	 * @param string $comportamentoPadrao Valor do comportamento padrão do controlador.
	 * @return \Numenor\Html\Controle Instância do próprio objeto para encadeamento.
	 */
	public function setComportamentoPadrao(string $comportamentoPadrao) : self
	{
		$this->comportamentoPadrao = $comportamentoPadrao;
		return $this;
	}
	
	/**
	 * Gera o snippet do código de inclusão dos assets adicionados à página.
	 *
	 * @access public
	 * @return string O snippet de inclusão dos arquivos processados.
	 */
	public function gerarCodigoInclusao() : string
	{
		$this->minificar();
		return $this->stringWrapper->unir('', $this->listaArquivosIncluir);
	}
}

