<?php

class Warp extends Space
{
    protected $array;

    public function __construct($array)
    {
        $this->array = $array;
    }

    public function sum($key = '')
    {
        return array_reduce($this->array, function ($carry, $item) use ($key) {
            if ($key == '') {
                $carry += $item;
            } else {
                $carry += array_key_exists($key, $item) ? $item[$key] : 0;
            }

            return $carry;
        });
    }

    public function count()
    {
        return count($this->array);
    }

    public function map($callback)
    {
        return array_map($callback, $this->array);
    }

    public function filter($callback = '')
    {
        if (func_num_args() == 0) {
            return array_filter($this->array);
        }

        return array_filter($this->array, $callback, ARRAY_FILTER_USE_BOTH);
    }

    public function flatten()
    {
        return $this->array_flatten($this->array);
    }

    public function flatMap($callback)
    {
        $data = array_map($callback, $this->array);

        return $this->array_flatten($data);
    }

    public function pluck($value, $key = null)
    {
        return $this->array_pluck($this->array, $value, $key);
    }

    public function groupBy($key)
    {
        $return = [];
        foreach ($this->array as $val) {
            $return[$val[$key]][] = $val;
        }

        return $return;
    }

    public function get($key, $default = null)
    {
        $array = $this->array;

        if (is_null($key)) {
            return $array;
        }

        if (isset($array[$key])) {
            return $array[$key];
        }

        foreach (explode('.', $key) as $segment) {
            if (! is_array($array) || ! array_key_exists($segment, $array)) {
                return $this->value($default);
            }
            $array = $array[$segment];
        }

        return $array;
    }

    public function sumRange($min, $max)
    {
        return $this->sumArray($this->array, $min, $max);
    }
}

class Space
{
    /**
     * Pluck an array of values from an array.
     *
     * @source https://github.com/WilmanBarrios/laravel-helpers/blob/master/src/helpers.php
     *
     * @example https://laravel.com/docs/5.6/helpers#method-array-pluck
     *
     * @param array  $array
     * @param string $value
     * @param string $key
     *
     * @return array
     */
    public function array_pluck($array, $value, $key = null)
    {
        $results = [];
        foreach ($array as $item) {
            $itemValue = $this->data_get($item, $value);
            // If the key is "null", we will just append the value to the array and keep
            // looping. Otherwise we will key the array using the value of the key we
            // received from the developer. Then we'll return the final array form.
            if (is_null($key)) {
                $results[] = $itemValue;
            } else {
                $itemKey = $this->data_get($item, $key);
                $results[$itemKey] = $itemValue;
            }
        }

        return $results;
    }

    /**
     * Get an item from an array or object using "dot" notation.
     *
     * @source https://github.com/WilmanBarrios/laravel-helpers/blob/master/src/helpers.php
     *
     * @example https://laravel.com/docs/5.6/helpers#method-array-pluck
     *
     * @param mixed  $target
     * @param string $key
     * @param mixed  $default
     *
     * @return mixed
     */
    public function data_get($target, $key, $default = null)
    {
        if (is_null($key)) {
            return $target;
        }
        foreach (explode('.', $key) as $segment) {
            if (is_array($target)) {
                if (! array_key_exists($segment, $target)) {
                    return value($default);
                }
                $target = $target[$segment];
            } elseif ($target instanceof ArrayAccess) {
                if (! isset($target[$segment])) {
                    return value($default);
                }
                $target = $target[$segment];
            } elseif (is_object($target)) {
                if (! isset($target->{$segment})) {
                    return value($default);
                }
                $target = $target->{$segment};
            } else {
                return value($default);
            }
        }

        return $target;
    }

    public function array_flatten($array)
    {
        if (! is_array($array)) {
            return false;
        }
        $result = [];
        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $result = array_merge($result, $this->array_flatten($value));
            } else {
                $result[$key] = $value;
            }
        }

        return $result;
    }

    public function value($value)
    {
        return $value instanceof Closure ? $value() : $value;
    }

    public function sumArray($array, $min, $max)
    {
        $sum = 0;
        foreach ($array as $k => $a) {
            if ($k >= $min && $k <= $max) {
                $sum += $a;
            }
        }

        return $sum;
    }
}
