<?php

/**
* @package   s9e\TextFormatter
* @copyright Copyright (c) 2010-2021 The s9e authors
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
*/
namespace s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors;

class SingleByteStringFunctions extends AbstractConvertor
{
	/**
	* {@inheritdoc}
	*/
	public function getMatchers(): array
	{
		$groups = 'Boolean:BooleanFunction:';

		return [
			$groups . 'Contains'      => 'contains \\( ((?&String)) , ((?&String)) \\)',
			$groups . 'EndsWith'      => 'ends-with \\( ((?&String)) , ((?&String)) \\)',
			$groups . 'NotContains'   => 'not \\( contains \\( ((?&String)) , ((?&String)) \\) \\)',
			$groups . 'NotEndsWith'   => 'not \\( ends-with \\( ((?&String)) , ((?&String)) \\) \\)',
			$groups . 'NotStartsWith' => 'not \\( starts-with \\( ((?&String)) , ((?&String)) \\) \\)',
			$groups . 'StartsWith'    => 'starts-with \\( ((?&String)) , ((?&String)) \\)',
			'Number:StringLength'     => 'string-length \\( ((?&String))? \\)'
		];
	}

	/**
	* Convert a call to contains()
	*
	* @param  string $haystack Expression for the haystack part of the call
	* @param  string $needle   Expression for the needle part of the call
	* @return string
	*/
	public function parseContains($haystack, $needle)
	{
		return $this->generateContains($haystack, $needle, true);
	}

	/**
	* Convert a call to ends-with()
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for the substring part of the call
	* @return string
	*/
	public function parseEndsWith($string, $substring)
	{
		return $this->generateEndsWith($string, $substring, true);
	}

	/**
	* Convert a call to not(contains())
	*
	* @param  string $haystack Expression for the haystack part of the call
	* @param  string $needle   Expression for the needle part of the call
	* @return string
	*/
	public function parseNotContains($haystack, $needle)
	{
		return $this->generateContains($haystack, $needle, false);
	}

	/**
	* Convert a call to not(ends-with())
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for the substring part of the call
	* @return string
	*/
	public function parseNotEndsWith($string, $substring)
	{
		return $this->generateEndsWith($string, $substring, false);
	}

	/**
	* Convert a call to not(starts-with())
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for the substring part of the call
	* @return string
	*/
	public function parseNotStartsWith($string, $substring)
	{
		return $this->generateStartsWith($string, $substring, false);
	}

	/**
	* Convert a call to starts-with()
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for the substring part of the call
	* @return string
	*/
	public function parseStartsWith($string, $substring)
	{
		return $this->generateStartsWith($string, $substring, true);
	}

	/**
	* Convert a call to string-length()
	*
	* @param  string $expr
	* @return string
	*/
	public function parseStringLength($expr = '.')
	{
		return "preg_match_all('(.)su'," . $this->recurse($expr) . ')';
	}

	/**
	* Generate the code for a call to contains()
	*
	* @param  string $haystack Expression for the haystack part of the call
	* @param  string $needle   Expression for the needle part of the call
	* @param  bool   $bool     Return value for a positive match
	* @return string
	*/
	protected function generateContains($haystack, $needle, $bool)
	{
		$operator = ($bool) ? '!==' : '===';

		return '(strpos(' . $this->recurse($haystack) . ',' . $this->recurse($needle) . ')' . $operator . 'false)';
	}

	/**
	* Generate the code for a call to ends-with()
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for the substring part of the call
	* @param  bool   $bool      Return value for a positive match
	* @return string
	*/
	protected function generateEndsWith($string, $substring, $bool)
	{
		return (preg_match('(^(?:\'[^\']+\'|"[^"]+")$)D', $substring))
		     ? $this->generateEndsWithLiteral($string, $substring, $bool)
		     : $this->generateEndsWithExpression($string, $substring, $bool);
	}

	/**
	* Generate the code for a call to ends-with() where the second argument is a literal string
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for a literal substring
	* @param  bool   $bool      Return value for a positive match
	* @return string
	*/
	protected function generateEndsWithLiteral($string, $substring, $bool)
	{
		$operator = ($bool) ? '===' : '!==';

		return '(substr(' . $this->recurse($string) . ',-' . (strlen($substring) - 2) . ')' . $operator . $this->recurse($substring) . ')';
	}

	/**
	* Generate the code for a call to ends-with()
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for the substring part of the call
	* @param  bool   $bool      Return value for a positive match
	* @return string
	*/
	protected function generateEndsWithExpression($string, $substring, $bool)
	{
		$operator = ($bool) ? '' : '!';

		return $operator . "preg_match('('.preg_quote(" . $this->recurse($substring) . ").'$)D'," . $this->recurse($string) . ')';
	}

	/**
	* Generate the code for a call to starts-with()
	*
	* @param  string $string    Expression for the string part of the call
	* @param  string $substring Expression for the substring part of the call
	* @param  bool   $bool      Return value for a positive match
	* @return string
	*/
	protected function generateStartsWith($string, $substring, $bool)
	{
		$operator = ($bool) ? '===' : '!==';

		return '(strpos(' . $this->recurse($string) . ',' . $this->recurse($substring) . ')' . $operator . '0)';
	}
}