<?php
namespace DevOwl\Multilingual;

use RecursiveArrayIterator;
use RecursiveIteratorIterator;

// @codeCoverageIgnoreStart
defined('ABSPATH') or die('No script kiddies please!'); // Avoid direct file request
// @codeCoverageIgnoreEnd

/**
 * Util functionalities.
 */
class Utils {
    use UtilsProvider;

    /**
     * Check if a string starts with a given needle.
     *
     * @param string $haystack The string to search in
     * @param string $needle The starting string
     * @see https://stackoverflow.com/a/834355/5506547
     * @codeCoverageIgnore
     */
    public static function startsWith($haystack, $needle) {
        if (!is_string($haystack) || !is_string($needle)) {
            return false;
        }

        $length = strlen($needle);
        return substr($haystack, 0, $length) === $needle;
    }

    /**
     * Check if a string starts with a given needle.
     *
     * @param string $haystack The string to search in
     * @param string $needle The starting string
     * @see https://stackoverflow.com/a/834355/5506547
     * @codeCoverageIgnore
     */
    public static function endsWith($haystack, $needle) {
        if (!is_string($haystack) || !is_string($needle)) {
            return false;
        }

        $length = strlen($needle);
        if (!$length) {
            return true;
        }
        return substr($haystack, -$length) === $needle;
    }

    /**
     * Expand keys to dot notation so `skipKeys` works as expected and can skip
     * multidimensional arrays. This functionality also keeps the reference!
     *
     * You can use regular expressions in the skip keys to match parts of the path,
     * e.g. "/technicalDefinitions\.\d+\.(?!purpose)/"
     *
     * @param mixed $arr
     * @param string[] $skipKeys
     * @param callable $skipKeysByCallback
     * @see https://stackoverflow.com/a/40217420/5506547
     */
    public static function expandKeys(&$arr, $skipKeys = [], $skipKeysByCallback = null) {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveArrayIterator($arr),
            RecursiveIteratorIterator::SELF_FIRST
        );
        $pathMapping = [];
        $flatArray = [];

        $regexSkipKeys = [];
        foreach ($skipKeys as $key) {
            // Only allow "/" as the regex delimiter, e.g. "/pattern/"
            if (strlen($key) >= 3 && $key[0] === '/' && substr($key, -1) === '/') {
                $inner = substr($key, 1, -1);
                if (strpos(str_replace('\/', '', $inner), '/') !== false) {
                    continue;
                }
                $regexSkipKeys[] = $inner;
            }
        }
        $regexSkipKeys = join('|', $regexSkipKeys);

        foreach ($iterator as $key => $value) {
            $pathMapping[$iterator->getDepth()] = $key;

            if (!is_array($value)) {
                $pathMapping = array_slice($pathMapping, 0, $iterator->getDepth() + 1);

                // Get the value by reference
                $refValue = &$arr;
                foreach ($pathMapping as $dot) {
                    if (in_array($dot, $skipKeys, true)) {
                        continue 2;
                    }

                    if (is_array($refValue)) {
                        $refValue = &$refValue[$dot];
                    } else {
                        $refValue = &$refValue->{$dot};
                    }
                }

                $path = implode('.', $pathMapping);
                if (
                    (is_callable($skipKeysByCallback) && $skipKeysByCallback($path, $refValue)) ||
                    (!empty($regexSkipKeys) && preg_match('/' . $regexSkipKeys . '/', $path))
                ) {
                    continue;
                }

                $flatArray[$path] = &$refValue;
            }
        }
        return $flatArray;
    }
}
