<?php

namespace apexl\Utils\Arrays;

class Merge
{
    protected static $weightStore = [];

    public static function reset(): void
    {
        self::$weightStore = [];
    }

    /**
     * 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))
        ));
    }

    /**
     * @param $weight
     * @param $value
     * @param $arrayToOrder
     */
    public static function arrayByWeight($weight, $value, &$arrayToOrder)
    {
        $futureKeys = 0;
        //first, check if we have the weight used. if not, then we simply inject it, sort the array and return.
        if (!isset($arrayToOrder[$weight])) {
            self::$weightStore[] = [$weight => true];
            $arrayToOrder[$weight] = $value;
            ksort($arrayToOrder);
        } else {
            //We have a key match! We have a few problems to solve here, including the fact we need to maintain other matched keys in the same 'grouping'
            //To do this, we first record this as a match, and the weight it matched at. This can be stored and referenced should we get another match, and thus used to
            // determine which key group this should belong to.
            //We need to do this ourselves here so we dont reset keys and can deal with negative values.
            $newOrder = [];
            foreach ($arrayToOrder as $startWeight => $existingValue) {
                if ($startWeight != $weight) {
                    $newOrder[($startWeight + $futureKeys)] = $existingValue;
                }
                //we matched!
                if ($startWeight == $weight) {
                    //restore the existing value
                    $newOrder[$startWeight] = $existingValue;
                    //Ok, we need to check if we have a previous conflict. If so, we need to add this to the end of the last conflicted item and set the number
                    //to increment the rest of the keys with.
                    $conflicts = 1;
                    foreach (self::$weightStore as $item) {
                        if (isset($item[($weight)])) {
                            ++$conflicts;
                        }
                    }
                    //next, store the new conflict
                    self::$weightStore[] = [$weight => true];

                    //We need to stack this on the end. Check if the next 'slot' is occupied.
                    if (isset($arrayToOrder[($weight + $conflicts)])) {
                        //We have a problem as we have an additional conflict, so we need to insert here and increment all future keys by one.
                        $futureKeys = 1;
                    }
                    //Space! Insert the new item, also record this as a conflict.
                    self::$weightStore[] = [$weight + $conflicts => true];
                    $newOrder[($weight + $conflicts)] = $value;
                }
            }
            //order the new array
            ksort($newOrder);
            //this is by reference so just set the value.
            $arrayToOrder = $newOrder;
        }
    }
}
