<?php

namespace TablePress\PhpOffice\PhpSpreadsheet\Calculation\MathTrig;

use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Exception;
use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Functions;
use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;

class Lcm
{
	/**
	 *  Private method to return an array of the factors of the input value.
	 *
	 * @return int[]
	 */
	private static function factors(float $value): array
	{
		$startVal = floor(sqrt($value));

		$factorArray = [];
		for ($i = $startVal; $i > 1; --$i) {
			if (($value % $i) == 0) {
				$factorArray = array_merge($factorArray, self::factors($value / $i));
				$factorArray = array_merge($factorArray, self::factors($i));
				if ($i <= sqrt($value)) {
					break;
				}
			}
		}
		if (!empty($factorArray)) {
			rsort($factorArray);
			/** @var int[] $factorArray */

			return $factorArray;
		}

		return [(int) $value];
	}

	/**
	 * LCM.
	 *
	 * Returns the lowest common multiplier of a series of numbers
	 * The least common multiple is the smallest positive integer that is a multiple
	 * of all integer arguments number1, number2, and so on. Use LCM to add fractions
	 * with different denominators.
	 *
	 * Excel Function:
	 *        LCM(number1[,number2[, ...]])
	 *
	 * @param mixed ...$args Data values
	 *
	 * @return int|string Lowest Common Multiplier, or a string containing an error
	 */
	public static function evaluate(...$args)
	{
		try {
			$arrayArgs = [];
			$anyZeros = 0;
			$anyNonNulls = 0;
			foreach (Functions::flattenArray($args) as $value1) {
				$anyNonNulls += (int) ($value1 !== null);
				$value = Helpers::validateNumericNullSubstitution($value1, 1);
				Helpers::validateNotNegative($value);
				$arrayArgs[] = (int) $value;
				$anyZeros += (int) !((bool) $value);
			}
			self::testNonNulls($anyNonNulls);
			if ($anyZeros) {
				return 0;
			}
		} catch (Exception $e) {
			return $e->getMessage();
		}

		$returnValue = 1;
		$allPoweredFactors = [];
		// Loop through arguments
		foreach ($arrayArgs as $value) {
			$myFactors = self::factors(floor($value));
			$myCountedFactors = array_count_values($myFactors);
			$myPoweredFactors = [];
			foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
				$myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower;
			}
			self::processPoweredFactors($allPoweredFactors, $myPoweredFactors);
		}
		foreach ($allPoweredFactors as $allPoweredFactor) {
			/** @var scalar $allPoweredFactor */
			$returnValue *= (int) $allPoweredFactor;
		}

		return $returnValue;
	}

	/**
	 * @param mixed[] $allPoweredFactors
	 * @param mixed[] $myPoweredFactors
	 */
	private static function processPoweredFactors(array &$allPoweredFactors, array &$myPoweredFactors): void
	{
		foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
			if (isset($allPoweredFactors[$myPoweredValue])) {
				if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
					$allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
				}
			} else {
				$allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
			}
		}
	}

	private static function testNonNulls(int $anyNonNulls): void
	{
		if (!$anyNonNulls) {
			throw new Exception(ExcelError::VALUE());
		}
	}
}
