<?php
/**
 * This file is part of the MathExecutor package
 *
 * (c) Alexander Kiryukhin
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code
 */

namespace NXP\Classes;

use NXP\Classes\Token\InterfaceOperator;
use NXP\Classes\Token\TokenFunction;
use NXP\Classes\Token\TokenNumber;
use NXP\Classes\Token\TokenString;
use NXP\Classes\Token\TokenVariable;
use NXP\Exception\IncorrectExpressionException;
use NXP\Exception\UnknownVariableException;

/**
 * @author Alexander Kiryukhin <alexander@symdev.org>
 */
class Calculator
{
    /**
     * Calculate array of tokens in reverse polish notation
     * @param  array                                       $tokens    Array of tokens
     * @param  array                                       $variables Array of variables
     * @return number                                      Result
     * @throws \NXP\Exception\IncorrectExpressionException
     * @throws \NXP\Exception\UnknownVariableException
     */
    public function calculate($tokens, $variables)
    {
        $stack = [];
        foreach ($tokens as $token) {
            if ($token instanceof TokenNumber) {
                array_push($stack, $token);
            }
            if ($token instanceof TokenString) {
                array_push($stack, $token);
            }
            if ($token instanceof TokenVariable) {
                $variable = $token->getValue();
                if (!array_key_exists($variable, $variables)) {
                    throw new UnknownVariableException($variable);
                }
                $value = $variables[$variable];
                array_push($stack, new TokenNumber($value));
            }
            if ($token instanceof InterfaceOperator || $token instanceof TokenFunction) {
                array_push($stack, $token->execute($stack));
            }
        }
        $result = array_pop($stack);
        if ($result === null || ! empty($stack)) {
            throw new IncorrectExpressionException();
        }

        return $result->getValue();
    }
}