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

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

/**
 * Generate an oscillating weave 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 weave path
 */
export default function weavePath(waypoints, params = {}) {
  const weavePoints = [];
  for (let i=0; i<waypoints.length-1; i++) {
    const p0 = waypoints[i];
    const p1 = waypoints[i + 1];
    const segment = generateWeavePoints(p0, p1, params);
    weavePoints.push(...segment.slice(Math.min(i, 1)));
  }
  return new VFXPath(weavePoints);
}

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

/**
 * 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.arcCount=1]         Number of Hermite arcs (1 = single arc, 2 = up/down pair, etc.)
 * @param {number} [options.amplitude=0.15]     Ratio of the path length that determines the peak displacement
 *                                              of the arcs (1 = full path length)
 * @param {number} [options.tangentScale=1]     Multiplier applied to the Hermite tangents
 * @param {number} [options.direction=1]        Starting direction of the weave (1 = "up", -1 = "down")
 * @param {number} [options.segmentPoints=8]    Points generated per Hermite segment
 * @param {string[]} [options.auxiliary]        An array of auxiliary parameter names
 * @returns {VFXBasePathPoint[]}              Array of path points
 */
export function generateWeavePoints(origin, destination, {arcCount = 1, amplitude=0.15, tangentScale=1, direction=1,
  segmentPoints=8, 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"];
  direction = (direction >= 0 ? 1 : -1);
  const segmentCount = Math.max(1, Math.round(arcCount));
  const resolution = Math.max(3, Math.floor(segmentPoints));
  const perpX = -dy / distance;
  const perpY = dx / distance;
  const baseAmplitude = Math.abs(amplitude) * distance;

  // Create weave points
  const points = [];
  for ( let segmentIndex=0; segmentIndex<segmentCount; segmentIndex++ ) {
    const startT = segmentIndex / segmentCount;
    const endT = (segmentIndex + 1) / segmentCount;
    const p0 = {
      x: origin.x + (dx * startT),
      y: origin.y + (dy * startT),
      ...interpolateProperties(origin, destination, startT, auxiliary)
    };
    const p1 = {
      x: origin.x + (dx * endT),
      y: origin.y + (dy * endT),
      ...interpolateProperties(origin, destination, endT, auxiliary)
    };

    const segmentDx = p1.x - p0.x;
    const segmentDy = p1.y - p0.y;
    const sign = direction * ((segmentIndex % 2 === 0) ? 1 : -1);
    const baseTangent = {x: segmentDx * tangentScale, y: segmentDy * tangentScale};
    const offsetVector = {x: perpX * baseAmplitude * sign * -1, y: perpY * baseAmplitude * sign * -1};
    const startTangent = {x: baseTangent.x + offsetVector.x, y: baseTangent.y + offsetVector.y};
    const endTangent = {x: baseTangent.x - offsetVector.x, y: baseTangent.y - offsetVector.y};

    // Generate weave segment
    const segment = generateHermiteSegment(p0, startTangent, p1, endTangent, resolution, auxiliary);
    points.push(...segment.slice(Math.min(segmentIndex, 1)));
  }
  return points;
}

