"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.convertDirection = convertDirection;
exports.convertDistanceUnit = convertDistanceUnit;
exports.convertPrepend = convertPrepend;
exports.convertUnitName = convertUnitName;
exports.generateGeojsonPath = void 0;
exports.generateInstructionText = generateInstructionText;
exports.getCurrentInstruction = getCurrentInstruction;
exports.getDistanceAlongPath = getDistanceAlongPath;
exports.getDistanceBetweenPoints = getDistanceBetweenPoints;
exports.getDistanceFromPoints = getDistanceFromPoints;
exports.getPerpendicularDistanceFromPath = getPerpendicularDistanceFromPath;
exports.getSurroundingPoints = getSurroundingPoints;
exports.preProcessPath = void 0;
const SLOW_WALKING_SPEED = 1.18;
function getCurrentInstruction(userLocation, instructions) {
  var _closestInstruction;
  let dist_unit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'METERS';
  let locale = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'en-US';
  // calculate pixel scaling factor
  var METERS_PER_PIXEL = 0.05962332392785686 * 4;
  if (instructions.length !== 0 && instructions[0].distance !== undefined && instructions[0].startPoint !== undefined && instructions[0].endPoint !== undefined) {
    var instruction = instructions[0];
    var dist = instruction.distance;
    var pixelDist = getDistanceBetweenPoints(instruction.startPoint, instruction.endPoint);
    METERS_PER_PIXEL = dist / pixelDist;
  }
  var closestInstruction = undefined;
  instructions.forEach(function (instruction) {
    const closestPointDist = getDistanceFromPoints(userLocation, instruction.startPoint, instruction.endPoint);
    const perpDist = getPerpendicularDistanceFromPath(userLocation, instruction.startPoint, instruction.endPoint);
    const {
      distOffPath,
      ratioAlongPath
    } = getDistanceAlongPath(userLocation, instruction.startPoint, instruction.endPoint);
    const withinPath = ratioAlongPath > 0 && ratioAlongPath < 1;
    let remainingDistance = getDistanceBetweenPoints(userLocation, instruction.endPoint);
    remainingDistance = Math.round(remainingDistance * METERS_PER_PIXEL * 100) / 100;
    const fullText = generateInstructionText({
      ...instruction,
      remainingDistance
    }, dist_unit, locale);
    var isBetterInstruction = false;
    if (closestInstruction === undefined) {
      isBetterInstruction = true;
    } else {
      if (Math.abs(closestPointDist - closestInstruction.closestPointDist) < 0.1) {
        // Start/end point of multiple paths are the same distance (near a corner)
        if (withinPath && !closestInstruction.withinPath) {
          // Outside corner of path, but only within current path bounds. Choose current path
          isBetterInstruction = true;
        } else if (!withinPath && closestInstruction.withinPath) {
          // outside corner of path, but only within previous path bounds. Keep previous path
          isBetterInstruction = false;
        } else if (withinPath && closestInstruction.withinPath) {
          // inside corner of path. Choose the instruction with the lowest perperndiclar distance
          if (perpDist < closestInstruction.perpDist) {
            isBetterInstruction = true;
          }
        } else {
          // Outside corner of path. Choose the instruction that the point is closest to being within (from distanceAlongPath distance)
          if (distOffPath < closestInstruction.distOffPath) {
            isBetterInstruction = true;
          }
        }
      } else if (closestPointDist < closestInstruction.closestPointDist) {
        // Closer to current instruction point
        isBetterInstruction = true;
      }
    }
    if (isBetterInstruction) {
      closestInstruction = {
        instruction: {
          ...instruction,
          remainingDistance,
          fullText
        },
        closestPointDist,
        withinPath,
        perpDist,
        distOffPath
      };
    }
  });
  // @ts-ignore
  return (_closestInstruction = closestInstruction) === null || _closestInstruction === void 0 ? void 0 : _closestInstruction.instruction;
}
function getSurroundingPoints(path, instructions, waypointId) {
  let instructionIndex = 0;
  let leadWaypointIndex = -1;
  for (let i = 0; i < path.points.length; i++) {
    var _path$points$i, _path$points$i2, _instructions$instruc;
    if (path.points[i] && ((_path$points$i = path.points[i]) === null || _path$points$i === void 0 ? void 0 : _path$points$i.id) == waypointId) {
      leadWaypointIndex = instructionIndex;
      break;
    }
    if (instructions[instructionIndex] && ((_path$points$i2 = path.points[i]) === null || _path$points$i2 === void 0 ? void 0 : _path$points$i2.id) == ((_instructions$instruc = instructions[instructionIndex]) === null || _instructions$instruc === void 0 ? void 0 : _instructions$instruc.wp)) {
      instructionIndex += 1;
    }
  }
  return leadWaypointIndex;
}
const preProcessPath = (path, instructions) => {
  var pathSegmentIndex = 0;
  var prevPathIndex = 0;
  var prevInstructionIndex;
  path.forEach(function (floorPath) {
    floorPath.points.forEach(function (pathSegment) {
      var _pathSegment$associat;
      if (pathSegment.associations && (_pathSegment$associat = pathSegment.associations) !== null && _pathSegment$associat !== void 0 && _pathSegment$associat.amenities) {
        var _pathSegment$associat2;
        let foundDoor = false;

        // Check if this waypoint is a door
        (_pathSegment$associat2 = pathSegment.associations) === null || _pathSegment$associat2 === void 0 || _pathSegment$associat2.amenities.forEach(function (amenity) {
          if (amenity == 13237) {
            foundDoor = true;
          }
        });

        // if the waypoint has a door find, inject a new nav instruction into the path.
        if (foundDoor) {
          var _instructions$index, _instructions$index2;
          const index = getSurroundingPoints(floorPath, instructions, pathSegment.id);
          const splitInstruction = {
            fullText: 'Proceed through the Door',
            prepend: '',
            direction: 'Proceed through the Door',
            distance: ((_instructions$index = instructions[index]) === null || _instructions$index === void 0 ? void 0 : _instructions$index.distance) || 1,
            remainingDistance: ((_instructions$index2 = instructions[index]) === null || _instructions$index2 === void 0 ? void 0 : _instructions$index2.remainingDistance) || 1,
            wp: pathSegment.id,
            dest: -1,
            startPoint: [],
            endPoint: []
          };
          instructions.splice(index, 0, splitInstruction);
        }
      }
    });
  });
  instructions.forEach(function (instruction, instructionIndex) {
    for (var i = pathSegmentIndex; i < path.length; i++) {
      var _path$i;
      const pathPoints = (_path$i = path[i]) === null || _path$i === void 0 ? void 0 : _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.x, point.y];
          instruction.endPointLngLat = [point.longitude, point.latitude];
          if (prevInstructionIndex !== undefined) {
            instruction.startPoint = instructions[prevInstructionIndex].endPoint;
            instruction.startPointLngLat = instructions[prevInstructionIndex].endPointLngLat;
          } else {
            var _pathPoints$, _pathPoints$2, _pathPoints$3, _pathPoints$4;
            instruction.startPoint = [(_pathPoints$ = pathPoints[0]) === null || _pathPoints$ === void 0 ? void 0 : _pathPoints$.x, (_pathPoints$2 = pathPoints[0]) === null || _pathPoints$2 === void 0 ? void 0 : _pathPoints$2.y];
            instruction.startPointLngLat = [(_pathPoints$3 = pathPoints[0]) === null || _pathPoints$3 === void 0 ? void 0 : _pathPoints$3.longitude, (_pathPoints$4 = pathPoints[0]) === null || _pathPoints$4 === void 0 ? void 0 : _pathPoints$4.latitude];
          }
          prevInstructionIndex = instructionIndex;
          return;
        }
        prevPathIndex = j;
      }
      pathSegmentIndex = i;
      prevPathIndex = 0;
    }
  });
  return instructions;
};
exports.preProcessPath = preProcessPath;
const generateGeojsonPath = (processedInstructions, jibestreamPath, distUnit, locale) => {
  const coordinates = [];
  jibestreamPath.forEach((path, floorIndex) => {
    path.points.forEach((point, pointIndex) => {
      if (floorIndex == 0 && pointIndex == 0) {
        coordinates.push([point.longitude, point.latitude]);
      } else if (processedInstructions.some(value => {
        return value.wp == point.id;
      })) coordinates.push([point.longitude, point.latitude]);
    });
  });
  let totalDistance = 0;
  const steps = [];
  for (let i = 0; i < processedInstructions.length; i++) {
    const instruction = processedInstructions[i];
    totalDistance += instruction.distance;
    const endPointLngLat = instruction.endPointLngLat;
    const step = {
      distance: instruction.distance,
      directions: generateInstructionText(instruction, distUnit, locale),
      lon: endPointLngLat[0],
      lat: endPointLngLat[1],
      prepend: instruction.prepend,
      direction: instruction.direction
    };
    steps.push(step);
  }
  const duration = Math.round(totalDistance / SLOW_WALKING_SPEED * 100) / 100;
  const startTime = Date.now();
  const endTime = startTime + duration * 1000;
  const path = {
    distance: totalDistance,
    duration,
    startTime,
    endTime,
    legGeometry: {
      properties: {
        name: 'wayfind-path-geojson'
      },
      type: 'Feature',
      geometry: {
        type: 'LineString',
        coordinates
      }
    },
    steps: steps
  };
  return path;
};

// get distance along path as proportion of total path length
exports.generateGeojsonPath = generateGeojsonPath;
function getDistanceAlongPath(point, startPathPoint, endPathPoint) {
  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);
  const totalPathDist = Math.sqrt(denominator);
  const ratioAlongPath = numerator / denominator;
  const distanceAlongPath = ratioAlongPath * totalPathDist;
  var distOffPath = 0;
  if (ratioAlongPath < 0) {
    distOffPath = -distanceAlongPath;
  } else if (ratioAlongPath > 1) {
    distOffPath = distanceAlongPath - totalPathDist;
  }
  return {
    distanceAlongPath,
    ratioAlongPath,
    totalPathDist,
    distOffPath
  };
}

// Get perpendicular distance from point to path
function getPerpendicularDistanceFromPath(point, startPathPoint, endPathPoint) {
  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;
}

// Get perpendicular distance from point to path
function getDistanceFromPoints(point, startPathPoint, endPathPoint) {
  return Math.min(getDistanceBetweenPoints(point, startPathPoint), getDistanceBetweenPoints(point, endPathPoint));
}
function getDistanceBetweenPoints(point1, point2) {
  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));
}
function generateInstructionText(instruction, dist_unit, locale) {
  const distance = Math.ceil(convertDistanceUnit(instruction.remainingDistance, dist_unit));
  const prepend = convertPrepend(instruction.prepend, locale);
  const direction = convertDirection(instruction.direction, locale);
  const atTranslated = locale === 'es-ES' ? 'en' : 'at';
  const destinationName = instruction.destinationName;
  const inTranslated = locale === 'es-ES' ? 'en' : 'in';
  const unitName = convertUnitName(dist_unit, locale, distance != 1);
  if (destinationName) return `${prepend}${direction} ${atTranslated} ${destinationName} ${inTranslated} ${distance} ${unitName}`;else return `${prepend}${direction} ${inTranslated} ${distance} ${unitName}`;
}
function convertDistanceUnit(distance, dist_unit) {
  switch (dist_unit) {
    case 'METERS':
      return distance;
    case 'FEET':
      return distance * 3.28084;
    case 'INCHES':
      return distance * 39.3701;
    default:
      return distance;
  }
}
function convertPrepend(prepend, locale) {
  switch (prepend) {
    case 'Turn ':
      switch (locale) {
        case 'es-ES':
          return 'Gire ';
        default:
          return prepend;
      }
    case 'Arrive ':
      switch (locale) {
        case 'es-ES':
          return 'Llegue ';
        default:
          return prepend;
      }
    default:
      return prepend;
  }
}
function convertDirection(prepend, locale) {
  switch (prepend) {
    case 'Left':
      switch (locale) {
        case 'es-ES':
          return 'Izquierdo';
        default:
          return prepend;
      }
    case 'Right':
      switch (locale) {
        case 'es-ES':
          return 'Derecho';
        default:
          return prepend;
      }
    case 'Forward':
      switch (locale) {
        case 'es-ES':
          return 'hacia adelante';
        default:
          return prepend;
      }
    default:
      return prepend;
  }
}
function convertUnitName(unit, locale, plural) {
  switch (unit) {
    case 'METERS':
      switch (locale) {
        case 'es-ES':
          return plural ? 'metros' : 'metro';
        default:
          return plural ? 'meters' : 'meter';
      }
    case 'FEET':
      switch (locale) {
        case 'es-ES':
          return plural ? 'pies' : 'pie';
        default:
          return plural ? 'feet' : 'foot';
      }
    case 'INCHES':
      switch (locale) {
        case 'es-ES':
          return plural ? 'pulgada' : 'pulgadas';
        default:
          return plural ? 'inches' : 'inch';
      }
    default:
      return unit;
  }
}
//# sourceMappingURL=Instructions.js.map