/**
* [Latitude, Longitude]
*/
export type Coords = [number, number];

/**
* Convert degrees to radians
*/
export function degreesToRadians(deg: number) {
  return deg * (Math.PI / 180);
}

/**
* Convert dms (degrees/minutes/seconds) string to decimal degrees
*   toDecimal(`40 20 50W`)   => -40.34722
*   toDecimal(`40°20'50" S`) => -40.34722
*/
export function dmsToDecimal(str: string) {
  const lastChar = str.slice(-1).toLowerCase();
  let negative = false;

  // south and west => negative
  if (lastChar === 'w' || lastChar === 's') negative = true;

  // convert strings to numbers
  const values = str
    .split(/[^0-9.]/)
    .filter((x) => x !== '') // remove empty values
    .map((x) => parseFloat(x));

  // make sure array has length 3
  for (let i = 0; i < 3; i++) {
    values[i] = values[i] || 0;
  }

  const result = values[0] + (values[1] / 60) + (values[2] / 3600);
  return negative ? -result : result;
}

const haversine = {
  /**
  * Configurable earthRadius (in meters)
  */
  earthRadius: 6371000,

  /**
  * Get distance between two points
  */
  distance(coords1: Coords, coords2: Coords) {
    const [lat1, lon1] = coords1;
    const [lat2, lon2] = coords2;

    const latitudeDifference = degreesToRadians(lat2 - lat1);
    const logitudeDifference = degreesToRadians(lon2 - lon1);

    const n =
      Math.sin(latitudeDifference / 2) * Math.sin(latitudeDifference / 2) +
      Math.cos(degreesToRadians(lat1)) * Math.cos(degreesToRadians(lat2)) *
      Math.sin(logitudeDifference / 2) * Math.sin(logitudeDifference / 2);

    const distance = 2 * Math.atan2(Math.sqrt(n), Math.sqrt(1 - n));

    return this.earthRadius * distance;
  },

  dmsToDecimal
};

export default haversine;


