<?php

namespace goodboyalex\php_components_pack\traits\ObjectArray;

/**
 * Часть кода класса ObjectArray, отвечающая за поиск и исключение объектов.
 *
 * @author Александр Бабаев
 * @package php_components_pack
 * @version 1.0
 * @since 1.0
 */
trait ObjectArraySearchAndSortTrait
{
    /**
     * Сортирует массив объектов, по пользовательской функции.
     *
     * @param callable $objectPropertyValuePredicate Функция <code>fn (mixed $item): mixed => $item-><PROPERTY></code>,
     *     возвращающая сравниваемый объект. Возвращаемое значение должно быть значением свойства этого объекта.
     *
     * <code>
     *     class Demo;
     *     {
     *          public string $Property1;
     *          public string $Property2;
     *          public function __construct(string $Property1, string $Property2)
     *          {
     *              $this->Property1 = $Property1;
     *              $this->Property2 = $Property2;
     *          }
     *      }
     *      $array = new ObjectArray (
     *        [
     *          new Demo('1', '2'),
     *          new Demo('5', '6'),
     *          new Demo('3', '4')
     *        ]);
     *
     *      // output:
     *      // [Demo('1', '2'), Demo('3', '4'), Demo('5', '6')]
     *      $result = $array->SortCallback($array, fn ($item): mixed => $item->Property1);
     * </code>
     * @param bool $descending Направление сортировки
     *
     * @return void
     */
    public function SortCallback (callable $objectPropertyValuePredicate, bool $descending = false): void
    {
        // Создаём результирующий массив
        $result = array_merge($this->Container, []);

        // Сортируем массив
        usort($result,
            fn ($a, $b)
                => !$descending
                ? $objectPropertyValuePredicate($a) <=> $objectPropertyValuePredicate($b)
                : $objectPropertyValuePredicate($b) <=> $objectPropertyValuePredicate($a));

        // Присваиваем результат
        $this->Container = $result;
    }

    /**
     * Сортирует массив объектов, по значению свойства объекта.
     *
     * @param string $objectProperty Имя свойства объекта
     * @param bool $descending Направление сортировки
     *
     * @return void
     */
    public function Sort (string $objectProperty, bool $descending = false): void
    {
        // Создаём результирующий массив
        $result = array_merge($this->Container, []);

        // Сортируем массив
        usort($result,
            fn ($a, $b)
                => !$descending
                ? $a->$objectProperty <=> $b->$objectProperty
                : $b->$objectProperty <=> $a->$objectProperty);

        // Присваиваем результат
        $this->Container = $result;
    }

    /**
     * Поиск объекта в массиве объектов по значению свойства объекта.
     *
     * @param string $forProperty Имя искомого свойства объекта.
     * @param mixed $searchValue Искомое значение свойства объекта.
     * @param callable|null $compareFunction Функция сравнения, принимающая два параметра: mixed $currentPropertyValue
     *     (текущее значение свойства) и mixed $searchPropertyValue (поисковое значение свойства) и возвращает bool -
     *     true, если значение свойства совпадает с искомым, false - если не совпадает. Если передаётся null, то будет
     *     использоваться DefaultComparerFunction. По умолчанию, null.
     *
     * @return mixed Объект, найденный по значению свойства или null, если не найден
     */
    public function Search (string $forProperty, mixed $searchValue, ?callable $compareFunction = null): mixed
    {
        // Проходим по массиву
        foreach ($this->Container as $item) {
            // - пропускаем не объекты
            if (!is_object($item))
                continue;

            // - пропускаем, если значение свойства нет в этом объекте
            if (!property_exists($item, $forProperty))
                continue;

            // - пропускаем, если значение свойства не задано
            if (!isset($item->$forProperty))
                continue;

            // - если не задана функция сравнения
            if (is_null($compareFunction) || !is_callable($compareFunction))
                // -- то устанавливаем функцию сравнения по умолчанию
                $compareFunction = $this->DefaultComparerFunction;

            // - если значение свойства совпадает с искомым
            if ($compareFunction($item->$forProperty, $searchValue))
                // -- возвращаем объект
                return $item;
        }

        // Если мы сюда дошли, значить объект не найден - возвращаем null
        return null;
    }
}