<?php

namespace LWVendor\PhpOffice\PhpSpreadsheet\Calculation\LookupRef;

use LWVendor\PhpOffice\PhpSpreadsheet\Calculation\Functions;
use LWVendor\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use LWVendor\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class Unique
{
    /**
     * UNIQUE
     * The UNIQUE function searches for value either from a one-row or one-column range or from an array.
     *
     * @param mixed $lookupVector The range of cells being searched
     * @param mixed $byColumn Whether the uniqueness should be determined by row (the default) or by column
     * @param mixed $exactlyOnce Whether the function should return only entries that occur just once in the list
     *
     * @return mixed The unique values from the search range
     */
    public static function unique($lookupVector, $byColumn = \false, $exactlyOnce = \false)
    {
        if (!\is_array($lookupVector)) {
            // Scalars are always returned "as is"
            return $lookupVector;
        }
        $byColumn = (bool) $byColumn;
        $exactlyOnce = (bool) $exactlyOnce;
        return $byColumn === \true ? self::uniqueByColumn($lookupVector, $exactlyOnce) : self::uniqueByRow($lookupVector, $exactlyOnce);
    }
    /**
     * @return mixed
     */
    private static function uniqueByRow(array $lookupVector, bool $exactlyOnce)
    {
        // When not $byColumn, we count whole rows or values, not individual values
        //      so implode each row into a single string value
        \array_walk($lookupVector, function (array &$value) : void {
            $value = \implode(\chr(0x0), $value);
        });
        $result = self::countValuesCaseInsensitive($lookupVector);
        if ($exactlyOnce === \true) {
            $result = self::exactlyOnceFilter($result);
        }
        if (\count($result) === 0) {
            return ExcelError::CALC();
        }
        $result = \array_keys($result);
        // restore rows from their strings
        \array_walk($result, function (string &$value) : void {
            $value = \explode(\chr(0x0), $value);
        });
        return \count($result) === 1 ? \array_pop($result) : $result;
    }
    /**
     * @return mixed
     */
    private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce)
    {
        $flattenedLookupVector = Functions::flattenArray($lookupVector);
        if (\count($lookupVector, \COUNT_RECURSIVE) > \count($flattenedLookupVector, \COUNT_RECURSIVE) + 1) {
            // We're looking at a full column check (multiple rows)
            $transpose = Matrix::transpose($lookupVector);
            $result = self::uniqueByRow($transpose, $exactlyOnce);
            return \is_array($result) ? Matrix::transpose($result) : $result;
        }
        $result = self::countValuesCaseInsensitive($flattenedLookupVector);
        if ($exactlyOnce === \true) {
            $result = self::exactlyOnceFilter($result);
        }
        if (\count($result) === 0) {
            return ExcelError::CALC();
        }
        $result = \array_keys($result);
        return $result;
    }
    private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues) : array
    {
        $caseInsensitiveCounts = \array_count_values(\array_map(function (string $value) {
            return StringHelper::strToUpper($value);
        }, $caseSensitiveLookupValues));
        $caseSensitiveCounts = [];
        foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
            if (\is_numeric($caseInsensitiveKey)) {
                $caseSensitiveCounts[$caseInsensitiveKey] = $count;
            } else {
                foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
                    if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
                        $caseSensitiveCounts[$caseSensitiveValue] = $count;
                        break;
                    }
                }
            }
        }
        return $caseSensitiveCounts;
    }
    private static function exactlyOnceFilter(array $values) : array
    {
        return \array_filter($values, function ($value) {
            return $value === 1;
        });
    }
}
