<?php

declare (strict_types=1);
namespace SlimStat\Dependencies\BrowscapPHP\Parser;

use SlimStat\Dependencies\BrowscapPHP\Formatter\FormatterInterface;
use SlimStat\Dependencies\BrowscapPHP\Parser\Helper\GetDataInterface;
use SlimStat\Dependencies\BrowscapPHP\Parser\Helper\GetPatternInterface;
use UnexpectedValueException;
use function array_shift;
use function count;
use function preg_match;
use function str_replace;
use function strpos;
use function strtok;
use function strtolower;
use function substr_replace;
/**
 * Ini parser class (compatible with PHP 5.3+)
 */
final class Ini implements ParserInterface
{
    private Helper\GetPatternInterface $patternHelper;
    private Helper\GetDataInterface $dataHelper;
    /**
     * Formatter to use
     */
    private FormatterInterface $formatter;
    /**
     * @throws void
     */
    public function __construct(GetPatternInterface $patternHelper, GetDataInterface $dataHelper, FormatterInterface $formatter)
    {
        $this->patternHelper = $patternHelper;
        $this->dataHelper = $dataHelper;
        $this->formatter = $formatter;
    }
    /**
     * Gets the browser data formatr for the given user agent
     * (or null if no data avaailble, no even the default browser)
     *
     * @throws UnexpectedValueException
     */
    public function getBrowser(string $userAgent): ?FormatterInterface
    {
        $userAgent = strtolower($userAgent);
        $formatter = null;
        foreach ($this->patternHelper->getPatterns($userAgent) as $patterns) {
            $patternToMatch = '/^(?:' . str_replace("\t", ')|(?:', $patterns) . ')$/i';
            if (!preg_match($patternToMatch, $userAgent)) {
                continue;
            }
            // strtok() requires less memory than explode()
            $pattern = strtok($patterns, "\t");
            while ($pattern !== false) {
                $pattern = str_replace('[\d]', '(\d)', $pattern);
                $quotedPattern = '/^' . $pattern . '$/i';
                $matches = [];
                if (preg_match($quotedPattern, $userAgent, $matches)) {
                    // Insert the digits back into the pattern, so that we can search the settings for it
                    if (1 < count($matches)) {
                        array_shift($matches);
                        foreach ($matches as $oneMatch) {
                            $numPos = (int) strpos($pattern, '(\d)');
                            $pattern = substr_replace($pattern, $oneMatch, $numPos, 4);
                        }
                    }
                    // Try to get settings - as digits have been replaced to speed up the pattern search (up to 90 faster),
                    // we won't always find the data in the first step - so check if settings have been found and if not,
                    // search for the next pattern.
                    $settings = $this->dataHelper->getSettings($pattern);
                    if (0 < count($settings)) {
                        $formatter = $this->formatter;
                        $formatter->setData($settings);
                        break 2;
                    }
                }
                $pattern = strtok("\t");
            }
        }
        return $formatter;
    }
}