<?php

namespace TablePress\PhpOffice\PhpSpreadsheet\Calculation\Token;

use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Engine\BranchPruner;
use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;

class Stack
{
	private BranchPruner $branchPruner;

	/**
	 * The parser stack for formulae.
	 *
	 * @var array<int, array<mixed>>
	 */
	private array $stack = [];

	/**
	 * Count of entries in the parser stack.
	 */
	private int $count = 0;

	public function __construct(BranchPruner $branchPruner)
	{
		$this->branchPruner = $branchPruner;
	}

	/**
	 * Return the number of entries on the stack.
	 */
	public function count(): int
	{
		return $this->count;
	}

	/**
				 * Push a new entry onto the stack.
				 * @param mixed $value
				 */
				public function push(string $type, $value, ?string $reference = null): void
	{
		$stackItem = $this->getStackItem($type, $value, $reference);
		$this->stack[$this->count++] = $stackItem;

		if ($type === 'Function') {
			$localeFunction = Calculation::localeFunc(StringHelper::convertToString($value));
			if ($localeFunction != $value) {
				$this->stack[($this->count - 1)]['localeValue'] = $localeFunction;
			}
		}
	}

	/** @param array<mixed> $stackItem */
	public function pushStackItem(array $stackItem): void
	{
		$this->stack[$this->count++] = $stackItem;
	}

	/** @return array<mixed>
				 * @param mixed $value */
				public function getStackItem(string $type, $value, ?string $reference = null): array
	{
		$stackItem = [
			'type' => $type,
			'value' => $value,
			'reference' => $reference,
		];

		// will store the result under this alias
		$storeKey = $this->branchPruner->currentCondition();
		if (isset($storeKey) || $reference === 'NULL') {
			$stackItem['storeKey'] = $storeKey;
		}

		// will only run computation if the matching store key is true
		$onlyIf = $this->branchPruner->currentOnlyIf();
		if (isset($onlyIf) || $reference === 'NULL') {
			$stackItem['onlyIf'] = $onlyIf;
		}

		// will only run computation if the matching store key is false
		$onlyIfNot = $this->branchPruner->currentOnlyIfNot();
		if (isset($onlyIfNot) || $reference === 'NULL') {
			$stackItem['onlyIfNot'] = $onlyIfNot;
		}

		return $stackItem;
	}

	/**
	 * Pop the last entry from the stack.
	 *
	 * @return null|array<mixed>
	 */
	public function pop(): ?array
	{
		if ($this->count > 0) {
			return $this->stack[--$this->count];
		}

		return null;
	}

	/**
	 * Return an entry from the stack without removing it.
	 *
	 * @return null|array<mixed>
	 */
	public function last(int $n = 1): ?array
	{
		if ($this->count - $n < 0) {
			return null;
		}

		return $this->stack[$this->count - $n];
	}

	/**
	 * Clear the stack.
	 */
	public function clear(): void
	{
		$this->stack = [];
		$this->count = 0;
	}
}
