import VFXPath from "../vfx-path.mjs";
import {generateHermiteSegment} from "../vfx-utils.mjs";

/**
 * @import {VFXBasePathPoint} from "../_types.mjs"
 */

/**
 * Generate an arcing path between provided control points using cubic hermite splines.
 * @param {VFXBasePathPoint[]} waypoints    Explicit waypoints to interpolate
 * @param {object} params                   Spline interpolation parameters
 * @returns {VFXPath}                       A generated arc path
 */
export default function arcPath(waypoints, params={}) {
  const arcPoints = [];
  for (let i=0; i<waypoints.length-1; i++) {
    const p0 = waypoints[i];
    const p1 = waypoints[i+1];
    const segment = generateArcPoints(p0, p1, params);
    arcPoints.push(...segment.slice(Math.min(i, 1)));
  }
  return new VFXPath(arcPoints);
}

/* -------------------------------------------- */

/**
 * Generate cubic hermite spline points for a pair of control points.
 * @param {VFXBasePathPoint} origin           Starting point of the arc
 * @param {VFXBasePathPoint} destination      Ending point of the arc
 * @param {object} options                    Configuration options
 * @param {number} [options.peakRatio=0.5]      Position along path where arc peaks (0-1)
 * @param {number} [options.peakHeight=0.3]     Height of arc as ratio of path length
 * @param {number} [options.direction=1]        Direction of arc perpendicular to path (1 for "up", -1 for "down")
 * @param {number} [options.numPoints]          Number of points to generate (default: half of distance in pixels)
 * @param {number} [options.tangentScale=1]     Scaling factor for tangent vectors. Determines how curvy the arc is
 * @param {string[]} [options.auxiliary]        An array of auxiliary parameter names
 * @returns {VFXBasePathPoint[]}              Array of path points
 */
export function generateArcPoints(origin, destination, {numPoints, peakRatio=0.5, peakHeight=0.3, direction=1,
  tangentScale=1, auxiliary}={}) {

  // Verify straight-line distance
  const dx = destination.x - origin.x;
  const dy = destination.y - origin.y;
  const distance = Math.hypot(dx, dy);
  if (distance === 0) return [origin, destination];

  // Prepare parameters
  auxiliary ||= ["elevation", "sort"];
  numPoints ||= Math.max(10, Math.floor(distance / 2));
  direction = (direction >= 0 ? 1 : -1);
  const perpX = -dy / distance;
  const perpY = dx / distance;

  // Calculate peak point position
  const peakBaseX = origin.x + (dx * peakRatio);
  const peakBaseY = origin.y + (dy * peakRatio);
  const peakOffsetX = perpX * peakHeight * distance * direction * -1;
  const peakOffsetY = perpY * peakHeight * distance * direction * -1;
  const peakX = peakBaseX + peakOffsetX;
  const peakY = peakBaseY + peakOffsetY;

  // Vector at peak is just the scaled source to destination vector
  const tangentPeak = {x: (destination.x - origin.x) * tangentScale, y: (destination.y - origin.y) * tangentScale};
  const peak = {
    x: peakX,
    y: peakY,
    elevation: Math.mix(origin.elevation, destination.elevation, peakRatio),
    sort: Math.mix(origin.sort || 0, destination.sort || 0, peakRatio)
  };

  // Generate the two Hermite segments
  const nRising = Math.floor(numPoints * peakRatio);
  const nFalling = numPoints - nRising;
  const tangentOrigin = {x: 0, y: 0};
  const segment1 = generateHermiteSegment(origin, tangentOrigin, peak, tangentPeak, nRising, auxiliary);
  const segment2 = generateHermiteSegment(peak, tangentPeak, destination, tangentOrigin, nFalling, auxiliary);
  return [...segment1, ...segment2.slice(1)];
}
