import type { INSTRUCTION, JIBESTREAM_PATH } from './JibestreamTypes';
const METERS_PER_PIXEL = 0.05962332392785686;

export function getCurrentInstruction(
  userLocation: number[],
  instructions: INSTRUCTION[]
) {
  var closestInstruction:
    | { instruction: INSTRUCTION; distance: number }
    | undefined = undefined;
  instructions.forEach(function (instruction) {
    const distance = getPerpendicularDistanceFromPath(
      userLocation,
      instruction.startPoint!,
      instruction.endPoint!
    );
    const distanceAlongPath = getDistanceAlongPath(
      userLocation,
      instruction.startPoint!,
      instruction.endPoint!
    );

    // Ignore matches if not sufficiently within bounds of path
    if (!(distanceAlongPath > -0.5 && distanceAlongPath < 1.5)) {
      return;
    }
    let remainingDistance = getDistanceBetweenPoints(
      userLocation,
      instruction.endPoint!
    );
    remainingDistance =
      Math.round(remainingDistance * METERS_PER_PIXEL * 100) / 100;
    const fullText = generateInstructionText({
      ...instruction,
      remainingDistance,
    });
    console.log();

    if (closestInstruction === undefined) {
      closestInstruction = {
        instruction: { ...instruction, remainingDistance, fullText },
        distance,
      };
      console.log(remainingDistance, userLocation, instruction.endPoint!);
    } else {
      if (distance < closestInstruction.distance) {
        closestInstruction = {
          instruction: { ...instruction, remainingDistance, fullText },
          distance,
        };
        console.log(remainingDistance, userLocation, instruction.endPoint!);
      }
    }
  });
  console.log(
    instructions.findIndex((i) => i.wp == closestInstruction?.instruction.wp)
  );
  // @ts-ignore
  console.log(closestInstruction?.instruction);
  // @ts-ignore
  return closestInstruction?.instruction;
}

export const preProcessPath = (
  path: JIBESTREAM_PATH[],
  instructions: INSTRUCTION[]
) => {
  var pathSegmentIndex = 0;
  var prevPathIndex = 0;
  var prevInstructionIndex: number;
  instructions.forEach(function (instruction, instructionIndex) {
    for (var i = pathSegmentIndex; i < path.length; i++) {
      const pathPoints = path[i]?.['_']?.points;
      if (pathPoints === undefined) continue;
      for (var j = prevPathIndex; j < pathPoints.length; j++) {
        const point = pathPoints[j]?.['_'];
        if (point === undefined) continue;
        if (point.id === instruction.wp) {
          instruction.endPoint = point.coordinates;
          if (prevInstructionIndex !== undefined) {
            instruction.startPoint =
              instructions[prevInstructionIndex]!.endPoint;
          } else {
            instruction.startPoint = pathPoints[0]?.['_'].coordinates;
          }
          prevInstructionIndex = instructionIndex;
          return;
        }
        prevPathIndex = j;
      }
      pathSegmentIndex = i;
      prevPathIndex = 0;
    }
  });
  return instructions;
};

// get distance along path as proportion of total path length
export function getDistanceAlongPath(
  point: number[],
  startPathPoint: number[],
  endPathPoint: number[]
) {
  const x1 = startPathPoint[0]!;
  const y1 = startPathPoint[1]!;
  const x2 = endPathPoint[0]!;
  const y2 = endPathPoint[1]!;
  const x0 = point[0]!;
  const y0 = point[1]!;
  const numerator = (x2 - x1) * (x0 - x1) + (y2 - y1) * (y0 - y1); // dot product
  const denominator = Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2);
  return numerator / denominator;
}

// Get perpendicular distance from point to path
export function getPerpendicularDistanceFromPath(
  point: number[],
  startPathPoint: number[],
  endPathPoint: number[]
) {
  const x1 = startPathPoint[0]!;
  const y1 = startPathPoint[1]!;
  const x2 = endPathPoint[0]!;
  const y2 = endPathPoint[1]!;
  const x0 = point[0]!;
  const y0 = point[1]!;
  const numerator = Math.abs(
    (y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1
  );
  const denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2));
  return numerator / denominator;
}

export function getDistanceBetweenPoints(point1: number[], point2: number[]) {
  const x1 = point1[0]!;
  const y1 = point1[1]!;
  const x2 = point2[0]!;
  const y2 = point2[1]!;
  return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}

export function generateInstructionText(instruction: INSTRUCTION) {
  if (instruction.destinationName)
    return `${instruction.prepend}${instruction.direction} at ${instruction.destinationName} in ${instruction.remainingDistance} meters`;
  else
    return `${instruction.prepend}${instruction.direction} in ${instruction.remainingDistance} meters`;
}
