<?php

namespace apexl\Utils\Arrays;

class Merge
{
    /**
     * Retained for backwards compatibility. Weight-conflict state is now derived
     * per invocation from the target array, so there is nothing to reset.
     */
    public static function reset(): void
    {
    }

    /**
     * Array merge recursive but keep data types consistent (Dont duplicate strings)
     * @see https://stackoverflow.com/a/56024890/2412837
     * @param $arrays
     * @return array
     */
    public static function arrayMergeRecursive($arrays)
    {
        $result = array ();
        foreach ($arrays as $array) {
            foreach ($array as $key => $value) {
                // Renumber integer keys as array_merge_recursive() does. Note that PHP
                // automatically converts array keys that are integer strings (e.g., '1')
                // to integers.
                if (is_int($key)) {
                    $result[] = $value;
                } elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
                    $result[$key] = self::arrayMergeRecursive(array (
                        $result[$key],
                        $value,
                    ));
                } else {
                    $result[$key] = $value;
                }
            }
        }
        return $result;
    }

    /**
     * @see https://stackoverflow.com/a/9131722 (Args have been re-ordered)
     * @param $arr
     * @param $key
     * @param $val
     * @param $index
     * @return array
     */
    public static function injectAssocKeyToPosition($key, $val, $arr, $index)
    {
        $arrayEnd = array_splice($arr, $index);
        $arrayStart = array_splice($arr, 0, $index);
        return (array_merge($arrayStart, array ($key => $val), $arrayEnd));
    }

    /**
     * @param $arr
     * @param $val
     * @param $index
     * @return array
     */
    public static function injectNumericKeyToPosition($index, $val, $arr)
    {
        $arrayEnd = array_splice($arr, $index);
        $arrayStart = array_splice($arr, 0, $index);
        //increment the rest of the keys by one.
        return (array_merge(
            $arrayStart,
            array ($index => $val),
            array_combine(array_keys(array_fill($index, count($arrayEnd), 0)), array_values($arrayEnd))
        ));
    }

    /**
     * Place $value into $arrayToOrder keyed by $weight, keeping the array sorted
     * by key. Entries are never overwritten: a taken weight stacks onto the first
     * free slot above it, so equal weights keep their registration order.
     *
     * @param $weight
     * @param $value
     * @param $arrayToOrder
     */
    public static function arrayByWeight($weight, $value, &$arrayToOrder)
    {
        //first, check if we have the weight used. if not, then we simply inject it, sort the array and return.
        if (!isset($arrayToOrder[$weight])) {
            $arrayToOrder[$weight] = $value;
            ksort($arrayToOrder);

            return;
        }

        //The weight is taken. Walk up from it to the first free slot and stack the
        //new entry there. Deriving the slot from the array itself (rather than from
        //state carried between calls) keeps repeated builds identical and means no
        //existing entry is ever displaced or overwritten.
        $slot = $weight;
        do {
            ++$slot;
        } while (isset($arrayToOrder[$slot]));

        $arrayToOrder[$slot] = $value;
        ksort($arrayToOrder);
    }
}
