<?php declare(strict_types=1);

/**
* @package   s9e\RegexpBuilder
* @copyright Copyright (c) 2016-2022 The s9e authors
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
*/
namespace s9e\RegexpBuilder\Passes;

use const false, true;
use function array_shift, array_unshift, count, end, is_array;

abstract class AbstractPass implements PassInterface
{
	/**
	* @var bool Whether the current set of strings is optional
	*/
	protected $isOptional;

	/**
	* {@inheritdoc}
	*/
	public function run(array $strings): array
	{
		$strings = $this->beforeRun($strings);
		if ($this->canRun($strings))
		{
			$strings = $this->runPass($strings);
		}
		$strings = $this->afterRun($strings);

		return $strings;
	}

	/**
	* Process the list of strings after the pass is run
	*
	* @param  array[] $strings
	* @return array[]
	*/
	protected function afterRun(array $strings): array
	{
		if ($this->isOptional && $strings[0] !== [])
		{
			array_unshift($strings, []);
		}

		return $strings;
	}

	/**
	* Prepare the list of strings before the pass is run
	*
	* @param  array[] $strings
	* @return array[]
	*/
	protected function beforeRun(array $strings): array
	{
		$this->isOptional = (isset($strings[0]) && $strings[0] === []);
		if ($this->isOptional)
		{
			array_shift($strings);
		}

		return $strings;
	}

	/**
	* Test whether this pass can be run on a given list of strings
	*
	* @param  array[] $strings
	* @return bool
	*/
	protected function canRun(array $strings): bool
	{
		return true;
	}

	/**
	* Run this pass on a list of strings
	*
	* @param  array[] $strings
	* @return array[]
	*/
	abstract protected function runPass(array $strings): array;

	/**
	* Test whether given string has an optional suffix
	*
	* @param  array $string
	* @return bool
	*/
	protected function hasOptionalSuffix(array $string): bool
	{
		$suffix = end($string);

		return (is_array($suffix) && $suffix[0] === []);
	}

	/**
	* Test whether given string contains a single alternation made of single values
	*
	* @param  array $string
	* @return bool
	*/
	protected function isCharacterClassString(array $string): bool
	{
		return ($this->isSingleAlternationString($string) && $this->isSingleCharStringList($string[0]));
	}

	/**
	* Test whether given string contains one single element that is an alternation
	*
	* @param  array $string
	* @return bool
	*/
	protected function isSingleAlternationString(array $string): bool
	{
		return (count($string) === 1 && is_array($string[0]));
	}

	/**
	* Test whether given string contains a single character value
	*
	* @param  array $string
	* @return bool
	*/
	protected function isSingleCharString(array $string): bool
	{
		return (count($string) === 1 && !is_array($string[0]));
	}

	/**
	* Test whether given list of strings contains nothing but single-char strings
	*
	* @param  array[] $strings
	* @return bool
	*/
	protected function isSingleCharStringList(array $strings): bool
	{
		foreach ($strings as $string)
		{
			if (!$this->isSingleCharString($string))
			{
				return false;
			}
		}

		return true;
	}
}