<?php
/**
 * @license GPL-2.0-or-later
 * @file
 * @ingroup Parser
 */

namespace MediaWiki\Parser;

use MediaWiki\Title\Title;

/**
 * Expansion frame with template arguments
 * @ingroup Parser
 */
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
class PPTemplateFrame_Hash extends PPFrame_Hash {

	/** @var array */
	public $numberedArgs;
	/** @var array */
	public $namedArgs;
	/** @var PPFrame_Hash */
	public $parent;
	/** @var array */
	public $numberedExpansionCache;
	/** @var array */
	public $namedExpansionCache;

	/**
	 * @param Preprocessor $preprocessor
	 * @param false|PPFrame $parent
	 * @param array $numberedArgs
	 * @param array $namedArgs
	 * @param false|Title $title
	 */
	public function __construct( $preprocessor, $parent = false, $numberedArgs = [],
		$namedArgs = [], $title = false
	) {
		parent::__construct( $preprocessor );
		/** @var PPFrame_Hash $parent */
		'@phan-var PPFrame_Hash $parent';

		$this->parent = $parent;
		$this->numberedArgs = $numberedArgs;
		$this->namedArgs = $namedArgs;
		$this->title = $title;
		$pdbk = $title ? $title->getPrefixedDBkey() : false;
		$this->titleCache = $parent->titleCache;
		$this->titleCache[] = $pdbk;
		$this->loopCheckHash = /*clone*/ $parent->loopCheckHash;
		if ( $pdbk !== false ) {
			$this->loopCheckHash[$pdbk] = true;
		}
		$this->depth = $parent->depth + 1;
		$this->numberedExpansionCache = $this->namedExpansionCache = [];
	}

	public function __toString() {
		$s = 'tplframe{';
		$first = true;
		$args = $this->numberedArgs + $this->namedArgs;
		foreach ( $args as $name => $value ) {
			if ( $first ) {
				$first = false;
			} else {
				$s .= ', ';
			}
			$s .= "\"$name\":\"" .
				str_replace( '"', '\\"', $value->__toString() ) . '"';
		}
		$s .= '}';
		return $s;
	}

	/**
	 * @param string|int $key
	 * @param string|PPNode $root
	 * @param int $flags
	 * @return string
	 */
	public function cachedExpand( $key, $root, $flags = 0 ) {
		if ( isset( $this->parent->childExpansionCache[$key] ) ) {
			return $this->parent->childExpansionCache[$key];
		}
		$retval = $this->expand( $root, $flags );
		if ( !$this->isVolatile() ) {
			$this->parent->childExpansionCache[$key] = $retval;
		}
		return $retval;
	}

	/**
	 * Returns true if there are no arguments in this frame
	 *
	 * @return bool
	 */
	public function isEmpty() {
		return !count( $this->numberedArgs ) && !count( $this->namedArgs );
	}

	/**
	 * @return array
	 */
	public function getArguments() {
		$arguments = [];
		foreach ( array_merge(
				array_keys( $this->numberedArgs ),
				array_keys( $this->namedArgs ) ) as $key ) {
			$arguments[$key] = $this->getArgument( $key );
		}
		return $arguments;
	}

	/**
	 * @return array
	 */
	public function getNumberedArguments() {
		$arguments = [];
		foreach ( $this->numberedArgs as $key => $_ ) {
			$arguments[$key] = $this->getArgument( $key );
		}
		return $arguments;
	}

	/**
	 * @return array
	 */
	public function getNamedArguments() {
		$arguments = [];
		foreach ( $this->namedArgs as $key => $_ ) {
			$arguments[$key] = $this->getArgument( $key );
		}
		return $arguments;
	}

	/**
	 * @param int $index
	 * @return string|false
	 */
	public function getNumberedArgument( $index ) {
		if ( !isset( $this->numberedArgs[$index] ) ) {
			return false;
		}
		if ( !isset( $this->numberedExpansionCache[$index] ) ) {
			# No trimming for unnamed arguments
			$this->numberedExpansionCache[$index] = $this->parent->expand(
				$this->numberedArgs[$index],
				PPFrame::STRIP_COMMENTS
			);
		}
		return $this->numberedExpansionCache[$index];
	}

	/**
	 * @param string $name
	 * @return string|false
	 */
	public function getNamedArgument( $name ) {
		if ( !isset( $this->namedArgs[$name] ) ) {
			return false;
		}
		if ( !isset( $this->namedExpansionCache[$name] ) ) {
			# Trim named arguments post-expand, for backwards compatibility
			$this->namedExpansionCache[$name] = trim(
				$this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) );
		}
		return $this->namedExpansionCache[$name];
	}

	/**
	 * @param int|string $name
	 * @return string|false
	 */
	public function getArgument( $name ) {
		$text = $this->getNumberedArgument( $name );
		if ( $text === false ) {
			$text = $this->getNamedArgument( $name );
		}
		return $text;
	}

	/**
	 * Return true if the frame is a template frame
	 *
	 * @return bool
	 */
	public function isTemplate() {
		return true;
	}

	/** @inheritDoc */
	public function setVolatile( $flag = true ) {
		parent::setVolatile( $flag );
		$this->parent->setVolatile( $flag );
	}

	/** @inheritDoc */
	public function setTTL( $ttl ) {
		parent::setTTL( $ttl );
		$this->parent->setTTL( $ttl );
	}
}

/** @deprecated class alias since 1.43 */
class_alias( PPTemplateFrame_Hash::class, 'PPTemplateFrame_Hash' );
