export const clamp = (value: number, min: number, max: number): number =>
  Math.max(min, Math.min(value, max));

export function precisionRound(number: number, precision = 1): number {
  let factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}

/**
 * Решение скопировано без изменений у MUI:
 * https://github.com/mui/material-ui/blob/v5.13.7/packages/mui-base/src/useSlider/useSlider.ts#L89-L105
 */
function getDecimalPrecision(num: number) {
  // This handles the case when num is very small (0.00000001), js will turn this into 1e-8.
  // When num is bigger than 1 or less than -1 it won't get converted to this notation so it's fine.
  if (Math.abs(num) < 1) {
    const parts = num.toExponential().split('e-');
    const matissaDecimalPart = parts[0].split('.')[1];
    return (matissaDecimalPart ? matissaDecimalPart.length : 0) + parseInt(parts[1], 10);
  }

  const decimalPart = num.toString().split('.')[1];
  return decimalPart ? decimalPart.length : 0;
}

function roundValueToStep(value: number, step: number, min: number) {
  const nearest = Math.round((value - min) / step) * step + min;
  return Number(nearest.toFixed(getDecimalPrecision(step)));
}

function decimatedClamp(val: number, min: number, max: number, step?: number) {
  if (step == null || step <= 0) {
    return clamp(val, min, max);
  }
  const roundedValue = roundValueToStep(val, step, min);
  return clamp(roundedValue, min, max);
}

export function rescale(
  value: number,
  from: [number, number],
  to: [number, number],
  options: { step?: number } = {},
): number {
  const scaled = ((value - from[0]) / (from[1] - from[0])) * (to[1] - to[0]) + to[0];
  return decimatedClamp(scaled, to[0], to[1], options.step);
}
