// mathUtils.js
// General math utilities extracted from tsunamiVector3Functions.js and tsunamiUtilityFunctions.js

export function clamp(value, min, max) {
  return Math.min(Math.max(value, min), max);
}
export function toDegrees(radians) {
  return (radians * 180) / Math.PI;
}
export function toRadians(degrees) {
  return (degrees * Math.PI) / 180;
}
export function slerp(p0, p1, t) {
  const angle = angleBetween(p0, p1);
  const sinOfAngle = math.sin(angle);
  return (p0 * sin((1 - t) * angle)) / sinOfAngle + (p1 * sin(t * angle)) / sinOfAngle;
}
export function fade(t) {
  return t * t * t * (t * (t * 6 - 15) + 10);
}

export function lerp(a, b, t) {
  return a + (b - a) * t;
}
export function hash2D(x, y) {
  const dot = x * 127.1 + y * 311.7;
  const s = Math.sin(dot) * 43758.5453;
  return s - Math.floor(s);
}
export function hash3D(x, y, z) {
  return x * 31 + y * 37 + z * 41;
}
export function randomInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
export function valueNoise2D(x, y, frequency = 1) {
  x *= frequency;
  y *= frequency;
  const x0 = Math.floor(x);
  const y0 = Math.floor(y);
  const x1 = x0 + 1;
  const y1 = y0 + 1;
  const tx = x - x0;
  const ty = y - y0;
  const u = fade(tx);
  const v = fade(ty);
  const n00 = hash2D(x0, y0);
  const n10 = hash2D(x1, y0);
  const n01 = hash2D(x0, y1);
  const n11 = hash2D(x1, y1);
  const nx0 = lerp(n00, n10, u);
  const nx1 = lerp(n01, n11, u);
  return lerp(nx0, nx1, v);
}
export function boxBlur(grid) {
  function getNeighbors(x, z) {
    const neighbors = [];
    for (let i = x - 1; i <= x + 1; i++) {
      for (let j = z - 1; j <= z + 1; j++) {
        if (i >= 0 && i < gridSize && j >= 0 && j < gridSize) {
          neighbors.push(grid[i * gridSize + j]);
        }
      }
    }
    return neighbors;
  }
  const gridSize = Math.sqrt(grid.length);
  const modifiedGrid = [];
  for (let i = 0; i < gridSize; i++) {
    modifiedGrid.push([]);
    for (let j = 0; j < gridSize; j++) {
      modifiedGrid[i].push({ ...grid[i * gridSize + j] });
    }
  }
  for (let i = 0; i < gridSize; i++) {
    for (let j = 0; j < gridSize; j++) {
      const currentPixel = modifiedGrid[i][j];
      const neighbors = getNeighbors(i, j);
      let totalBrightness = 0;
      for (const neighbor of neighbors) {
        totalBrightness += neighbor.z;
      }
      const avgBrightness = totalBrightness / neighbors.length;
      currentPixel.z = avgBrightness;
    }
  }
  return modifiedGrid;
}
export function randomChance(x = 0.5, y = 1) {
  return Math.random() < x / y;
}
export function weightedRandom(entries) {
  const totalWeight = entries.reduce((total, entry) => total + entry.weight, 0);
  const randomNumber = totalWeight * Math.random();
  let cumulativeWeight = 0;
  for (const entry of entries) {
    cumulativeWeight += entry.weight;
    if (cumulativeWeight >= randomNumber) {
      return entry;
    }
  }
}
export function chunkSeed(coords, chunkSize = 16) {
  const chunkCoords = blockToChunkCoords(coords, chunkSize);
  return hash3D(chunkCoords.x, chunkCoords.y, chunkCoords.z);
}
export function valueNoise2D(x, y, frequency = 1) {
  x *= frequency;
  y *= frequency;

  const x0 = Math.floor(x);
  const y0 = Math.floor(y);
  const x1 = x0 + 1;
  const y1 = y0 + 1;

  const tx = x - x0;
  const ty = y - y0;
  const u = fade(tx);
  const v = fade(ty);

  const n00 = hash2D(x0, y0);
  const n10 = hash2D(x1, y0);
  const n01 = hash2D(x0, y1);
  const n11 = hash2D(x1, y1);

  const nx0 = lerp(n00, n10, u);
  const nx1 = lerp(n01, n11, u);
  return lerp(nx0, nx1, v); // stays in [0, 1]
}
