// deck.gl-community
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {Vector2} from '@math.gl/core';

/** GeoJSON style position coordinate vector */
export type Position = [number, number] | [number, number, number];

/** [red, green, blue, alpha] in premultiplied alpha format */
export type Color = [number, number, number, number];

export interface PathMarker {
  position: Position;
  angle: number;
  color: Color;
  object: unknown;
}

function getLineLength(vPoints) {
  // calculate total length
  let lineLength = 0;
  for (let i = 0; i < vPoints.length - 1; i++) {
    lineLength += vPoints[i].distance(vPoints[i + 1]);
  }
  return lineLength;
}

const DEFAULT_COLOR = [0, 0, 0, 255];
const DEFAULT_DIRECTION = {forward: true, backward: false};

export function createPathMarkers({
  data,
  getPath = (x, context) => x.path,
  getDirection = x => x.direction,
  getColor = _x => DEFAULT_COLOR,
  getMarkerPercentages = (x, info) => [0.5],
  projectFlat
}): PathMarker[] {
  const markers: PathMarker[] = [];

  for (const object of data) {
    const path = getPath(object, null);
    const direction = getDirection(object) || DEFAULT_DIRECTION;
    const color = getColor(object);

    const vPoints = path.map(p => new Vector2(p));
    const vPointsReverse = vPoints.slice(0).reverse();

    // calculate total length
    const lineLength = getLineLength(vPoints);

    // Ask for where to put markers
    const percentages = getMarkerPercentages(object, {lineLength});

    // Create the markers
    for (const percentage of percentages) {
      if (direction.forward) {
        const marker = createMarkerAlongPath({
          path: vPoints,
          percentage,
          lineLength,
          color,
          object,
          projectFlat
        });
        markers.push(marker);
      }

      if (direction.backward) {
        const marker = createMarkerAlongPath({
          path: vPointsReverse,
          percentage,
          lineLength,
          color,
          object,
          projectFlat
        });
        markers.push(marker);
      }
    }
  }

  return markers;
}

function createMarkerAlongPath({
  path,
  percentage,
  lineLength,
  color,
  object,
  projectFlat
}): PathMarker {
  const distanceAlong = lineLength * percentage;
  let currentDistance = 0;
  let previousDistance = 0;
  let i = 0;
  for (i = 0; i < path.length - 1; i++) {
    currentDistance += path[i].distance(path[i + 1]);
    if (currentDistance > distanceAlong) {
      break;
    }
    previousDistance = currentDistance;
  }

  // If reached the end of the loop without exiting early,
  // undo the final increment to avoid a null-pointer exception
  if (i === path.length - 1) {
    i -= 1;
  }

  const vDirection = path[i + 1].clone().subtract(path[i]).normalize();
  const along = distanceAlong - previousDistance;
  const vCenter = vDirection.clone().multiply(new Vector2(along, along)).add(path[i]);

  const vDirection2 = new Vector2(projectFlat(path[i + 1])).subtract(projectFlat(path[i]));

  const angle = (vDirection2.verticalAngle() * 180) / Math.PI;

  return {position: [vCenter.x, vCenter.y, 0], angle, color, object};
}
