<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonCommon\Classes\SmartyPlugins\Modifier;

use Doctrine\ORM\EntityRepository;
use Zend_Currency;
use Zend_Locale;

/**
 * Helper class with the smarty modifier function to format currency values. The values are formatted for
 * the given Zend_Currency, or a currency specified by a 'pickwareCurrencyId' option.
 *
 * In contrast to Shopware's default implementation, the values are formatted by default in the given
 * {@see Zend_Currency currency}, not in the contexts' currently registered {@see \Shopware\Models\Shop\Shop Shop}'s
 * currency.
 *
 * @see self::modify() the formatting function which describes how to apply options
 */
class CurrencyModifier
{
    /**
     * @var Zend_Currency
     */
    protected $defaultCurrency;

    /**
     * @var null|Zend_Locale
     */
    protected $locale;

    /**
     * @var null|EntityRepository
     */
    protected $currencyRepository;

    /**
     * @param Zend_Currency $defaultCurrency the currency for which a given amount shall be formatted, unless overridden
     *     by a 'pickwareCurrencyId' option supplied to {@see @see self::modify()}
     * @param null|Zend_Locale $locale if not null, the locale rules to format the amounts with currencies (ignored for
     *     the $defaultCurrency)
     * @param null|EntityRepository $currencyRepository if not null, a repository with which an amount can be formatted
     *     for a specific {@see \Shopware\Models\Shop\Currency Shopware Currency model} by specifying a
     *     'pickwareCurrencyId' option to the 'currency' template modifier.
     */
    public function __construct(
        Zend_Currency $defaultCurrency,
        Zend_Locale $locale = null,
        EntityRepository $currencyRepository = null
    ) {
        $this->defaultCurrency = $defaultCurrency;
        $this->locale = $locale;
        $this->currencyRepository = $currencyRepository;
    }

    /**
     * Registers this modifier in the given template engine. Unregisters any modifier with the same tag (function) that
     * may be already registered in this template engine.
     *
     * @param \Smarty $templateEngine
     */
    public function registerInSmarty(\Smarty $templateEngine)
    {
        $templateEngine->unregisterPlugin('modifier', 'currency');
        $templateEngine->registerPlugin(
            'modifier',
            'currency',
            [
                $this,
                'modify',
            ]
        );
    }

    /**
     * Formats the given value into a currency string. It is formatted according to the set currency of this class, or,
     * if given a 'pickwareCurrencyId' option, for that currency.
     *
     * @see https://github.com/shopware/shopware/blob/d7481242e8198853a5285ee67e288dd33485e697/engine/Library/Enlight/Template/Plugins/modifier.currency.php
     *
     * You can specify a specific currency for which to format the given value like this in a Smarty template:
     *
     * <code>
     *     {$myAmount|currency:null:null:['pickwareCurrencyId' => $myCurrencyId]}
     * </code>
     *
     * Note: 'pickwareCurrencyId' may only be used if the instance has been {@see self::__construct() set up} with the
     * $locale and $currencyRepository parameters.
     *
     * @param string $value the amount as a number (Note: both commas and periods are treated as decimal separators)
     * @param null|string|array $config either a value suitable for $position (as a shorthand),
     *     or a display style for the currency sign (one of 'NO_SYMBOL', 'USE_SYMBOL', 'USE_SHORTNAME', 'USE_NAME', see
     *     {@see Zend_Currency}'s display constants),
     *     or an array map containing such values for its 'position' and/or 'display' keys
     * @param null|string $position determines the position of the currency sign relative to the amount
     *     (one of 'STANDARD', 'LEFT', 'RIGHT', see {@see Zend_Currency}'s position constants)
     * @param null|array $currencyOptions null, or an array map with an optional 'pickwareCurrencyId' key, the value of
     *     which will be used to retrieve the corresponding currency settings from Shopware's {@see
     *     \Shopware\Models\Shop\Currency Currency model}.
     * @return string the formatted amount, with or without a currency sign,
     */
    public function modify($value, $config = null, $position = null, $currencyOptions = null)
    {
        if (!empty($config) && is_string($config)) {
            $config = mb_strtoupper($config);
            if (defined('Zend_Currency::' . $config)) {
                $config = ['display' => constant('Zend_Currency::' . $config)];
            } else {
                $config = [];
            }
        } else {
            $config = [];
        }

        if (!empty($position) && is_string($position)) {
            $position = mb_strtoupper($position);
            if (defined('Zend_Currency::' . $position)) {
                $config['position'] = constant('Zend_Currency::' . $position);
            }
        }

        /** @var null|Zend_Currency $currency */
        $currency = null;
        if (is_array($currencyOptions) && array_key_exists('pickwareCurrencyId', $currencyOptions)) {
            $currency = $this->getCurrencyById($currencyOptions['pickwareCurrencyId']);
        } else {
            $currency = $this->defaultCurrency;
        }
        if ($currency === null) {
            // Match Shopware's original currency modifier's behavior and fall back to not using any currency.
            return $value;
        }

        $formattedValue = (float) str_replace(',', '.', $value);
        $formattedValue = $currency->toCurrency($formattedValue, $config);
        $formattedValue = mb_convert_encoding($formattedValue, 'HTML-ENTITIES', 'UTF-8');

        return htmlentities($formattedValue, ENT_COMPAT, 'UTF-8', false);
    }

    /**
     * @param mixed $currencyId the id for a {@see \Shopware\Models\Shop\Currency Shopware Currency model}
     * @return null|Zend_Currency if found, returns a {@see Zend_Currency} corresponding to this instance's configured
     *     {@see Zend_Locale locale} and the given {@see \Shopware\Models\Shop\Currency currency}'s id, otherwise null
     */
    private function getCurrencyById($currencyId)
    {
        if ($this->currencyRepository === null) {
            throw new \LogicException(sprintf(
                'Cannot request a specific currency if %s was not set up with a currencyRepository',
                get_class($this)
            ));
        }
        $shopwareCurrency = $this->currencyRepository->find($currencyId);
        if ($shopwareCurrency === null) {
            return null;
        }

        return new Zend_Currency(
            $shopwareCurrency->toArray(),
            $this->locale
        );
    }
}
