<?php

use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\AdminBundle\Util\LegacyAdminCredentials;
use KupShop\CatalogBundle\Section\SectionTree;
use KupShop\ContentBundle\Util\ImageLocator;
use KupShop\ContentBundle\Util\MenuUtil;
use KupShop\I18nBundle\Translations\ArticlesSectionsTranslation;
use KupShop\I18nBundle\Translations\PhotosTranslation;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Context\CacheContext;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\DomainContext;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Context\VatContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Compat\SymfonyBridge;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\KupShopBundle\Util\Functional\Mapping;
use KupShop\KupShopBundle\Util\Smarty\SmartyML;
use Query\Operator;
use Query\QueryBuilder;
use Query\Translation;
use Symfony\Component\Routing\Router;

function autoload($fqcn)
{
    global $cfg;

    static $subclass = 1;

    if (strpos($fqcn, '\\')) {
        $fileName = strtr($fqcn, '\\', '/').'.php';
    } else {
        $fileName = "class.{$fqcn}.php";
    }

    $shopFile = $cfg['Path']['web_root'].$fileName;
    $engineFile = $cfg['Path']['shared_class'].$fileName;

    if (file_exists($engineFile)) {
        require_once $engineFile;
    }

    if (!empty($cfg['autoload_web_root']) && file_exists($shopFile)) {
        require_once $shopFile;
    } else {
        if (!class_exists($fqcn, 0) && class_exists($fqcn.'Base', 0)) {
            list($namespace, $className) = parseClassName($fqcn);
            eval("namespace {$namespace} {class {$className} extends {$className}Base {}}");
        }
    }
}

/**
 * Splits FQCN into namespace part and class name part, returns tuple [$namespace, $className].
 *
 * @param string $className
 *
 * @return array
 */
function parseClassName($className)
{
    $parts = explode('\\', $className);
    $className = array_pop($parts);

    return [implode('\\', $parts), $className];
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//
/**
 * @param int  $vat
 * @param bool $ceil
 * @param null $currency
 *
 * @return Decimal[]
 */
function formatPrice($price, $vat = 0, $ceil = true, $currency = null)
{
    // pole, ktere se bude vracet
    $price = toDecimal($price);
    $PRICE['value_with_vat'] = $price;
    // normalni cena s DPH
    $PRICE['value_without_vat'] = $price;
    // cena DPH
    if (empty($currency)) {
        $currencyContext = Contexts::get(CurrencyContext::class);
        $currency = $currencyContext->getActive()->getSymbol();
        $currencyObject = $currencyContext->getActive();
    }
    $PRICE['currency_object'] = $currencyObject ?? null;
    $PRICE['currency'] = $currency;
    // mena
    $PRICE['vat'] = $vat;
    // obsahuje hodnotu dane, ktera je pro tuto polozku

    // -------------------------------------------------
    // pricteni dane z ceny
    if ($vat != 0) {
        // normalni cena s DPH
        $PRICE['value_with_vat'] = calcPrice($PRICE['value_without_vat'], $vat)->value(2); // ( $PRICE['value_without_vat'] * ( 1 + ( $vat / 100 ) ) );
        // cena DPH
        $PRICE['value_vat'] = $PRICE['value_with_vat']->sub(toDecimal($PRICE['value_without_vat']));
    } else {
        $PRICE['value_vat'] = toDecimal(0);
    }

    // cena pred bez zaokrouhleni
    $PRICE['value_with_vat_no_rounding'] = $PRICE['value_with_vat'];
    $PRICE['value_without_vat_no_rounding'] = $PRICE['value_without_vat'];

    return $PRICE;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param int  $discount
 * @param int  $vat
 * @param null $IDproduct
 * @param null $IDcategory
 * @param null $IDproducer
 *
 * @return Decimal[]
 */
function formatCustomerPrice($price, $discount = 0, $vat = 0, $IDproduct = null, $IDcategory = null, $IDproducer = null, ?PriceLevel $priceLevel = null, $currency = null)
{
    $discount = toDecimal($discount);
    // pole, ktere se bude vracet
    $PRICE['price_with_vat'] = toDecimal($price);
    // normalni cena s DPH
    $PRICE['price_without_vat'] = toDecimal($price);
    // cena bez DPH

    $currencyContext = Contexts::get(CurrencyContext::class);
    if (!$currency) {
        $currency = $currencyContext->getDefault();
    }

    $PRICE['currency_object'] = $currency;
    $PRICE['currency'] = $currencyContext->getActive()->getSymbol();

    // mena
    $PRICE['vat'] = $vat;

    // -------------------------------------------------
    // sleva pro dealery
    $PRICE = calcCustomerPrice($PRICE, $discount, $vat, $IDproduct, $IDcategory, $IDproducer, $priceLevel);

    // -------------------------------------------------
    // normalni sleva
    if (!$discount->isZero() && empty($PRICE['B2B_discount'])) {
        $PRICE['price_without_vat'] = calcPrice($PRICE['price_without_vat'], null, $discount);
        $PRICE['price_with_vat'] = $PRICE['price_without_vat'];
    }

    // -------------------------------------------------
    // pricteni dane z ceny
    if ($vat != 0) {
        // normalni cena s DPH
        $PRICE['price_with_vat'] = calcPrice($PRICE['price_with_vat'], $vat);
    }

    // -------------------------------------------------
    // rozhodnout, ktera cena se bude zapocitavat
    if (!empty($PRICE['B2B_discount'])) {
        $PRICE['value_with_vat'] = roundPrice($PRICE['B2B_price_with_vat']);
        $PRICE['value_without_vat'] = $PRICE['value_with_vat']->removeVat($vat);
    } else {
        $PRICE['value_with_vat'] = roundPrice($PRICE['price_with_vat']);
        $PRICE['value_without_vat'] = $PRICE['value_with_vat']->removeVat($vat);
    }

    // cena pred bez zaokrouhleni
    $PRICE['value_with_vat_no_rounding'] = $PRICE['value_with_vat'];
    $PRICE['value_without_vat_no_rounding'] = $PRICE['value_without_vat'];

    // -------------------------------------------------
    return $PRICE;
}

function calcCustomerPrice($PRICE, $discount, $vat, $IDproduct = null, $IDcategory = null, $IDproducer = null, ?PriceLevel $priceLevel = null)
{
    if (!$priceLevel && findModule(Modules::PRICE_LEVELS)) {
        /** @var \KupShop\KupShopBundle\Context\PriceLevelContext $plService */
        $plService = ServiceContainer::getService(\KupShop\KupShopBundle\Context\PriceLevelContext::class);
        $priceLevel = $plService->getActive();
    }

    if ($priceLevel) {
        $PRICE['B2B_price_with_vat'] = null;
        // cena s B2B slevou s dani
        $PRICE['B2B_price_without_vat'] = null;
        // cena s B2B slevou bez dane
        $PRICE['B2B_discount'] = null;
        // obsahuje hodnotu dane slevy
        $PRICE['B2B_discount_unit'] = null;
        // obsahuje jednotku slevy

        // $discount = 0;
        $B2B_discount = null;
        $B2B_unit = null;

        // urceni slevy podle slevy k produktu
        if ($IDproduct > 0 && isset($priceLevel->products[$IDproduct])) {
            $B2B_discount = $priceLevel->products[$IDproduct]['discount'];
            $B2B_unit = $priceLevel->products[$IDproduct]['unit'];
        } // urceni slevy podle slevy k sekci
        elseif (!is_array($IDcategory) && $IDcategory > 0 && isset($priceLevel->sections[$IDcategory])) {
            $B2B_discount = $priceLevel->sections[$IDcategory]['discount'];
            $B2B_unit = $priceLevel->sections[$IDcategory]['unit'];
        } // urceni slevy podle slevy k vyrobci
        elseif ($IDproducer > 0 && isset($priceLevel->producers[$IDproducer])) {
            $B2B_discount = $priceLevel->producers[$IDproducer]['discount'];
            $B2B_unit = $priceLevel->producers[$IDproducer]['unit'];
        } // urceni slevy pokud neni urcena sekce - dohledat sekci k produktu
        else {
            if ($IDproduct > 0 && (is_null($IDcategory) || is_array($IDcategory)) && !empty($priceLevel->sections)) {
                if (is_null($IDcategory)) {
                    $IDcategory = sqlQueryBuilder()->select('id_section')->from('products_in_sections')
                        ->where(\Query\Operator::equals(['id_product' => $IDproduct]))
                        ->execute()->fetchAll();
                    $IDcategory = array_column($IDcategory, 'id_section');
                }
                foreach ($IDcategory as $id_section) {
                    if (isset($priceLevel->sections[$id_section])) {
                        if (is_null($B2B_discount)
                            || ($priceLevel->sections[$id_section]['discount'] > $B2B_discount
                                && $priceLevel->sections[$id_section]['unit'] == $B2B_unit)
                            || ($priceLevel->sections[$id_section]['unit'] == 'perc'
                                && $B2B_unit == 'price')
                        ) {
                            $B2B_discount = $priceLevel->sections[$id_section]['discount'];
                            $B2B_unit = $priceLevel->sections[$id_section]['unit'];
                            // ulozit nalezenou informaci o zbozi
                            $priceLevel->products[$IDproduct]['discount'] = $B2B_discount;
                            $priceLevel->products[$IDproduct]['unit'] = $B2B_unit;
                        }
                    }
                }
            }

            // urceni slevy podle vyrobce - dohledat vyrobce k produktu
            if (is_null($B2B_discount) && is_null($IDproducer) && $IDproduct > 0 && !empty($priceLevel->producers)) {
                $SQL3 = sqlQuery('SELECT p.producer
               FROM products AS p
               WHERE p.id=:id_product', ['id_product' => $IDproduct]);
                while ($row = sqlFetchArray($SQL3)) {
                    if (isset($priceLevel->producers[$row['producer']])) {
                        $B2B_discount = $priceLevel->producers[$row['producer']]['discount'];
                        $B2B_unit = $priceLevel->producers[$row['producer']]['unit'];
                    }
                }
                sqlFreeResult($SQL3);
            }

            if ($range = $priceLevel->getRangeByPrice($PRICE['price_without_vat'])) {
                $B2B_discount = $range['discount'];
                $B2B_unit = $range['unit'];
            }
        }

        // globalni sleva
        if (is_null($B2B_discount)) {
            $B2B_discount = $priceLevel->discount;
            $B2B_unit = $priceLevel->unit;
        }

        // Sleva ze slevy
        if (!$discount->isZero() && !is_null($priceLevel->discount_discount)) {
            $B2B_discount = $priceLevel->discount_discount;
        }

        $PRICE['B2B_price_original'] = $PRICE['price_without_vat'];
        $PRICE['B2B_price_original_vat'] = calcPrice($PRICE['price_without_vat'], $vat);

        $currency = Contexts::get(CurrencyContext::class)->getActive();

        switch ($B2B_unit) {
            case 'perc':
                if (!$discount->isZero()) {
                    if ($priceLevel->add) {
                        $B2B_discount = toDecimal(100)->sub(
                            toDecimal(100)->sub(toDecimal($B2B_discount))->mul(toDecimal(100)->sub($discount))->div(toDecimal(100)));
                    } else {
                        $B2B_discount = toDecimal($B2B_discount)->lowerThan($discount) ? $discount : $B2B_discount;
                    }
                }
                $PRICE['B2B_price_without_vat'] = calcPrice($PRICE['price_with_vat'], 0, $B2B_discount);
                $PRICE['B2B_discount'] = toDecimal($B2B_discount)->round(2);
                $PRICE['B2B_discount_unit'] = '%';
                $PRICE['B2B_discount_name'] = $priceLevel->name;
                break;
            case 'price':
                $PRICE['B2B_price_without_vat'] = $PRICE['price_with_vat']->sub(toDecimal($B2B_discount));
                $PRICE['B2B_discount'] = toDecimal($B2B_discount);
                $PRICE['B2B_discount_unit'] = $currency->getSymbol();
                if ($PRICE['B2B_price_without_vat']->isNegative()) {
                    $PRICE['B2B_price_without_vat'] = DecimalConstants::zero();
                }
                break;
            case 'final_price':
                $PRICE['B2B_price_without_vat'] = toDecimal($B2B_discount);
                $PRICE['B2B_discount'] = $PRICE['B2B_price_without_vat']->sub(toDecimal($B2B_discount));
                $PRICE['B2B_discount_unit'] = $currency->getSymbol();
                break;
        }
        $PRICE['B2B_price_with_vat'] = $PRICE['B2B_price_without_vat'];

        // pricteni dane z ceny
        if ($vat != 0) {
            $PRICE['B2B_price_with_vat'] = calcPrice($PRICE['B2B_price_without_vat'], $vat);
            // ( $PRICE['B2B_price_without_vat'] * ( 1 + ( $vat / 100 ) ) );
            // normalni B2B cena s DPH
        }
    }

    if (findModule('currencies')) {
        $fields = ['B2B_price_with_vat',
            'B2B_price_without_vat',
            'B2B_price_original',
            'B2B_price_original_vat',
            'price_with_vat',
            'price_without_vat',
            'value_with_vat',
            'value_without_vat',
            'value_with_vat_no_rounding',
            'value_without_vat_no_rounding',
        ];
        foreach ($fields as $field) {
            if (isset($PRICE[$field])) {
                $PRICE[$field] = applyCurrency($PRICE[$field], $PRICE['currency_object']);
            }
        }
    }

    return $PRICE;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// Prepocita castu podle meny, kterou ma nastaven aktualne prihlaseny uzivatel
/**
 * @deprecated Use PriceConverter
 *
 * @return Decimal
 */
function applyCurrency($price, $currencyObject = null)
{
    $price = toDecimal($price);

    $currency = Contexts::get(CurrencyContext::class)->getActive();

    if ($currencyObject && $currencyObject->getId() == $currency->getId()) {
        return $price;
    }

    if (!$currency->getRate()->equals(DecimalConstants::one())) {
        $price = $price->div($currency->getRate());
    }

    return $price;
}

/**
 * @deprecated Use PriceConverter
 *
 * @return Decimal
 */
function unapplyCurrency($price)
{
    $price = toDecimal($price);

    $currency = Contexts::get(CurrencyContext::class)->getActive();

    if (!$currency->getRate()->equals(DecimalConstants::one())) {
        $price = $price->mul($currency->getRate());
    }

    return $price;
}

$activeCurrency = null;
/**
 * @return array
 *
 * @deprecated Pouzij CurrencyContext
 */
function getCustomerCurrency()
{
    $currency = Contexts::get(CurrencyContext::class)->getActive();

    return [
        'name' => $currency->getName(),
        'code' => $currency->getId(),
        'symbol' => $currency->getSymbol(),
        'price_round' => $currency->getPriceRound(),
        'price_round_order' => $currency->getPriceRoundOrder(),
        'price_round_direction' => $currency->getPriceRoundDirection(),
        'price_precision' => $currency->getPricePrecision(),
        'price_decimal_mark' => $currency->getPriceDecimalMark(),
        'rate' => $currency->getRate(),
    ];
}

/**
 * @deprecated Use ContextManager
 */
function changeCurrency($currency = 'rollback')
{
    static $currency_stack = [];
    global $activeCurrency;

    $currencyContext = Contexts::get(CurrencyContext::class);

    $activeCurrency = null;

    if ($currency == 'rollback') {
        $currency = array_pop($currency_stack);
    } elseif (!empty($currencyContext->getActive())) {
        array_push($currency_stack, $currencyContext->getActive());
    }

    if (is_object($currency)) {
        $currencyContext->activate($currency->getId());
    } else {
        if ($currency == null) {
            $currency = 'CZK';
        }
        $currencyContext->activate($currency);
    }

    return $currency;
}

/**
 * @deprecated Use PriceLevelContext
 */
function changePriceLevel($priceLevel = null)
{
    static $static_price_level = null;
    global $ctrl;

    if ($priceLevel === null) {
        $priceLevel = $static_price_level;
    } elseif (!empty($ctrl['dealerPricelevel']) && is_null($static_price_level)) {
        $static_price_level = $ctrl['dealerPricelevel'];
    }

    $ctrl['dealerPricelevel'] = $priceLevel;

    return $priceLevel;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function printPrice($PRICE, $param = [])
{
    global $cfg;

    $PRICE = \KupShop\KupShopBundle\Wrapper\PriceWrapper::unwrap($PRICE);

    $dbcfg = Settings::getDefault();

    $currencyContext = Contexts::get(CurrencyContext::class);
    $currency = $currencyContext->getActive();
    $source = null;

    // class Price to array
    if ($PRICE instanceof KupShop\KupShopBundle\Util\Price\Price) {
        $source = $PRICE->getSource();
        // convert currency
        $price = \KupShop\KupShopBundle\Util\Price\PriceCalculator::convert($PRICE, $currency);
        $ceil = ($param['ceil'] ?? null);
        $round = !($ceil === false);
        $PRICE = [
            'value_with_vat' => $price->getPriceWithVat($round),
            'value_without_vat' => $price->getPriceWithoutVat($round),
            'currency' => $currency->getSymbol(),
            'currency_object' => $currency,
            'vat' => $price->getVat(),
        ];
    }

    // class Price to array
    if ($PRICE instanceof KupShop\KupShopBundle\Util\Price\TotalPrice) {
        // convert currency
        $priceConverter = ServiceContainer::getService(\KupShop\I18nBundle\Util\PriceConverter::class);
        $PRICE = [
            'value_with_vat' => $priceConverter->convert($PRICE->getCurrency(), $currency, $PRICE->getPriceWithVat()),
            'value_without_vat' => $priceConverter->convert($PRICE->getCurrency(), $currency, $PRICE->getPriceWithoutVat()),
            'currency' => $currency->getSymbol(),
            'currency_object' => $currency,
            'vat' => 0,
        ];
    }

    // kontrola promenne $PRICE
    if (!is_array($PRICE)) {
        $price = toDecimal($PRICE);
        $PRICE = [];
        // obsahuje konecnou cenu s dani, ktera ma byt zapocitana
        $PRICE['value_with_vat'] = $price;
        // obsahuje konecnou cenu bez dane, ktera ma byt zapocitana
        $PRICE['value_without_vat'] = $price;

        // mena
        if (!empty($param['currency'])) {
            if ($param['currency'] instanceof \KupShop\I18nBundle\Entity\Currency) {
                $currency = $param['currency'];
            } else {
                $currencies = $currencyContext->getAll();
                if (array_key_exists($param['currency'], $currencies)) {
                    $currency = $currencies[$param['currency']];
                }
            }
        }
        $PRICE['currency'] = $currency->getSymbol();
        $PRICE['currency_object'] = $currency;

        // obsahuje hodnotu dane, ktera je pro tuto polozku
        $PRICE['vat'] = 0;
    }

    $cond = [
        'withVat' => $dbcfg['prod_show_price_vat'] == 'Y',
        'ceil' => 'default',
        'format' => true,
        'printcurrency' => true,
        'printdealerdiscount' => false,
        'dealerdiscount' => true,
        'decimal' => null,
    ];

    $cond = array_merge($cond, $param);

    if ($cond['withVat'] === 'dynamic') {
        if (isset($_COOKIE['prices_vat'])) {
            $cond['withVat'] = $_COOKIE['prices_vat'] == 'withVat';
        } else {
            $cond['withVat'] = $dbcfg['prod_show_price_vat'] == 'Y';
        }
    }

    // zda pouzit cenu s / bez DPH
    $price = ($cond['withVat']) ? $PRICE['value_with_vat'] : $PRICE['value_without_vat'];

    if (!$cond['dealerdiscount'] && isset($PRICE['B2B_price_original'])) {
        $price = ($cond['withVat']) ? $PRICE['B2B_price_original_vat'] : $PRICE['B2B_price_original'];
    }

    // Formatted price decimal places
    $decimal = $currency->getPricePrecision();

    // kdyz je format=default, tak rozhodnout podle nastaveni v DB
    if ($cond['ceil'] == 'default') {
        $cond['ceil'] = $currency->getPriceRound();
    } elseif (!$cond['ceil']) {
        $decimal = 2;
    }

    // Round price if set
    if ($cond['ceil']) {
        $price = roundPrice($price, -1, 'DB', 2, $currency, source: $source);
    } else {
        if ($price->round(2)->isZero() && !$price->isZero()) {
            $price = roundPrice($price, 1, 'up', 4, $currency, source: $source);
        }
    }

    if ($cond['format'] === 'float') {
        return $price->asString();
    }

    if ($cond['decimal']) {
        $decimal = $cond['decimal'];
    }

    // Backward compatibility if price_precision not set
    if (is_null($decimal)) {
        $decimal = $cond['ceil'] == 100 ? 0 : 2;
    }

    // Detect precision
    if ($decimal == 'dynamic') {
        $decimal = $price->round(2)->isInteger() ? 0 : 2;
    }

    $price = $price->printValue($decimal, $currency->getPriceDecimalMark());

    // formatovani ceny pro vystup
    if ($cond['format'] == true) {
        // dopsat jednotku ceny
        if ($cond['printcurrency']) {
            $price_format = translate('price_format', 'functions', true) ?: ['price_value', ' ', 'currency_symbol'];
            $price_format = array_map(function ($value) use ($price, $PRICE) {
                return match ($value) {
                    'price_value' => $price,
                    'currency_symbol' => $PRICE['currency'],
                    'currency_id' => $PRICE['currency_object']->getId(),
                    default => $value,
                };
            }, $price_format);
            $price = join('', $price_format);
        }

        // dopsat slevu dealera
        if ($cond['printdealerdiscount'] && isset($PRICE['B2B_discount'])) {
            $name = getVal('B2B_discount_name', $PRICE, translate('setPrice', 'functions')['b2b']);
            if ($PRICE['B2B_discount_unit'] == '%') {
                $price .= " <sub class='dealer_discount'>({$name}: {$PRICE['B2B_discount']}%)</sub>";
            } else {
                $price .= "<br><span class='dealer_discount'>({$name}: {$PRICE['B2B_discount']} {$PRICE['B2B_discount_unit']})</span>";
            }
        }
    }

    return $price;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param        $price     Decimal
 * @param int    $roundTo
 * @param string $direction
 * @param int    $decimal
 *
 * @return Decimal
 */
function roundPrice($price, $roundTo = -1, $direction = 'DB', $decimal = null, ?KupShop\I18nBundle\Entity\Currency $currency = null, $bata = null, $source = null)
{
    if (!$currency) {
        $currency = Contexts::get(CurrencyContext::class)->getActive();
    }

    if ($source) {
        $params = ['price' => &$price, 'roundTo' => &$roundTo, 'direction' => &$direction, 'decimal' => &$decimal, 'currency' => &$currency, 'bata' => &$bata];
        Contexts::get(CurrencyContext::class)->getSourceRounding($source, $params);
    }

    if (!is_a($price, 'Decimal')) {
        if (is_null($price)) {
            $price = '0';
        }

        $price = toDecimal($price);
    }

    // kontrola zadanych parametru
    if ($roundTo == -1 || $roundTo === null) {
        $roundTo = $currency->getPriceRound();
    }
    if ($direction == 'DB' || $direction === null) {
        $direction = $currency->getPriceRoundDirection();
    }

    if ($decimal === null) {
        $decimal = $currency->getPricePrecision();

        // Detect precision
        if ($decimal == 'dynamic') {
            $decimal = 2;
        }
    }

    if ($bata === null) {
        $bata = $currency->getBata();
    }

    // Pre-round input number to avoid invalid rounding of 5.00000213 numbers
    $price = $price->value(4);

    if (!$roundTo) {
        return $price;
    }

    $original_price = $price;
    $price = $price->value(2);

    // Pri zaporne hodnote je potreba otocit up to down, aby vysla stejna cena v plusu i v minusu
    if ($price->isNegative()) {
        $price = $price->abs();
    }

    if ($bata) {
        if ($price->isZero()) {
            $bata = null;
        } else {
            $bata = toDecimal($bata);
            $price = $price->add($bata);
            $decimal = 2;
        }
    }

    // kdyz zaokrouhlovat na cela cisla
    if ($roundTo == 100) {
        switch ($direction) {
            case 'up':
                $price = $price->ceil();
                break;
            case 'down':
                $price = $price->floor();
                break;
            case 'math':
                $price = $price->round();
                break;
        }
    } elseif ($roundTo == -5 || $roundTo == -10) {
        $money = $price->asFloat();
        switch ($direction) {
            case 'up':
                $money = abs($roundTo) * ceil($money / abs($roundTo));
                break;
            case 'down':
                $money = abs($roundTo) * floor($money / abs($roundTo));
                break;
            case 'math':
                $money = abs($roundTo) * round($money / abs($roundTo));
                break;
        }
        $price = toDecimal($money);
    } elseif ($roundTo == 50 || $roundTo == 5 || $roundTo == 25) {
        // kdyz zaokrouhlovat na 50satniky
        // rozdelit cenu na koruny a halere
        @list($money, $groat) = explode('.', $price->printFloatValue(2));
        settype($money, 'integer');
        settype($groat, 'integer');

        $multiplier = $roundTo == 50 ? 2 : ($roundTo == 25 ? 4 : 20);

        $groat = ($groat / pow(10, $decimal)) * $multiplier;
        switch ($direction) {
            case 'up':
                $groat = ceil($groat);
                break;
            case 'down':
                $groat = floor($groat);
                break;
            case 'math':
                $groat = round($groat);
                break;
        }
        $groat = $groat / $multiplier;

        if ($price->isNegative()) {
            $price = toDecimal($money)->sub(toDecimal($groat));
        } else {
            $price = toDecimal($money)->add(toDecimal($groat));
        }
    } elseif ($roundTo == 10) {
        // zaokrouhleni bud na halere nebo na desetniky
        $decimal = 1;
        switch ($direction) {
            case 'up':
                $price = $price->ceil($decimal);
                break;
            case 'down':
                $price = $price->floor($decimal);
                break;
            case 'math':
                $price = $price->round($decimal);
                break;
        }
    }

    if ($bata) {
        $price = $price->sub($bata);
    }

    if ($original_price->isNegative() && $price->isPositive()) {
        $price = $price->additiveInverse();
    }

    if ($price->isZero() && !$original_price->isZero()) {
        if ($roundTo > 0) {
            $price = toDecimal($roundTo / 100);
        } else {
            $price = toDecimal(-$roundTo);
        }
    }

    return $price->value($decimal);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
if (!function_exists('getVat')) {
    function getVat(&$id = null)
    {
        $vatContext = Contexts::get(VatContext::class);

        if (is_null($id)) {
            $vat = $vatContext->getDefault();
            $id = $vat['id'];
        } else {
            $vat = $vatContext->getVat($id);
        }

        return $vatContext->translate($vat['vat'] ?? 0);
    }
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

if (!function_exists('getAdminVat')) {
    /**
     * @return array ['value' => 21, 'id' => 1]
     */
    function getAdminVat(): array
    {
        static $defaultVat;
        if (!isset($defaultVat)) {
            $qb = sqlQueryBuilder()->select('id, vat')->from('vats')
                ->where("is_default='Y'");
            if (findModule(Modules::OSS_VATS)) {
                $qb->andWhere('automanaged=0')
                    ->orderBy('id_country');
            }
            $vatRow = $qb->setMaxResults(1)->execute()->fetch();
            $defaultVat = [
                'value' => $vatRow ? $vatRow['vat'] : 21,
                'id' => $vatRow ? $vatRow['id'] : null,
            ];
        }

        return $defaultVat;
    }
}

/**
 * @return \KupShop\KupShopBundle\Util\Logging\SentryLogger
 *
 * @deprecated Use \Sentry\captureException() directly
 */
function getRaven()
{
    static $client = null;
    if (!$client) {
        $client = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Logging\SentryLogger::class);
    }

    return $client;
}

/**
 * For debug logging only!
 */
function getLogger(): Psr\Log\LoggerInterface
{
    static $logger = null;
    if (!$logger) {
        $logger = ServiceContainer::getService('logger');
    }

    return $logger;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function error_handler($errno, $errstr, $errfile, $errline, $errctx = null)
{
    if (!($errno & error_reporting())) {
        return;
    }

    if (isDevelopment()) {
        return false;
    }

    if (isDebug()) {
        // debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
        exit("{$errfile}:{$errline} - Error: {$errstr}");
    }

    // disable logging into kibana - sentry shoul be used
    // logError($errfile, $errline, "Error: {$errstr}", false);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

function exception_handler($exception, $event_id = null)
{
    http_response_code(500);
    if (isSuperuser()) {
        echo $exception->getTraceAsString();
        exit("\n".$exception->getFile().':'.$exception->getLine().' - '.$exception->getMessage());
    }

    global $cfg;
    if (getVal('SMARTY_DEBUG') || isDebug() || !empty($cfg['development'])) {
        exit('Uncaught exception: '.$exception->getMessage());
    }

    if (!$event_id) {
        $raven = getRaven();
        $event_id = (string) $raven->captureException($exception);
    }

    logError('Exception', 0, "Uncaught exception: Event id: {$event_id}: {$exception->getMessage()}", false);

    $smarty = createSmarty();
    $smarty->assign([
        'exception' => $exception,
        'event_id' => $event_id,
        'ctrl' => &$GLOBALS['ctrl'],
    ]);

    $smarty->display('error500.tpl');
}

if (!isLocalDevelopment() && php_sapi_name() != 'cli') {
    // Production
    if (!findModule('components')) {
        set_exception_handler('exception_handler');
    }
} else {
    // Local dev
    ini_set('display_errors', 1);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// funkce zjistuje aktualni cas provadeni skriptu
function getScriptTime()
{
    return microtime(true);
}

/**
 * @deprecated Unused, delete
 */
function measureTime($name)
{
    static $time = null;

    if (!$time) {
        $time = [$name, microtime(true)];

        return;
    }

    wpj_debug("Time measurement '{$time[0]}: ".((microtime(true) - $time[1]) * 1000).'ms');

    $time = [$name, microtime(true)];
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// funkce definuje
/**
 * @deprecated Useless, delete
 */
function setStartTime()
{
    if (!defined('SCRIPT_START_TIME')) {
        define('SCRIPT_START_TIME', getScriptTime());
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// funkce kontroluje cas provadeni skriptu a kdyz tak prodlouzi time limit
/**
 * @deprecated Useless, delete
 */
function controlTimeLimit()
{
    if (!defined('SCRIPT_START_TIME')) {
        return;
    }

    if ((getScriptTime() - SCRIPT_START_TIME) >= (ini_get('max_execution_time') - 1)) {
        @set_time_limit(ini_get('max_execution_time') + 5);
        // echo ini_get('max_execution_time')."<br>";
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Do not use, type table name directly
 */
function getTableName($name, $quote = true)
{
    $old_mapping = [
        'photos-products' => 'photos_products_relation',
        'products-related' => 'products_related',
        'products-favorites' => 'products_favorites',
        'products-sections' => 'products_in_sections',
        'photos_articles' => 'photos_articles_relation',
    ];

    if (array_key_exists($name, $old_mapping)) {
        return $old_mapping[$name];
    }

    if (strstr($name, '.') !== false) {
        return $name;
    }

    return ($quote == true) ? '`'.$name.'`' : $name;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

// funkce na monitorovani chyb v systemu
// $file = soubor, $line = radka, $text = pridavny text chyby,
// $repeatMsg = zda opakovat chybove hlaseni, nebo staci jen jednou
/**
 * @deprecated Use logger directly
 */
function logError($file, $line, $text, $repeatMsg = true)
{
    static $logger = null;

    $logger = $logger ?? ServiceContainer::getService('logger');
    $logger->error($text, ['file' => $file, 'line' => $line]);

    return;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param null $submodule
 * @param null $default
 */
function findModule($module, $submodule = null, $default = null)
{
    $array = &$GLOBALS['cfg']['Modules'];
    if (!empty($array[$module])) {
        if (is_null($submodule)) {
            return true;
        }

        return getVal($submodule, $array[$module], $default);
    } elseif (array_search($module, $array, true) === false) {
        if ($default === null) {
            return false;
        }

        return $default;
    } else {
        if (!is_null($default)) {
            return $default;
        }

        return is_null($submodule);
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use built-in Smarty template loading mechanism
 */
function findTemplate($file, $dir = 'null')
{
    return $file;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use getImage with prefetched photo id, update_date and description
 */
function leadImage($IDa, $imgType = 1, $type = 'PRODUCT')
{
    $ret = null;

    switch ($type) {
        case 'PRODUCT':
            $qb = sqlQueryBuilder()
                ->select('ph.id, ph.descr, ph.source, ph.image_2, ph.date_update')
                ->from('photos_products_relation', 'ppr')
                ->leftJoin('ppr', 'photos', 'ph', 'ppr.id_photo = ph.id')
                ->where(Translation::coalesceTranslatedFields(PhotosTranslation::class))
                ->andWhere('ppr.id_product=:id_product AND ppr.show_in_lead="Y" AND ppr.active="Y"')
                ->setParameter('id_product', $IDa)
                ->setMaxResults(1);
            break;
        case 'ARTICLE':
            $qb = sqlQueryBuilder()
                ->select('ph.id, ph.descr, ph.source, ph.image_2, ph.date_update')
                ->from('photos_articles_relation', 'pa')
                ->leftJoin('pa', 'photos', 'ph', 'pa.id_photo = ph.id')
                ->where(Translation::coalesceTranslatedFields(PhotosTranslation::class))
                ->andWhere('pa.id_art=:id_art AND pa.show_in_lead="Y" AND pa.active="Y"')
                ->setParameter('id_art', $IDa)
                ->setMaxResults(1);
            break;
    }

    if ($photo = $qb->execute()->fetchAssociative()) {
        $ret = getImage($photo['id'], $photo['image_2'], $photo['source'], $imgType, $photo['descr'], strtotime($photo['date_update']));
    }

    return $ret;
}

function quickLoadImage($ID, $size, $photoId)
{
    if (empty($photoId) || ($image = getImage($photoId, null, null, $size)) === false) {
        $image = leadImage($ID, $size);
    }

    return $image;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function productCampaign($field, &$codes = null)
{
    global $cfg;

    $campaign = [];

    $explodedCampaign = explodeFlags($field);

    foreach (getCampaigns() as $flag => $name) {
        if (!empty($explodedCampaign[$flag])) {
            $campaign[] = $name['singular'];
            $codes[$flag] = $name;
        }
    }

    return join(' + ', $campaign);
}

function getCampaigns()
{
    $cfg = Config::get();

    $languageContext = Contexts::get(LanguageContext::class);

    if ($languageContext->translationActive()) {
        $translator = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Locale\PHPArrayTranslator::class);

        $campaignTranslations = [];
        $translator->loadTranslations($campaignTranslations, 'campaigns', false, $languageContext->getActiveId());

        if (isset($campaignTranslations['campaigns'])) {
            return array_replace_recursive($cfg['Products']['Flags'], $campaignTranslations['campaigns']);
        }
    }

    return $cfg['Products']['Flags'];
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function getCategoryFullPath($IDCat)
{
    $cat_parts = getReturnNavigation($IDCat);

    $category = '';

    if (count($cat_parts) > 0) {
        $category = join(' / ', array_map(function ($x) {
            return $x['text'];
        }, array_slice($cat_parts[0], 1)));
    }

    return $category;
}

function getCategoryUrl($IDCat, $campaign = '', $producer = '')
{
    $cat_parts = getReturnNavigation($IDCat, 'ESHOP_CATEGORY', [], $campaign, $producer);

    if (count($cat_parts) > 0) {
        $parents = [];

        foreach ($cat_parts[0] as $part) {
            if (!empty($part['ID'])) {
                $parents[$part['ID']] = $part['text'];
            }
        }

        return createScriptURL([
            's' => 'category',
            'IDcat' => $campaign.$IDCat,
            'IDpd' => $producer,
            'parents' => $parents,
            'TITLE' => $cat_parts[0][count($cat_parts[0]) - 1]['text'],
        ]);
    }

    return null;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function getReturnNavigation($IDcat, $type = 'ESHOP_CATEGORY', $linkplus = [], $catCampaign = '', $catProducer = '')
{
    $ret_array = [];
    $IDcat = intval($IDcat);

    if ($IDcat > 0) {
        switch ($type) {
            case 'ESHOP_CATEGORY':
                $section = ServiceContainer::getService(SectionTree::class)->get()->getSectionById($IDcat);
                if (isset($section)) {
                    $retRow_array = [];

                    // load producer name
                    $catProducerTitle = '';
                    if (findModule(\Modules::PRODUCERS) && !empty($catProducer)) {
                        $qb = sqlQueryBuilder()->from('producers', 'pr')
                            ->andWhere(\Query\Operator::equals(['pr.id' => $catProducer]))
                            ->setMaxResults(1);
                        if (findModule(\Modules::TRANSLATIONS)) {
                            $qb->andWhere(
                                \Query\Translation::coalesceTranslatedFields(
                                    \KupShop\I18nBundle\Translations\ProducersTranslation::class,
                                    ['name']
                                )
                            );
                        } else {
                            $qb->select('name');
                        }
                        $catProducerTitle = $qb->execute()->fetchColumn();
                    }

                    // add ancestor sections
                    foreach ($section->getParents() as $parent) {
                        if ($parent->getId() === $section->getId()) {
                            // add active section
                            array_push($retRow_array, [
                                'text' => $section->getNameShort() ? $section->getNameShort() : $section->getName(),
                                'ID' => $parent->getId(),
                            ]);
                        } else {
                            array_push($retRow_array, [
                                'text' => $parent->getNameShort(),
                                'link' => createScriptURL([
                                    'URL' => 'launch.php',
                                    's' => 'category',
                                    'IDcat' => $parent->getId().$catCampaign,
                                    'IDpd' => $catProducer,
                                    'producerTitle' => $catProducerTitle,
                                    'TITLE' => $parent->getNameShort(),
                                    'parents' => $parent->getParentIDs(),
                                ]),
                                'ID' => $parent->getId(),
                            ]);
                        }
                    }

                    // add producer
                    if (!empty($catProducerTitle)) {
                        $home = sprintf(translate('getSections', 'functions')['catalogProducer'], $catProducerTitle);

                        array_unshift($retRow_array, [
                            'text' => $home,
                            'link' => createScriptURL([
                                'URL' => 'launch.php',
                                's' => 'category',
                                'IDpd' => $catProducer,
                                'producerTitle' => $catProducerTitle,
                                'TITLE' => $home,
                            ]),
                            'ID' => '0',
                        ]);
                    }

                    array_unshift($retRow_array, [
                        'text' => translate('getSections', 'functions')['home'],
                        'link' => createScriptURL([
                            'URL' => 'launch.php',
                            's' => 'index',
                        ]),
                    ]);

                    array_push($ret_array, $retRow_array);
                }
                break;

            case 'PRODUCT':
                $retRow_array = [];

                $section = ServiceContainer::getService(SectionTree::class)->get()->getSectionById($IDcat);
                if (isset($section)) {
                    foreach ($section->getParents() as $parent) {
                        array_push($retRow_array, [
                            'text' => $parent->getNameShort(),
                            'section_name' => $parent->getName(),
                            'link' => createScriptURL([
                                'URL' => 'launch.php',
                                's' => 'category',
                                'IDcat' => $parent->getId().$catCampaign,
                                'IDpd' => $catProducer,
                                'TITLE' => $parent->getNameShort(),
                                'parents' => $parent->getParentIDs(),
                            ]),
                            'ID' => $parent->getId(),
                        ]);
                    }
                }

                array_unshift($retRow_array, [
                    'text' => translate('getSections', 'functions')['home'],
                    'link' => createScriptURL([
                        'URL' => 'launch.php',
                        's' => 'index',
                    ]),
                ]);

                array_push($ret_array, $retRow_array);
                break;

            case 'ARTICLE':
                $ret_array[0] = [];

                if (findModule(\Modules::ARTICLES_SECTIONS)) {
                    $branchID = $IDcat;
                    while (!empty($branchID)) {
                        $qb = sqlQueryBuilder()->select('ab.id, ab.top_branch')
                            ->from('articles_branches', 'ab')
                            ->andWhere(\Query\Operator::equals(['ab.id' => $branchID]))
                            ->andWhere(\Query\Translation::coalesceTranslatedFields(ArticlesSectionsTranslation::class, ['name']))
                            ->setMaxResults(1);
                        $qb->andWhere(Translation::joinTranslatedFields(ArticlesSectionsTranslation::class,
                            function (QueryBuilder $qb, $columnName, $translatedField) {
                                $qb->andWhere(Operator::coalesce($translatedField, 'ab.figure').' = "Y" ');

                                return false;
                            }, ['figure']));
                        $Row = $qb->execute()->fetch();

                        if ($Row) {
                            $branchID = $Row['top_branch'];
                            $Name = $Row['name'];
                            $IDCatCurr = $Row['id'];

                            $url = createScriptURL([
                                'URL' => 'launch.php',
                                's' => 'articles',
                                'IDb' => $IDCatCurr,
                                'TITLE' => $Name,
                            ]);
                            array_unshift($ret_array[0], [
                                'text' => $Name,
                                'link' => $url,
                                'IDb' => $IDCatCurr,
                            ]);
                        } else {
                            break;
                        }
                    }
                } else {
                    $url = createScriptURL([
                        'URL' => 'launch.php',
                        's' => 'articles',
                    ]);
                    array_unshift($ret_array[0], [
                        'text' => translate('getSections', 'functions')['articles'],
                        'link' => $url,
                    ]);
                }

                $url = createScriptURL([
                    'URL' => 'launch.php',
                    's' => 'index',
                ]);
                array_unshift($ret_array[0],
                    [
                        'text' => translate('getSections', 'functions')['home'],
                        'link' => $url,
                    ]/*,
               [
                  "text"=>translate('getSections', 'functions')['articles'],
                  "link"=>$url . "clanky/"
               ]*/
                );
                break;

            case 'ARTICLES_BRANCHES':
                $ret_array[0] = [];

                $i = 0;
                while (!empty($IDcat)) {
                    $qb = sqlQueryBuilder()->select('ab.id, ab.top_branch')
                        ->from('articles_branches', 'ab')
                        ->andWhere(\Query\Operator::equals(['ab.id' => $IDcat]))
                        ->andWhere(\Query\Translation::coalesceTranslatedFields(ArticlesSectionsTranslation::class, ['name']))
                        ->setMaxResults(1);
                    $qb->andWhere(Translation::joinTranslatedFields(ArticlesSectionsTranslation::class,
                        function (QueryBuilder $qb, $columnName, $translatedField) {
                            $qb->andWhere(Operator::coalesce($translatedField, 'ab.figure').' = "Y" ');

                            return false;
                        }, ['figure']));
                    $Row = $qb->execute()->fetch();

                    if ($Row) {
                        if ($i > 0) {
                            $url = createScriptURL([
                                'URL' => 'launch.php',
                                's' => 'articles',
                                'IDb' => $IDcat,
                                'TITLE' => $Row['name'],
                            ]);
                            array_unshift($ret_array[0], [
                                'text' => $Row['name'],
                                'link' => $url,
                            ]);
                        } else {
                            array_unshift($ret_array[0], [
                                'text' => $Row['name'],
                            ]);
                        }

                        $IDcat = $Row['top_branch'];
                    } else {
                        break;
                    }
                    $i++;
                }

                $url = createScriptURL([
                    'URL' => 'launch.php',
                    's' => 'index',
                ]);
                array_unshift(
                    $ret_array[0],
                    ['text' => translate('getSections', 'functions')['home'], 'link' => $url]/*,
               [ "text"=>translate('getSections', 'functions')['articles']]*/
                );
                break;

            case 'PAGE':
                $id_link = sqlQueryBuilder()->select('parent')
                    ->from('menu_links')
                    ->where(Operator::equals(['id' => $IDcat]))
                    ->orderBy('parent', 'ASC')
                    ->execute()->fetchColumn();

                $ret_array[0] = [];
                while ($id_link > 0) {
                    $qb = sqlQueryBuilder()->select('ml.id, ml.parent, ml.name, ml.url')
                        ->from('menu_links', 'ml')
                        ->andWhere(\Query\Operator::equals(['ml.id' => $id_link]))
                        ->andWhere(Operator::equals(['ml.type' => MenuUtil::TYPE_PAGE]))
                        ->andWhere(\Query\Translation::coalesceTranslatedFields(\KupShop\I18nBundle\Translations\MenuLinksTranslation::class, ['name', 'url']))
                        ->setMaxResults(1);
                    $row = $qb->execute()->fetch();

                    if ($row) {
                        if ($id_link == $row['parent']) {
                            break;
                        }

                        $id_link = $row['parent'];

                        array_unshift($ret_array[0], [
                            'text' => $row['name'],
                            'link' => '/'.ltrim($row['url']),
                        ]);
                    } else {
                        break;
                    }
                }

                $url = createScriptURL([
                    'URL' => 'launch.php',
                    's' => 'index',
                ]);

                array_unshift($ret_array[0], [
                    'text' => translate('getSections', 'functions')['home'],
                    'link' => $url,
                ]);
                break;
        }
    } else {
        // IDcat = 0
        switch ($type) {
            case 'ESHOP_CATEGORY':
                $ret_array[0] = [];
                $catProducerTitle = '';
                if (!empty($catProducer)) {
                    $catProducerTitle = returnSQLResult('SELECT name
                        FROM '.getTableName('producers').'
                        WHERE id='.intval($catProducer).' LIMIT 1');
                }

                if (!empty($catProducer)) {
                    $home = sprintf(translate('getSections', 'functions')['catalogProducer'], $catProducerTitle);
                } elseif ($catCampaign) {
                    $titles = translate('title', 'category');
                    switch ($catCampaign) {
                        case 'F':
                            $home = $titles['favorites'];
                            break;
                        case 'W':
                            $home = $titles['watchdog'];
                            break;
                        case 'N':
                            $home = $titles['news'];
                            break;
                        case 'A':
                            $home = $titles['sale'];
                            break;
                        case 'D':
                            $home = $titles['campaign'];
                            break;
                        case 'M':
                            $home = $titles['newsletter'];
                            break;
                    }
                } else {
                    $home = translate('getSections', 'functions')['catalog'];
                }

                if (!empty($home)) {
                    array_unshift($ret_array[0], [
                        'text' => $home,
                    ]);
                }

                array_unshift($ret_array[0], [
                    'text' => translate('getSections', 'functions')['home'],
                    'link' => createScriptURL([
                        'URL' => 'launch.php',
                        's' => 'index',
                    ]),
                ]);
                break;

            case 'ARTICLES_BRANCHES':
                $ret_array[0] = [];

                array_unshift($ret_array[0],
                    ['text' => translate('getSections', 'functions')['home'], 'link' => '/'],
                    ['text' => translate('getSections', 'functions')['articles']]
                );
                break;

            case 'USER':
                $ret_array[0] = [];

                $url = path('account');
                array_unshift($ret_array[0], [
                    'text' => translate('title', 'account'),
                    'link' => $url,
                ]);
                $url = createScriptURL([
                    'URL' => 'launch.php',
                    's' => 'index',
                ]);
                array_unshift($ret_array[0], [
                    'text' => translate('getSections', 'functions')['home'],
                    'link' => $url,
                ]);
                break;

            case 'RECLAMATION':
                array_unshift($linkplus, [
                    'text' => translate('reclamations', 'reclamations'),
                    'link' => path('kupshop_reclamations_reclamations_reclamations'),
                ]);

                if (\User::getCurrentUserId()) {
                    return getReturnNavigation(-1, 'USER', $linkplus);
                } else {
                    return getReturnNavigation(-1, 'NO_TYPE', $linkplus);
                }

                // no break
            case 'RETURN':
                array_unshift($linkplus, [
                    'text' => translate('returns_title', 'returns'),
                    'link' => path('kupshop_returns_returns_returns'),
                ]);

                if (\User::getCurrentUserId()) {
                    return getReturnNavigation(-1, 'USER', $linkplus);
                } else {
                    return getReturnNavigation(-1, 'NO_TYPE', $linkplus);
                }

                // no break
            case 'NO_TYPE':
                $ret_array[0] = [];

                $url = createScriptURL([
                    'URL' => 'launch.php',
                    's' => 'index',
                ]);
                array_unshift($ret_array[0], [
                    'text' => translate('getSections', 'functions')['home'],
                    'link' => $url,
                ]);
                break;
        }
    }

    if ($type != 'ESHOP_CATEGORY' && $linkplus) {
        foreach ($linkplus as $link) {
            if (!is_array($link)) {
                $link = ['text' => $link];
            }

            $ret_array[0][] = $link;
        }
    }

    return $ret_array;
}

/**
 * @deprecated Use Symfony Request, getClientIp method
 */
function getIP()
{
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) { // if from shared
        return $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {   // if from a proxy
        return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }

    return $_SERVER['REMOTE_ADDR'] ?? '';
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function stripTags($String, $Convert = false, $NL = '\r\n')
{
    if ($Convert) {
        $String = preg_replace("@((<br( /)?>)|<\/p>)@i", $NL, $String);
    }
    $String = preg_replace('/(<[^>]*>)/i', ' ', $String);
    if ($Convert) {
        $String = str_replace('&nbsp;', ' ', $String);
        $String = str_replace('&quot;', '"', $String);
        $String = str_replace('&gt;', '>', $String);
        $String = str_replace('&lt;', '<', $String);
        $String = str_replace('&amp;', '&', $String);
    }
    $String = preg_replace('/( )+/i', ' ', $String);
    $String = trim($String);

    return $String;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated in favour of \KupShop\KupShopBundle\Util\StringUtil::normalizeWhitespace()
 *
 * @return string|string[]|null
 */
function CutString($String)
{
    $String = trim($String);
    $String = preg_replace("/[\t\r\n]/i", '', $String);
    $String = preg_replace('/( )+/i', ' ', $String);

    return $String;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// vybere polozku ze selectoveho menu
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function CheckSelect($Value1, $Value2)
{
    if ($Value1 == $Value2) {
        return ' selected="selected"';
    } else {
        return '';
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// zaskrtne checkbox
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function CheckRadio($Value1, $Value2)
{
    if ($Value1 == $Value2) {
        return ' checked="checked"';
    } else {
        return '';
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function setCookies($Name, $Value, $Expiry = 2592000, $options = [])
{
    if (isFunctionalTests()) {
        return true;
    }

    $time = null;

    if ($Expiry < 0) {  // Remove
        $Value = '';
        unset($_COOKIE[$Name]);
    } else {
        $_COOKIE[$Name] = $Value;
    }

    if (!is_null($Expiry)) {
        $time = time() + $Expiry;
    }

    setcookie($Name, $Value, array_merge([
        'expires' => $time,
        'path' => '/',
        'secure' => true,
        'samesite' => 'Lax',
    ], $options));
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Unused, delete
 */
function ShortText($Text, $Reduce)
{
    $Word = preg_split('/[[:blank:]]+/', $Text);
    $NoWords = count($Word);
    $Length = 0;
    $RtrnText = '';

    for ($i = 0; $i < $NoWords; $i++) {
        $RtrnText .= $Word[$i].' ';
        $Length = strlen($RtrnText);
        if ($Length + 3 + strlen($Word[$i]) >= $Reduce) {
            break;
        }
    }
    if (strlen($RtrnText) >= $Reduce) {
        $RtrnText = substr($RtrnText, 0, $Reduce - 3);
    }
    if (strlen($Text) > strlen($RtrnText)) {
        $RtrnText .= '...';
    }

    return wordwrap($RtrnText, 60, "\n", 1);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Unused, delete
 */
function getInputsFromString($String, $ex1, $ex2, $except = [])
{
    $return = '';
    $Input = explode($ex1, $String);
    for ($i = 0; $i < count($Input); $i++) {
        if ($Input[$i] != '') {
            $data = explode($ex2, $Input[$i]);
            if (array_search($data[0], $except) === false) {
                $return .= '<input type="hidden" name="'.$data[0].'" value="'.$data[1].'" />';
            }
        }
    }

    return $return;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// FUNKCE: 	pridava vytvari URL odkazu bud klasickou nebo SEO friendly
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
if (!function_exists('createScriptURL')) {
    function createScriptURL($params)
    {
        /** @var $legacyUrlGenerator \KupShop\KupShopBundle\Util\LegacyUrlGenerator */
        $legacyUrlGenerator = ServiceContainer::getService(\KupShop\KupShopBundle\Util\LegacyUrlGenerator::class);

        return $legacyUrlGenerator->generate($params);
    }
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// FUNKCE: 	pridava do vytvarene URL dalsi parametry
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function createScriptURL_Next_Params($SEO_URL, $params)
{
    if (count($params) == 0) {
        return $SEO_URL;
    }

    // zda se ma URL Encodovat
    $ENCODE_URL = !empty($params['ESCAPE']) && ($params['ESCAPE'] != 'NO');

    unset($params['ESCAPE']);
    $QS = '';

    foreach ($params as $param => $value) {
        if ($param != 'URL' && $param != 'TITLE') {
            if ($ENCODE_URL) {
                $value = urlencode($value);
            }
            $QS .= '&'.$param.'='.$value;
        }
    }

    if ($QS != '') {
        if (strpos($SEO_URL, '?') === false) {
            $SEO_URL .= '?';
            $QS = substr($QS, 1);
        }

        if ($ENCODE_URL) {
            $QS = htmlspecialchars($QS);
        }
        $SEO_URL .= $QS;
    }

    return $SEO_URL;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// FUNKCE: 	vytvari ze stringu SEO friendly URL
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use StringUtil::slugify
 */
function createScriptURL_Text($string)
{
    return \KupShop\KupShopBundle\Util\StringUtil::slugify($string);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Do nothing
 */
function sqlConnect()
{
    return true;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param array $params
 * @param array $types
 *
 * @return \Doctrine\DBAL\Statement
 */
function sqlQuery($Query, $params = '', $types = [])
{
    if (is_array($params)) {
        return sqlQueryParams($Query, $params, $types);
    }

    wpj_debug($Query);

    $stmt = sqlGetConnection()->query($Query);

    return $stmt;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function sqlQueryParams($Query, $params = [], $types = [])
{
    wpj_debug([$Query, $params]);

    $stmt = sqlGetConnection()->executeQuery($Query, $params, $types);

    return $stmt;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Unused, delete
 */
function sqlError($stmt, $logError = true, $printError = true, $exit = true, $msg = '')
{
    // jinak chybu aspon zalogovat
    if ($logError) {
        $error = 'MySQL Error: '.print_r($stmt->errorInfo(), true);
        if ($msg != '') {
            $error .= "\r\nSQL: ".$msg;
        }
        // $error .= print_r(debug_backtrace(), true);
        logError(__FILE__, __LINE__, $error);
    }
    // vypsat hlasku a ukoncit
    if ($printError) {
        echo "<i><b>Database Error: </b></i><pre>{$error}</pre>";
    }
    // ukoncit skript
    if ($exit) {
        exit;
    }

    return true;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function sqlPrepare($Query)
{
    wpj_debug([$Query]);

    $stmt = sqlGetConnection()->prepare($Query);

    return $stmt;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function sqlExecute($stmt, $params)
{
    wpj_debug([$stmt, $params]);
    $stmt->execute($params);

    return $stmt;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param $stmt \Doctrine\DBAL\Statement
 *
 * @return int
 */
function sqlNumRows($stmt)
{
    if (!$stmt) {
        return 0;
    }

    return $stmt->rowCount();
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Unused, delete
 */
function sqlNumFields($stmt)
{
    if (($user = getAdminUser()) && $user['id'] === 0) {
        exit('<br><strong>Použití sqlNumFields. Fuj!:</strong> '.print_r(debug_backtrace(), true));
    }

    return 0;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param $stmt \Doctrine\DBAL\Statement
 */
function sqlFetchArray($stmt)
{
    return $stmt->fetch(PDO::FETCH_BOTH);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param $stmt \Doctrine\DBAL\Statement
 */
function sqlFetch($stmt)
{
    return $stmt->fetch(PDO::FETCH_ASSOC);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param $stmt \Doctrine\DBAL\Statement
 *
 * @deprecated
 */
function sqlFetchRow($stmt)
{
    return $stmt->fetch(PDO::FETCH_NUM);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param $stmt \Doctrine\DBAL\Statement
 */
function sqlFetchAssoc($stmt)
{
    return $stmt->fetch(PDO::FETCH_ASSOC);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function sqlFetchAll($stmt, $field = null, $deduplicate = false)
{
    if ($stmt instanceof \Doctrine\DBAL\Query\QueryBuilder) {
        $stmt = $stmt->execute();
    }

    $arr = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if ($field) {
        $values = $arr;
        if (is_array($field)) { // Specify field for both key and value
            $value = array_values($field)[0];
            $field = array_keys($field)[0];

            // Filter out only given value instead of whole array
            $values = array_map(function ($row) use ($value) {
                return $row[$value];
            }, $values);
        }

        return array_combine(array_map(function ($row) use ($field) {
            return $row[$field];
        }, $arr), $values);
    }

    return $arr;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// formatuje vstup do SQL prikazu, zabraneni SQL Injection
/**
 * @deprecated Use parameter binding
 */
function sqlFormatInput($input)
{
    return $stmt = substr(sqlGetConnection()->quote($input), 1, -1);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Do nothing, delete
 */
function sqlResult($ID, $i, $field)
{
    if (($user = getAdminUser()) && $user['id'] === 0) {
        exit('<br><strong>Použití sqlResult. Fuj!:</strong> '.print_r(debug_backtrace(), true));
    }

    return null;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Unused, delete
 */
function sqlFieldName($ID, $i)
{
    if (($user = getAdminUser()) && $user['id'] === 0) {
        exit('<br><strong>Použití sqlFieldName. Fuj!:</strong> '.print_r(debug_backtrace(), true));
    }

    return null;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function sqlInsertId()
{
    return sqlGetConnection()->lastInsertId();
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Do nothing, delete
 */
function sqlFreeResult($stmt)
{
    unset($stmt);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function returnSQLResult($Query, $params = null, $types = [])
{
    if ($params) {
        $SQL = sqlQueryParams($Query, $params, $types);
    } else {
        $SQL = sqlQuery($Query);
    }

    $row = sqlFetchArray($SQL);
    sqlFreeResult($SQL);

    if ($row && count($row) > 0) {
        return $row[0];
    } else {
        return false;
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Do nothing, delete
 */
function sqlClose()
{
    sqlGetConnection()->close();
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use sqlGetConnection()->transactional()
 */
function sqlStartTransaction()
{
    sqlGetConnection()->beginTransaction();
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use sqlGetConnection()->transactional()
 */
function sqlFinishTransaction()
{
    sqlGetConnection()->commit();
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @return bool|void
 *
 * @deprecated Use sqlGetConnection()->transactional()
 *
 * @throws \Doctrine\DBAL\ConnectionException
 */
function sqlRollbackTransaction()
{
    sqlGetConnection()->rollBack();
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function sqlAffectedRows($stmt)
{
    return sqlNumRows($stmt);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @return \Doctrine\DBAL\Connection
 */
function sqlGetConnection()
{
    return ServiceContainer::getService('doctrine.dbal.default_connection');
}

/**
 * @return \Query\QueryBuilder
 */
function sqlQueryBuilder()
{
    return ServiceContainer::getService(\Query\QueryBuilder::class);
}

define('HEAD_CRLF', "\r\n");

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use Mail classes
 */
function SendMail($From, $For, $Subject, $Message, $contentType = null, $attachments = null, $cc = null, $bcc = null, $headers = [], $utm_campaign = null)
{
    $emailSender = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Mail\EmailSender::class);

    try {
        $msg = $emailSender->createMessage($From, $For, $Subject, $Message, $contentType, $attachments, $cc, $bcc, $headers, $utm_campaign);
    } catch (Exception $e) {
        getRaven()->captureException($e);

        return false;
    }

    return $emailSender->sendMessage($msg);
}

function getImagePath($id, $type, $ext = 'jpg', $lang = null, ?string $version = null)
{
    static $imageLocator = null;
    if (!$imageLocator) {
        $imageLocator = ServiceContainer::getService(ImageLocator::class);
    }

    return $imageLocator->getPath($id, $type, $ext, $lang, $version);
}

function getImageSettings(&$type)
{
    static $imageLocator = null;
    if (!$imageLocator) {
        $imageLocator = ServiceContainer::getService(ImageLocator::class);
    }

    return $imageLocator->getTypeConfig($type);
}

function getImage($id, $file, $folder, $type, $desc = '', ?int $dateUpdate = null, $data = null)
{
    static $imageLocator = null;
    if (!$imageLocator) {
        $imageLocator = ServiceContainer::getService(ImageLocator::class);
    }

    return $imageLocator->getOldImageArray($id, $file, $folder, $type, $desc, $dateUpdate, $data);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function wpj_debug($string, $force = false)
{
    static $used = false;

    if (isset($_GET['wpj_debug']) || $force) {
        if (!$used) {
            echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
        }

        if (!is_string($string)) {
            $string = var_export($string, true);
        }

        echo "<!-- {$string} -->\n";
        $used = true;
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

function getProductDeliveryText(&$inStore, &$deliveryTime)
{
    $cfg = Config::get();
    $dbcfg = Settings::getDefault();

    if ($inStore > 0) {
        $deliveryTime = 0;
    }

    if ($inStore <= 0 && $deliveryTime == 0 && $dbcfg->prod_subtract_from_store != 'N') {
        $deliveryTime = (isset($cfg['Products']['SoldOutIndex']) ? $cfg['Products']['SoldOutIndex'] : -1);
    }

    $inStore = max($inStore, 0);

    $languageContext = Contexts::get(LanguageContext::class);
    if ($deliveryTime <= 0) {
        if (!$languageContext->translationActive()) {
            return $cfg['Products']['DeliveryTime'][$deliveryTime] ?? '';
        } else {
            return translate_shop($deliveryTime, 'deliveryTime');
        }
    } else {
        if (!$languageContext->translationActive()) {
            return sprintf($cfg['Products']['DeliveryTime']['1'] ?? '', strval($deliveryTime));
        } else {
            return sprintf(translate('1', 'deliveryTime'), strval($deliveryTime));
        }
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

function getStatuses($status = null)
{
    global $cfg;

    // backward compatibility
    if ($status === null) {
        $status = 'handled';
        $sentryLogger = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Logging\SentryLogger::class);
        $sentryLogger->captureMessage('Function getStatuses() called with missing parameter "$status"', [], [], true);
    }

    switch ($status) {
        case 'complete':
            if (isset($cfg['Order']['Status']['complete'])) {
                return $cfg['Order']['Status']['complete'];
            } else {
                return [0, 1];
            }

            // no break
        case 'notpacked':
            if (isset($cfg['Order']['Status']['notpacked'])) {
                return [...$cfg['Order']['Status']['notpacked'], 100];
            } else {
                return [0, 1, 100];
            }

            // no break
        case 'active':
            if (isset($cfg['Order']['Status']['active'])) {
                return $cfg['Order']['Status']['active'];
            } else {
                return [0, 1];
            }

            // no break
        case 'editable':
            if (isset($cfg['Order']['Status']['editable'])) {
                return $cfg['Order']['Status']['editable'];
            } else {
                return [0, 1];
            }

            // no break
        case 'reclamation':
            if (isset($cfg['Order']['Status']['reclamation'])) {
                return $cfg['Order']['Status']['reclamation'];
            } else {
                return [3];
            }

            // no break
        case 'nothandled':
            return array_diff(array_keys($cfg['Order']['Status']['global']), getStatuses('handled'));

        case 'handled':
            if (isset($cfg['Order']['Status']['handled'])) {
                return $cfg['Order']['Status']['handled'];
            } else {
                return [2];
            }

            // no break
        case 'invoice':
            return getVal('invoice', $cfg['Order']['Status'], []);

        case 'in_store': // Pokud je vypnuto odecitani ze skladu, v jakych stavech je produkt skladem
            return getVal('in_store', $cfg['Order']['Status'], [0]);

        case 'telfa_disallow_change_status': // Telfa pri techto stavech vraci sms, ze je obj uzavrena
            return getVal('telfa_disallow_change_status', $cfg['Order']['Status'], getStatuses('handled'));

        case 'payment_change_allowed':
            return getVal('payment_change_allowed', $cfg['Order']['Status'], [0]);

        default:
            logError(__FILE__, __LINE__, "Unknown getStatuses status: {$status}");

            return [];
    }
}

function getOrderStatus($status)
{
    return getOrderStatuses()[$status];
}

function getOrderStatuses()
{
    static $statuses = null;

    if (is_null($statuses)) {
        global $cfg;

        // $colors = ["ffffff", "d8edf6", "edf7d6", "c9f5b9", "baf0ee", "f6f6bb", "eaf4f8", "ffebfc", "c8c8c8", "fff3e6", "", "", ""];
        $colors = ['EEEEEE', '9ED2E8', 'bfec55', 'FFFF78', 'F2AEF2', 'FCCF81', 'bac8f3', 'c8c8c8', 'e4f3ba', '8cd993', '', ''];

        $info = getVal('global_info', $cfg['Order']['Status']);

        foreach ($cfg['Order']['Status']['global'] as $status => $name) {
            if (isset($info[$status]['color'])) {
                $colors[$status] = $info[$status]['color'];
            }

            $statuses[$status] = ['name' => $name, 'color' => $colors[$status] ?? ''];
        }
    }

    return $statuses;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function getMemcached()
{
    static $m = null;

    if ($m) {
        return $m;
    }

    global $cfg;

    $m = new Memcached('main_cache');
    if (!count($m->getServerList())) {
        if (!empty($cfg['Cache']['memcached'])) {
            foreach (explode(',', $cfg['Cache']['memcached']) as $memcached) {
                $m->addServer($memcached, 11211);
            }
        } else {
            $m->addServer('/var/run/memcached/memcached.socket', 0);
        }

        $m->setOptions([
            Memcached::OPT_BINARY_PROTOCOL => true,
            Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
            Memcached::OPT_PREFIX_KEY => $cfg['Cache']['prefix'],
        ]);
    }

    return $m;
}

function getRedisCache(): \Redis|\RedisCluster
{
    static $r = null;

    if (!$r) {
        $r = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Redis\RedisService::class)->connect();
    }

    return $r;
}

function getCache($key, &$found = null)
{
    if (getVal('skip_cache')) {
        return null;
    }

    $key .= getenv('BUILD_ID');

    if (findModule(Modules::COMPONENTS)) {
        $redisCache = getRedisCache();

        return $redisCache->get($key);
    }

    $m = getMemcached();
    $ret = $m->get($key);
    $found = $m->getResultCode() != Memcached::RES_NOTFOUND;

    if (is_array($ret) && array_key_exists('@@OBJ@@', $ret)) {
        return unserialize($ret['@@OBJ@@']);
    }

    return $ret;
}

function setCache($key, &$value, $ttl = 7200)
{
    $key .= getenv('BUILD_ID');

    if (findModule(Modules::COMPONENTS)) {
        $redisCache = getRedisCache();

        if (!$ttl) { // if ttl is 0, item never expires (Memcached BC)
            return $redisCache->set($key, $value);
        }

        return $redisCache->set($key, $value, ['EX' => $ttl]);
    }

    $m = getMemcached();
    if (is_object($value)) {
        return $m->set($key, ['@@OBJ@@' => serialize($value)], $ttl);
    }

    return $m->set($key, $value, $ttl);
}

function clearCache($key, $prefix = false)
{
    if (findModule(Modules::COMPONENTS)) {
        $key .= getenv('BUILD_ID');

        $redisCache = getRedisCache();

        return $redisCache->del($key);
    }

    global $cfg;

    if (!$prefix) {
        $key .= getenv('BUILD_ID');
        $m = getMemcached();
        $m->delete($key);
    } else {
        $m = new Memcached();
        if (!empty($cfg['Cache']['memcached'])) {
            foreach (explode(',', $cfg['Cache']['memcached']) as $memcached) {
                $m->addServer($memcached, 11211);
            }
        } else {
            $m->addServer('/var/run/memcached/memcached.socket', 0);
        }

        $keys = (array) $m->getAllKeys();

        $prefix = $cfg['Cache']['prefix'].$key;

        foreach ($keys as $index => $key) {
            if (strpos($key, $prefix) === 0) {
                $splitKey = explode('/', $key);
                if (isset($splitKey[1]) && \KupShop\KupShopBundle\Util\StringUtil::startsWith($splitKey[1], \KupShop\KupShopBundle\Util\Session\MemcachedSessionHandler::SESSION_PREFIX)) {
                    continue;
                }

                getMemcached()->delete(substr($key, strlen($cfg['Cache']['prefix'])));
            }
        }
    }

    return true;
}

function getLocalCache(): Symfony\Component\Cache\Adapter\AdapterInterface
{
    static $cache = null;

    if (is_null($cache)) {
        $cache = ServiceContainer::getService('cache.app');
    }

    return $cache;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

// funkce: emphasizeString
// popis:  zadany retezec prohleda a oznaci slova, ktera jsou zadana
/**
 * @deprecated Do not use
 */
function emphasizeString($text, $needle)
{
    $text = html_entity_decode($text);

    // Default highlighting
    $highlight = '<span class="checkedExpression">\1</span>';

    // Select pattern to use
    $pattern = '#(?!<.*?)(%s)(?![^<>]*?>)#';

    // Case sensitivity
    $pattern .= 'i';

    $needle = (array) $needle;
    foreach ($needle as $needle_s) {
        $needle_s = preg_quote($needle_s);

        $regex = sprintf($pattern, $needle_s);
        $text = preg_replace($regex, $highlight, $text);
    }

    return $text;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use QueryBuilder::directValues
 */
function queryCreate($fields, $directValues = false, $notNull = [])
{
    $ret = ' ';
    $first = true;
    foreach ($fields as $field => $variable) {
        if (is_numeric($field)) {
            $field = $variable;
        }
        if (!$directValues && !isset($GLOBALS[$variable])) {
            continue;
        }
        if (!$directValues) {
            $variable = $GLOBALS[$variable];
        }
        if (is_float($variable)) {
            $variable = str_replace(',', '.', $variable);
        }
        if ((is_null($variable) || $variable === '') && array_search($field, $notNull) === false) {
            $variable = 'NULL';
        } else {
            $variable = "'".sqlFormatInput($variable)."'";
        }
        $ret .= ($first ? '' : ', ')."`{$field}`={$variable} ";
        $first = false;
    }

    return $ret;
}

/**
 * @deprecated Use Query::equals or Query::equalsNullable specs
 */
function selectQueryCreate($fields, $directValues = false, $operator = 'AND', $notNull = [])
{
    $ret = ' ';
    $first = true;
    foreach ($fields as $field => $variable) {
        if (is_numeric($field)) {
            $field = $variable;
        }
        if (!$directValues && !isset($GLOBALS[$variable])) {
            continue;
        }
        if (!$directValues) {
            $variable = $GLOBALS[$variable];
        }
        if ((is_null($variable) || $variable === '') && array_search($field, $notNull) === false) {
            $ret .= ($first ? '' : " {$operator} ")."{$field} IS NULL";
        } else {
            $ret .= ($first ? '' : " {$operator} ")."{$field}='".sqlFormatInput($variable)."' ";
        }

        $first = false;
    }

    return $ret;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

/**
 * @param int $id_category
 *
 * @return array
 */
function getDescendantCategories($id_category)
{
    // Load from menu categories structure
    $category = ServiceContainer::getService(SectionTree::class)->get()->getSectionById($id_category);

    if (!$category) {
        return [$id_category];
    }

    $ids = [];
    getDescendantCategoriesFromMenu($ids, $category);

    return $ids;
}

/**
 * @deprecated Unused, delete
 */
function getDescendantCategoriesOld(&$where, $CatID)
{
    if (empty($CatID)) {
        $where .= ' 1 ';

        return;
    }

    $ids = getDescendantCategories($CatID);

    $where .= ' ps.id_section IN ('.join(',', $ids).') ';
}

/**
 * @deprecated Unused, delete
 */
function getDescendantCategoriesFromMenu(&$ids, KupShop\CatalogBundle\Entity\Section $cat)
{
    $ids[] = $cat->getId();

    foreach ($cat->getChildren() as &$category) {
        if ($category->getFigure() != 'N') {
            getDescendantCategoriesFromMenu($ids, $category);
        }
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

function getVal($value, $array = null, $default = null)
{
    if ($array !== null) {
        return isset($array[$value]) ? $array[$value] : $default;
    } else {
        return isset($_REQUEST[$value]) ? $_REQUEST[$value] : $default;
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

function generatePassword($seed, $length = 4)
{
    $pass = md5($seed);
    $pass = strtr($pass, '0123456789', 'ghijkmnspr');

    return substr($pass, 0, $length);
}

function generateCaptcha($seed)
{
    global $cfg;

    $length = 4;
    if (!empty($cfg['captcha']['length'])) {
        $length = $cfg['captcha']['length'];
    }

    return generatePassword($seed, $length);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function isAjax()
{
    return strtolower(getVal('HTTP_X_REQUESTED_WITH', $_SERVER, '')) == 'xmlhttprequest';
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @param $params string[]
 *
 * @return string
 */
function getAdminUrl($page, $params, bool $absolute = false)
{
    global $cfg;

    $admin_url = $cfg['Menu']['wpj_toolbar']['admin_url'];

    $params['s'] = $page.'.php';
    $params['acn'] = 'edit';
    $params['inline'] = '1';

    if ($absolute) {
        $domainContext = ServiceContainer::getService(DomainContext::class);
        $admin_url = "https://{$domainContext->getActiveId()}/".ltrim($admin_url, '/');
    }

    return $admin_url.'launch.php?'.http_build_query($params);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
define('POSITION_FIRST', 0);
define('POSITION_LAST', 10000);
define('POSITION_STANDARD', 5000);

function loadLanguage($category = 'main', $lng = null)
{
    global $txt_str;

    $translator = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Locale\PHPArrayTranslator::class);

    $translator->loadTranslations($txt_str, $category, isAdministration(), $lng ?? $translator->getLanguage(isAdministration()));

    return $txt_str;
}

function createSmarty($admin = false, $add_globals = false, $translate = null)
{
    $cfg = &Config::get()->getContainer();

    $translate = (findModule(\Modules::TRANSLATIONS) || findModule(\Modules::TEMPLATE_TRANSLATIONS)) && ($translate ?? (!$admin));

    if (isFunctionalTests() || $admin) {
        if ($translate) {
            // Používá se pouze pro výchozí text emailů. Negenerují se ale nikde překlady :-(
            $languageContext = Contexts::get(LanguageContext::class);
            $smarty = new SmartyML($languageContext->getDefaultId());
        } else {
            $smarty = new Smarty();
        }
        $smarty->setMergeCompiledIncludes(true);
        $smarty->setErrorReporting(E_ALL & ~(E_WARNING | E_NOTICE | E_USER_DEPRECATED));

        // Register required administration modifiers
        $smarty->registerPlugin('modifier', 'is_numeric', 'is_numeric');
        $smarty->registerPlugin('modifier', 'translate_shop', 'translate_shop');
        $smarty->registerPlugin('modifier', 'get_class', 'get_class');
        $smarty->registerPlugin('modifier', 'json_encode', 'json_encode');
        $smarty->registerPlugin('modifier', 'array_values', 'array_values');
        $smarty->registerPlugin('modifier', 'strstr', 'strstr');
        $smarty->registerPlugin('modifier', 'getShopName', 'getShopName');
        $smarty->registerPlugin('modifier', 'isSuperuser', 'isSuperuser');
        $smarty->registerPlugin('modifier', 'getAdminUser', 'getAdminUser');
        $smarty->registerPlugin('modifier', 'isLocalDevelopment', 'isLocalDevelopment');
        $smarty->registerPlugin('modifier', 'isDevelopment', 'isDevelopment');
        $smarty->registerPlugin('modifier', 'is_null', 'is_null');
        $smarty->registerPlugin('modifier', 'findModule', 'findModule');
        $smarty->registerPlugin('modifier', 'is_string', 'is_string');
        $smarty->registerPlugin('modifier', 'intval', 'intval');
        $smarty->registerPlugin('modifier', 'ltrim', 'ltrim');
        $smarty->registerPlugin('modifier', 'toDecimal', 'toDecimal');
        $smarty->registerPlugin('modifier', 'json_decode', 'json_decode');
        $smarty->registerPlugin('modifier', 'array_keys', 'array_keys');
        $smarty->registerPlugin('modifier', 'array_splice', 'array_splice');
        $smarty->registerPlugin('modifier', 'implode', 'implode');
        $smarty->registerPlugin('modifier', 'lcfirst', 'lcfirst');
        $smarty->registerPlugin('modifier', 'path', 'path');
        $smarty->registerPlugin('modifier', 'getOrderStatuses', 'getOrderStatuses');
        $smarty->registerPlugin('modifier', 'getStatuses', 'getStatuses');
        $smarty->registerPlugin('modifier', 'min', 'min');
    } else {
        if ($translate) {
            $languageContext = Contexts::get(LanguageContext::class);
            $smarty = new SmartyML($languageContext->getActiveId());
        } else {
            $smarty = new Smarty();
        }
        $smarty->setMergeCompiledIncludes(true);
        $smarty->setErrorReporting(E_ALL & ~(E_WARNING | E_NOTICE | E_STRICT | E_USER_DEPRECATED));
        $smarty->muteUndefinedOrNullWarnings();
    }

    require_once './engine/bundles/KupShop/KupShopBundle/Util/Smarty/php7_functions.php';

    // HACK: Short {t} tag does not compile well (leaves original translation in place)
    // this transforms short call to call with parameter
    // HACK: For BC use modified versions of some PHP functions that ignores errors
    $smarty->registerFilter('pre', function ($source, Smarty_Internal_Template $template) {
        $source = preg_replace('/\b(?<!\.)(in_array|array_values|array_filter|join|array_column|array_key_last|array_key_first|array_key_exists|array_keys|array_merge)\s*\(/', 'bc_$1(', $source);

        return str_replace(['{t}'], ['{t 1=1}'], $source);
    });

    $compileDir = $cfg['Path']['data'].'tmp/templates';

    if ($dir = getenv('CONTAINER_DIR')) {
        $compileDir = realpath($cfg['Path']['web_root'])."/{$dir}/templates/";
    }

    $smarty->setCompileDir($compileDir)
        ->setCacheDir($compileDir)
        ->setTemplateDir([]);

    $smarty->setCompileCheck(isProduction() ? Smarty::COMPILECHECK_OFF : Smarty::COMPILECHECK_ON);

    if ($admin) {
        $templates = [
            'admin' => $cfg['Path']['admin'].'templates/',
        ];
        $smartyPlugins = [
            '../smarty_plugins/',
        ];
    } else {
        $templates = [
            'web' => $cfg['Path']['smarty_tpl']['templates'],
        ];
        $smartyPlugins = [
            'smarty_plugins/',
        ];
    }

    if (isFunctionalTests()) {
        $templates['test'] = './engine/tests/functional/templates/';
    }

    if (!$admin && findModule(\Modules::COMPONENTS)) {
        $templates = [...$templates, 'twig_compat' => './engine/web/templates/twig_compat'];
    }

    try {
        /** @var \KupShop\KupShopBundle\Util\System\BundleFinder $bundleFinder */
        $bundleFinder = ServiceContainer::getService('kupshop.util_system.bundle_finder');

        if ($admin) {
            $templates = array_merge($templates, array_reverse($bundleFinder->getBundlesPath('Admin/templates')));
            $smartyPlugins = array_merge($smartyPlugins, Mapping::mapKeys(array_reverse($bundleFinder->getBundlesPath('Admin/smarty_plugins')), function ($k, $v) { return [$k.'_admin', $v]; }));
            $smartyPlugins = array_merge($smartyPlugins, array_reverse($bundleFinder->getBundlesPath('Resources/smarty_plugins')));
        } else {
            $templates = array_merge($templates, array_reverse($bundleFinder->getBundlesPath('Resources/templates')));
            $smartyPlugins = array_merge($smartyPlugins, array_reverse($bundleFinder->getBundlesPath('Resources/smarty_plugins')));
        }
    } catch (Exception $e) {
        // Ignore Symfony initialization errors to make less Sentry noise
    }

    if ($admin) {
        $templates['shared'] = $cfg['Path']['shared_version'].'admin/templates/';
        $smartyPlugins[] = $cfg['Path']['shared_version'].'admin/class/smarty_plugins';
        $smartyPlugins[] = $cfg['Path']['shared_version'].'class/smarty_plugins/';
    } else {
        $templates['shared'] = $cfg['Path']['shared_version']."web/templates/{$cfg['Path']['smarty_tpl']['theme']}/";
        $templates['common'] = $cfg['Path']['shared_version'].'web/common/templates/';
        $smartyPlugins[] = $cfg['Path']['shared_version'].'class/smarty_plugins/';
    }

    $smarty->addTemplateDir($templates)
        ->addPluginsDir($smartyPlugins);

    $smarty->debugging_ctrl = 'URL';
    $smarty->escape_html = true;
    $smarty->debug_tpl = 'engine/bundles/KupShop/KupShopBundle/Util/Smarty/smarty_debug.tpl';

    if (getVal('SMARTY_DEBUG')) {
        $smarty->force_compile = true;
    }

    unset($cfg['Connection']['password']);
    unset($cfg['Connection']['host']);
    unset($cfg['Connection']['user']);
    unset($cfg['Admin']['Login']['User']);
    unset($cfg['Admin']['Login']['Password']);
    unset($cfg['Admin']['Login']['MasterPassword']);

    if ($add_globals) {
        if ($translate && !$admin) {
            $cacheContext = Contexts::get(CacheContext::class);
            $cacheKey = $cacheContext->getKey([CacheContext::TYPE_TEXT]);
            $dbcfg = Settings::getFromCache($cacheKey);
        } else {
            $dbcfg = Settings::getDefault();
        }

        $configLoader = Config::get();
        $smarty->assign([
            'cfg' => &$configLoader,
            'dbcfg' => &$dbcfg,
            'ctrl' => &$GLOBALS['ctrl'],
            'module' => ModulesWrapper::getInstance(),
        ]);
    }

    return $smarty;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function translate($key, $section = null, $silent = false, $isAdministration = null)
{
    global $txt_str;

    $translator = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Locale\PHPArrayTranslator::class);

    $isAdministration = $isAdministration ?? isAdministration();
    $lang = $translator->getLanguage($isAdministration);

    static $languageTranslations = [];

    if (!array_key_exists($lang, $languageTranslations)) {
        $languageTranslations[$lang] = ($isAdministration) ? $txt_str : [];
    }

    if (empty($txt_str)) {
        $txt_str = &$languageTranslations[$lang];
    }

    return $translator->translate($languageTranslations[$lang], $key, $section, $silent, $isAdministration);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
function translate_template($key, $lang = null)
{
    if (!findModule(\Modules::TRANSLATIONS) && !findModule(\Modules::TEMPLATE_TRANSLATIONS)) {
        return $key;
    }

    if (!$lang) {
        global $cfg;
        $lang = getVal('language', $cfg['Lang'], 'czech');
    }

    // TODO: Symfony
    return \KupShop\KupShopBundle\Util\Locale\TemplateTranslator::getService()->get($lang)->gettext($key);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

function getAdminUser(bool $forceUpdate = false)
{
    static $user = null;

    if (is_null($user) || $forceUpdate) {
        global $cfg;

        $legacyAdminCredentials = ServiceContainer::getService(LegacyAdminCredentials::class);

        $user = false;

        if ($legacyAdminCredentials->isLogged()) {
            $adminID = $legacyAdminCredentials->getAdminID();
            $user = LegacyAdminCredentials::getAdminById((int) $adminID);
        }
    }

    return $user;
}

function getAdminID($superAdminValue = 0)
{
    $user = getAdminUser();
    if (getVal('superuser', $user, false)) {
        return $superAdminValue;
    }

    return $user['id'] ?? null;
}

function isSuperuser()
{
    $user = getAdminUser();

    return getVal('superuser', $user, false);
}

function addUserEmail($email, $groups = [], $fields = ['figure' => 'N'], $confirmed = false)
{
    if (empty($email)) {
        return false;
    }

    return sqlGetConnection()->transactional(function () use ($email, $groups, $fields, $confirmed) {
        $fields['email'] = $email;

        $existing_user = sqlQueryBuilder()->select('get_news')->from('users')
            ->where('email=:email')->setParameter('email', $email)
            ->execute()->fetch();
        $new_user = ($existing_user ? false : true);

        if (!empty($fields['get_news'])) {
            $newsletter = $fields['get_news'];
            unset($fields['get_news']);
        } else {
            $newsletter = ($new_user ? 'N' : $existing_user['get_news']);
        }

        if (findModule(Modules::CURRENCIES) && $new_user) {
            $currencyContext = Contexts::get(CurrencyContext::class);
            $fields['currency'] = $currencyContext->getActiveId();
        }

        if (findModule(Modules::TRANSLATIONS) && $new_user) {
            $languageContext = Contexts::get(LanguageContext::class);
            $fields['id_language'] = $languageContext->getActiveId();
        }

        $qb = sqlQueryBuilder()->insert('users')->directValues($fields)->setValue('date_updated', 'NOW()');
        $sql = $qb->getSQL().' ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), date_updated=NOW()';

        sqlQuery($sql, $qb->getParameters(), $qb->getParameterTypes());

        $user_id = sqlInsertID();

        foreach ($groups as $group) {
            sqlQuery('INSERT INTO '.getTableName('users_groups')." (name) VALUES ('{$group}')
              ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id)");

            $group_id = sqlInsertID();

            sqlQuery('INSERT IGNORE INTO '.getTableName('users_groups_relations')." (id_group, id_user) VALUES ('{$group_id}', '{$user_id}')");
        }

        $userConsent = ServiceContainer::getService(\KupShop\UserBundle\Util\UserConsent::class);
        $userConsent->updateNewsletter($user_id, $newsletter, $new_user, $confirmed);

        return $user_id;
    });
}

function isAdministration()
{
    global $ctrl;

    return getVal('admin', $ctrl, false);
}

function isDebug()
{
    global $cfg;

    return getVal('debug', $cfg) || getVal('wpj_debug');
}

function isDevelopment()
{
    global $cfg;

    return !empty($cfg['development']);
}

function isBeta()
{
    if (isDevelopment()) {
        return true;
    }

    if (!empty(getenv('STABLE_BETA'))) {
        return true;
    }

    return false;
}

function isProduction()
{
    return !isBeta();
}

function isStage()
{
    return isBeta();
}

function isAutoDeploy()
{
    return !empty(getenv('VIRTUAL_HOST'));
}

function isLocalDevelopment()
{
    return !isAutoDeploy();
}

function isFunctionalTests()
{
    return defined('TEST_SUITE') ? TEST_SUITE : false;
}

function isCacheEnabled()
{
    return !getVal('skip_cache');
}

function isRunningOnCluster()
{
    return getenv('KUPSHOP_ENV') === 'kube';
}

function isLive()
{
    return Settings::getDefault()->eshop_live == 'Y';
}

function isFrontendTests(): bool
{
    return (bool) getenv('FRONTEND_TESTS');
}

function isFrontend(): bool
{
    return !isAdministration() && PHP_SAPI !== 'cli';
}

/**
 * @deprecated Use \KupShop\CatalogBundle\Query\Search::searchFields
 *
 * @param $search string search term
 * @param $fields array Definition of search fields
 *                'field' => database field name
 *                'match' => type of value matching. one of 'exact', 'left', 'right', 'both', 'numeric'
 *                'order' => order by this field, if exact match, display first
 *
 * @return string[] Array containing keys 'where', 'data', 'order'
 *
 * Example:
 *    $fields = [
 *        ['field' => 'p.title', 'match' => 'both'],
 *        ['field' => 'p.short_descr', 'match' => 'exact'],
 *        ['field' => 'p.code', 'match' => 'left', 'order' => true],
 *     ];
 *    $search = get_search_query($search_term, $fields);
 */
function get_search_query($search, $fields, $operator = 'AND')
{
    static $counter = 0;

    // Limit search size
    $search = substr(trim($search), 0, 100);

    // Preprocess search terms
    $words = preg_split('/[[:blank:]]+/', $search);
    foreach ($words as &$word) {
        // odstraneni znaku procento
        $word = str_replace('%', '\\%', $word);
        // odstraneni znaku PODTRZITKO
        $word = str_replace('_', '\\_', $word);
        // zmena znaku hvezdicka na procento
        $word = str_replace('*', '%', $word);
        // zmena znaku OTAZNIK na PODTRZITKO
        $word = str_replace('?', '_', $word);
    }
    unset($word);

    // Return non-existing search query
    if (count($words) <= 0) {
        return ['where' => '1=0', 'data' => []];
    }

    $keys = range($counter, $counter + count($words) - 1);
    $words = array_combine($keys, $words);
    $search_param = 'search_'.$counter;

    // Prepare field search terms
    $terms = [];
    $order = [];
    foreach ($fields as $field) {
        if ($field['match'] == 'numeric') {
            $terms[] = "{$field['field']} = :field__numeric";
        } else {
            $terms[] = "{$field['field']} LIKE :field__{$field['match']}";
        }

        if (!empty($field['order'])) {
            $order[] = "{$field['field']} = :{$search_param} DESC";
        }
    }

    // Generate data fields
    $data = [$search_param => $search];
    foreach ($words as $index => $word) {
        $data["field_{$index}_numeric"] = is_numeric($word) ? $word : null; // Limit numeric to only numbers
        $data["field_{$index}_exact"] = $word;
        $data["field_{$index}_left"] = "{$word}%";
        $data["field_{$index}_both"] = "%{$word}%";
        $counter++;
    }

    // Generate whole query
    $where = '('.join(') '.$operator.' (', array_map(function ($index) use ($terms) {
        return '('.join(') OR (', array_map(function ($term) use ($index) {
            return str_replace(':field__', ":field_{$index}_", $term);
        }, $terms)).')';
    }, array_keys($words))).')';

    return ['where' => $where, 'data' => $data, 'order' => $order ? join(',', $order) : null];
}

/**
 * @deprecated Use Decimal {remove|add}Vat, {remove|ddd}Discount
 */
function calcPrice($value, $vat = null, $discount = null, $force_return_decimal = true)
{
    if (!is_a($value, 'Decimal')) {
        $value = Decimal::fromString((string) $value);
    }

    // pricteni dane z ceny
    if (!is_null($vat)) {
        $vat_decimal = Decimal::fromFloat(abs((float) $vat))->add(Decimal::fromInteger(100))->div(Decimal::fromInteger(100));

        if ($vat > 0) {
            $value = $value->mul($vat_decimal);
        } else {
            $value = $value->div($vat_decimal);
        }
    }

    if (!is_a($discount, 'Decimal')) {
        $discount = Decimal::fromString((string) $discount);
    }

    // normalni sleva
    if (!$discount->isZero()) {
        $value = $value->sub($value->mul($discount)->div(toDecimal(100)));
    }

    return $force_return_decimal ? $value->value() : (string) $value->value();
}

function toDecimal($value)
{
    return Decimal::ensureDecimal($value);
}

function formatEAN($ean)
{
    return !empty($ean) ? sprintf('%013d', $ean) : $ean;
}

/**
 * @param \KupShop\ContentBundle\Entity\Placeholder[] $placeholders
 */
function replacePlaceholders($str, $params, $callback = null, array $placeholders = [])
{
    return preg_replace_callback('/{(.+?)}/', function ($matches) use ($placeholders, $params, $callback) {
        if (isset($params[$matches[1]])) {
            return $params[$matches[1]];
        }

        foreach ($placeholders as $placeholder) {
            if ($placeholder->code == $matches[1]) {
                return $placeholder->getValue();
            }
        }

        if ($callback) {
            return call_user_func($callback, $matches[1]);
        }
    }, $str);
}

/**
 * @deprecated Unused, delete
 */
function implodeKeyValue($array, $keyValueGlue, $itemGlue)
{
    return implode($itemGlue, array_map(function ($key, $value) use ($keyValueGlue) {
        return $key.$keyValueGlue.$value;
    }, array_keys($array), $array));
}

function explodeFlags($flags)
{
    if (is_string($flags)) {
        $flags = array_flip(explode(',', $flags));
    }

    $flags = $flags ?? [];
    foreach ($flags as &$flag) {
        $flag = '1';
    }

    return $flags;
}

function logged()
{
    global $ctrl;

    return $ctrl['logged'];
}

/**
 * @deprecated Use SecurityTrait
 */
function loggedRedirect()
{
    if (!logged()) {
        redirection('LOGIN');
    }
}

function f_prop($objectProperty)
{
    return function ($object) use ($objectProperty) {
        return $object->{$objectProperty};
    };
}

function f_field($arrayField)
{
    return function (array $array) use ($arrayField) {
        return $array[$arrayField];
    };
}

function f_call($objectMethod)
{
    return function ($object) use ($objectMethod) {
        return call_user_func([$object, $objectMethod]);
    };
}

function f_valueIs($value)
{
    return function ($toFilter) use ($value) {
        return $toFilter === $value;
    };
}

function indexByMethod(array $arrayOfObjects, $objectMethodName)
{
    return array_combine(array_map(f_call($objectMethodName), $arrayOfObjects), $arrayOfObjects);
}

function indexByField(array $arrayOfArrays, $arrayField)
{
    return array_combine(array_map(f_field($arrayField), $arrayOfArrays), $arrayOfArrays);
}

if (!class_exists('JsonException')) {
    class JsonException extends RuntimeException
    {
    }
}

/**
 * Calls json_decode(), but throws exception on failure.
 */
function json_decode_strict($json, $assoc = false, $depth = 512, $options = 0)
{
    $result = json_decode($json, $assoc, $depth, $options);
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new JsonException(/* json_last_error_msg() */
            'Error decoding JSON', json_last_error());
    }

    return $result;
}

/**
 * @return array
 */
function array_merge_strict(array $array1, array $array2)
{
    foreach ($array2 as $key2 => $value2) {
        if (
            isset($array1[$key2])
            // is_array() is slow. `(array) $var === $var` is much faster
            && (array) $array1[$key2] === $array1[$key2]
            && (array) $value2 === $value2
        ) {
            $value2 = array_merge_strict($array1[$key2], $value2);
        }
        $array1[$key2] = $value2;
    }

    return $array1;
}

/**
 * @return \Symfony\Component\DependencyInjection\ContainerInterface
 *
 * @deprecated Use \KupShop\KupShopBundle\Util\Compat\ServiceContainer instead
 */
function getSymfonyContainer()
{
    return ServiceContainer::getContainer();
}

/**
 * @param $service string #Service to get
 *
 * @return object
 *
 * @deprecated Use \KupShop\KupShopBundle\Util\Compat\ServiceContainer instead
 */
function getSymfonyService($service)
{
    return ServiceContainer::getService($service);
}

/**
 * @see UrlGeneratorInterface
 *
 * @param string $name          #Route
 * @param array  $parameters
 * @param int    $referenceType
 */
function path($name, $parameters = [], $referenceType = Router::ABSOLUTE_PATH)
{
    /** @var Router $router */
    $router = ServiceContainer::getService('router');

    if ($referenceType == Router::ABSOLUTE_URL) {
        if (isLocalDevelopment() && !isFunctionalTests()) {
            $router->getContext()
                ->setHost('www.kupshop.local')
                ->setScheme('https');
        } else {
            $domainContext = ServiceContainer::getService(\KupShop\KupShopBundle\Context\DomainContext::class);
            $router->getContext()
                ->setHost($domainContext->getActiveId())
                ->setScheme('https');
        }
    }

    return call_user_func_array([$router, 'generate'], func_get_args());
}

function loadBundlesAdmin($path)
{
    /** @var \KupShop\KupShopBundle\Util\System\BundleFinder $bundleFinder */
    $bundleFinder = ServiceContainer::getService('kupshop.util_system.bundle_finder');

    $files = $bundleFinder->getExistingBundlesPath('Admin/'.$path);

    uasort($files, function ($a, $b) {
        // bundles/KupShop soubory davam az na konec pole, aby na prvnich mistech byyl shop a external bundly
        return (strpos($a, 'bundles/KupShop') !== false ? 1 : 0) <=> (strpos($b, 'bundles/KupShop') !== false ? 1 : 0);
    });

    return reset($files);
}

function loadBundles($path)
{
    /** @var \KupShop\KupShopBundle\Util\System\BundleFinder $bundleFinder */
    $bundleFinder = ServiceContainer::getService('kupshop.util_system.bundle_finder');

    $files = $bundleFinder->getExistingBundlesPath($path);

    uasort($files, function ($a, $b) {
        // bundles/KupShop soubory davam az na konec pole, aby na prvnich mistech byyl shop a external bundly
        return (strpos($a, 'bundles/KupShop') !== false ? 1 : 0) <=> (strpos($b, 'bundles/KupShop') !== false ? 1 : 0);
    });

    return reset($files);
}

function getShopUniqueName()
{
    return Config::get()['Connection']['database'];
}

function getUserKey()
{
    $userKey = Cart::getCartID();

    return $userKey;
}

/**
 * @param string $message  User message to display
 * @param string $severity One of success, info, warning, danger
 * @param array  $data     Data specific to this message
 *
 * @return bool
 */
function addUserMessage($message, $severity = 'warning', $data = [])
{
    /** @var \Symfony\Component\HttpFoundation\Session\Session $session */
    $session = \KupShop\KupShopBundle\Util\Compat\ServiceContainer::getService('session');

    $message = ['text' => $message, 'severity' => $severity];
    if ($data) {
        $session->getFlashBag()->add($severity, array_merge($message, $data));
    } else {
        $session->getFlashBag()->add($severity, $message);
    }

    return true;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/**
 * @deprecated Use addActivityLog
 */
function writeDownActivity($text, $repeatMsgs = false, $robot = false)
{
    $text = trim($text);
    $text = stripTags($text);
    $text = ucfirst($text);

    addActivityLog(ActivityLog::SEVERITY_NOTICE, ActivityLog::TYPE_CHANGE, $text);

    return true;
}

function addActivityLog(string $severity, string $type, string $message, ?array $data = [], ?array $tags = [])
{
    $activityLog = ServiceContainer::getService(ActivityLog::class);

    return $activityLog->addActivityLog($severity, $type, $message, $data, $tags);
}

/**
 * @return false|string
 */
function increaseMemoryLimit(int $MB)
{
    $memoryLimit = ini_get('memory_limit');
    $memoryLimitUnit = substr($memoryLimit, -1, 1);
    $memoryLimitValue = (int) substr($memoryLimit, 0, -1);
    switch ($memoryLimitUnit) {
        case 'M': $memoryLimitValue += $MB;
            break;
        case 'K': $memoryLimitValue += $MB * 1024;
            break;
        case 'G': $memoryLimitValue += ceil($MB / 1024);
            break;
    }

    return ini_set('memory_limit', $memoryLimitValue.$memoryLimitUnit);
}

function increaseMaxExecutionTime(int $seconds): int
{
    $oldValue = ini_get('max_execution_time');

    // unlimited
    if ($oldValue === '0') {
        return 0;
    }

    $newValue = ((int) $oldValue) + $seconds;
    ini_set('max_execution_time', (string) $newValue);

    return $newValue;
}

function getShopName()
{
    return getenv('BRANCH') ?: 'no';
}

function translate_shop($key, $section = null, $silent = false)
{
    if (!isAdministration()) {
        return translate($key, $section, $silent, false);
    }

    $translator = ServiceContainer::getService(\KupShop\KupShopBundle\Util\Locale\PHPArrayTranslator::class);

    $lang = $translator->getShopLanguage();

    static $shop_translations = [];

    if (!array_key_exists($lang, $shop_translations)) {
        $shop_translations[$lang] = [];
    }

    if (empty($shop_translations[$lang])) {
        $shop_translations[$lang] = $translator->loadShopMainTranslations();
    }

    $translator->loadTranslations($shop_translations[$lang], $section, false, $translator->getShopLanguage());

    return $translator->translate($shop_translations[$lang], $key, $section, $silent);
}

function redirection($URL, $typeOrPermanent = null)
{
    if (isAdministration()) {
        redirect($URL, $typeOrPermanent ?? 'header');
    }

    $typeOrPermanent ??= false;

    if ($URL == 'NEXT') {
        if (!empty($_GET['next'])) {
            redirection($_GET['next']);
        } else {
            redirection('REFERER');
        }
    }
    if ($URL == 'REFERER') {
        if (!empty($_SERVER['HTTP_REFERER'])) {
            $URL = $_SERVER['HTTP_REFERER'];
        } else {
            $URL = './';
        }
    }
    if ($URL == 'REFRESH') {
        $URL = $_SERVER['REQUEST_URI'];
    } elseif ($URL == 'LOGIN') {
        $request = SymfonyBridge::getCurrentRequest();
        redirection(createScriptURL([
            'URL' => 'launch.php',
            's' => 'login',
            'msg' => '1',
            'url' => urlencode($request->getUri()),
            'ESCAPE' => 'NO',
        ]));
    } elseif ($URL == 'MODUL_NOT_FOUND') {
        // zalogovat chybu
        // logError(__FILE__, __LINE__, "Presmerovani MODUL NOT FOUND referer: ".$GLOBALS['HTTP_REFERER'], false);
        $URL = createScriptURL([
            'URL' => 'launch.php',
            's' => 'error404',
        ]);
    } elseif ($URL == 'INDEX') {
        $URL = createScriptURL([
            'URL' => 'launch.php',
            's' => 'index',
        ]);
    } elseif ($URL == 'REWRITE') {
        $URL = createScriptURL([
            'URL' => 'launch.php',
            's' => 'error404',
            'search' => $_SERVER['REQUEST_URI'],
        ]);
    }

    // pridat absolutni adresovani
    if (!preg_match('@^([a-z]{3,6}://|/|\./)@', $URL)) {
        $URL = preg_replace('@^/*@', '', $URL);
        $URL = $GLOBALS['cfg']['Addr']['rel'].$URL;
    }

    if (defined('TEST_SUITE')) {
        throw new Exception("Redirecting to {$URL}: permament: {$typeOrPermanent}");
    }

    if ($typeOrPermanent) {
        throw new \KupShop\KupShopBundle\Exception\PermanentRedirectException($URL);
    } else {
        throw new \KupShop\KupShopBundle\Exception\RedirectException($URL);
    }
}

// Not using class.Modules because that would require autoloader
if (!findModule('components')) {
    spl_autoload_register('autoload');
}
