<?php
/**
 * @copyright Copyright 2014 Carsten Brandt, 2024 Daniel Pimley
 * @license https://github.com/xenocrat/chyrp-markdown/blob/master/LICENSE
 * @link https://github.com/xenocrat/chyrp-markdown#readme
 */

namespace xenocrat\markdown\inline;

/**
 * Adds inline emphasizes and strong elements.
 */
trait EmphStrongTrait
{
	protected function parseEmphStrongMarkers(): array
	{
		return array('_', '*');
	}

	/**
	 * Parses emphasized and strong elements.
	 *
	 * @marker _
	 * @marker *
	 */
	protected function parseEmphStrong($text): array
	{
		$marker = $text[0];

		if (!isset($text[1])) {
			return [['text', $text[0]], 1];
		}

		if ($marker == $text[1]) {
		// Strong.
			// Avoid excessive regex backtracking if there is no closing marker.
			if (strpos($text, $marker . $marker, 2) === false) {
				return [['text', $text[0]], 1];
			}
			if (
				$marker === '*'
				&& preg_match(
					'/^[*]{2}((?>\\\\[*]|[^*]|[*][^*]*[*])+?)[*]{2}/s',
					$text,
					$matches
				)
				|| $marker === '_'
				&& preg_match(
					'/^__((?>\\\\_|[^_]|_[^_]*_)+?)__\b/us',
					$text,
					$matches
				)
			) {
				$content = $matches[1];
				// If nothing is contained in a strong,
				// do not consider it valid.
				if ($content === '') {
					return [['text', $text[0]], 2];
				}
				// First and last chars of the strong text
				// cannot be whitespace.
				if (
					strspn($content, " \t\n", 0, 1) === 0
					&& strspn($content, " \t\n", -1) === 0
				) {
					return [
						[
							'strong',
							$this->parseInline($content),
						],
						strlen($matches[0])
					];
				}
			}
		} else {
		// Emphasis
			// Avoid excessive regex backtracking if there is no closing marker.
			if (strpos($text, $marker, 1) === false) {
				return [['text', $text[0]], 1];
			}
			if (
				$marker === '*'
				&& preg_match(
					'/^[*]((?>\\\\[*]|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*][^*])/s',
					$text,
					$matches
				)
				|| $marker === '_'
				&& preg_match(
					'/^_((?>\\\\_|[^_]|__[^_]*__)+?)_(?!_[^_])\b/us',
					$text,
					$matches
				)
			) {
				$content = $matches[1];
				// If nothing is contained in an emphasis,
				// do not consider it valid.
				if ($content === '') {
					return [['text', $text[0]], 2];
				}
				// First and last chars of the emphasised text
				// cannot be whitespace.
				if (
					strspn($content, " \t\n", 0, 1) === 0
					&& strspn($content, " \t\n", -1) === 0
				) {
					return [
						[
							'emph',
							$this->parseInline($content),
						],
						strlen($matches[0])
					];
				}
			}
		}

		return [['text', $text[0]], 1];
	}

	protected function renderStrong($block): string
	{
		return '<strong>'
			. $this->renderAbsy($block[1])
			. '</strong>';
	}

	protected function renderEmph($block): string
	{
		return '<em>'
			. $this->renderAbsy($block[1])
			. '</em>';
	}

	abstract protected function parseInline($text);
	abstract protected function renderAbsy($blocks);
}