/**
 * RSRP → color mapping with smooth gradient interpolation.
 *
 * Gradient stops are chosen to match standard RF planning tools:
 *   -130 dBm = deep blue (no service)
 *    -90 dBm = green (fair)
 *    -50 dBm = red (excellent)
 *
 * All functions are pure and allocation-free on the hot path
 * (pre-built lookup table for fast per-pixel color resolution).
 */

interface GradientStop {
  value: number; // normalized 0–1
  r: number;
  g: number;
  b: number;
}

const GRADIENT_STOPS: GradientStop[] = [
  { value: 0.0, r: 26, g: 35, b: 126 },    // #1a237e — deep blue
  { value: 0.15, r: 13, g: 71, b: 161 },    // #0d47a1
  { value: 0.25, r: 33, g: 150, b: 243 },   // #2196f3 — blue
  { value: 0.35, r: 0, g: 188, b: 212 },    // #00bcd4 — cyan
  { value: 0.45, r: 0, g: 137, b: 123 },    // #00897b — teal
  { value: 0.55, r: 76, g: 175, b: 80 },    // #4caf50 — green
  { value: 0.65, r: 139, g: 195, b: 74 },   // #8bc34a — light green
  { value: 0.75, r: 255, g: 235, b: 59 },   // #ffeb3b — yellow
  { value: 0.85, r: 255, g: 152, b: 0 },    // #ff9800 — orange
  { value: 1.0, r: 244, g: 67, b: 54 },     // #f44336 — red
];

/**
 * Pre-built 256-entry lookup table for O(1) color resolution.
 * Each entry is [r, g, b].
 */
const COLOR_LUT: Uint8Array = buildColorLUT();

function buildColorLUT(): Uint8Array {
  const size = 256;
  const lut = new Uint8Array(size * 3);

  for (let i = 0; i < size; i++) {
    const value = i / (size - 1); // 0–1
    const [r, g, b] = interpolateGradient(value);
    lut[i * 3] = r;
    lut[i * 3 + 1] = g;
    lut[i * 3 + 2] = b;
  }

  return lut;
}

function interpolateGradient(value: number): [number, number, number] {
  // Find surrounding stops
  let lower = GRADIENT_STOPS[0];
  let upper = GRADIENT_STOPS[GRADIENT_STOPS.length - 1];

  for (let i = 0; i < GRADIENT_STOPS.length - 1; i++) {
    if (value >= GRADIENT_STOPS[i].value && value <= GRADIENT_STOPS[i + 1].value) {
      lower = GRADIENT_STOPS[i];
      upper = GRADIENT_STOPS[i + 1];
      break;
    }
  }

  const range = upper.value - lower.value;
  const t = range > 0 ? (value - lower.value) / range : 0;

  return [
    Math.round(lower.r + (upper.r - lower.r) * t),
    Math.round(lower.g + (upper.g - lower.g) * t),
    Math.round(lower.b + (upper.b - lower.b) * t),
  ];
}

/**
 * Normalize RSRP (dBm) to 0–1 range.
 *
 * -130 dBm → 0.0
 *  -50 dBm → 1.0
 */
export function normalizeRSRP(rsrp: number): number {
  const minRSRP = -130;
  const maxRSRP = -50;
  const normalized = (rsrp - minRSRP) / (maxRSRP - minRSRP);
  return Math.max(0, Math.min(1, normalized));
}

/**
 * Fast color lookup from normalized value (0–1).
 * Uses pre-built LUT — O(1), no allocations.
 *
 * Returns [r, g, b] (0–255 each).
 */
export function valueToColorRGB(value: number): [number, number, number] {
  const idx = Math.max(0, Math.min(255, Math.round(value * 255))) * 3;
  return [COLOR_LUT[idx], COLOR_LUT[idx + 1], COLOR_LUT[idx + 2]];
}

/**
 * Full gradient interpolation (slower, for non-hot-path use like legends).
 */
export function valueToColor(value: number): [number, number, number] {
  return interpolateGradient(Math.max(0, Math.min(1, value)));
}

/**
 * Gaussian falloff for smooth point edges.
 *
 * Returns 0–1 weight based on distance from point center.
 * sigma defaults to radius/3 for a natural falloff.
 */
export function gaussianWeight(distance: number, radius: number, sigma?: number): number {
  const s = sigma ?? radius / 3;
  return Math.exp(-(distance * distance) / (2 * s * s));
}
