﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace Meadow.Services.Geocoding
{
    /// <summary>
    /// Contains utilities for processing geography.
    /// </summary>
    public static class GeoUtils
    {
        /// <summary>
        /// The radius of the earth in meters. This is the average of authalic radius and approximation radius.
        /// </summary>
        public const double EarthRadius = 6371.9;

        /// <summary>
        /// Returns the distance between two coordinates, in kilometers.
        /// </summary>
        /// <param name="lat1">The first latitude.</param>
        /// <param name="lon1">The first longitude.</param>
        /// <param name="lat2">The second latitude.</param>
        /// <param name="lon2">The second longitude.</param>
        /// <returns>The distance between two coordinates.</returns>
        [SuppressMessage("ReSharper", "ExceptionNotDocumented")]
        public static double GetDistanceBetweenTwoCoordinates(double lat1, double lon1, double lat2, double lon2)
        {
            lon1 -= lon2;

            // Magic numbers are magical. (Pi / 180)
            lon1 *= 0.017453292;
            lat1 *= 0.017453292;
            lat2 *= 0.017453292;

            double dz = Math.Sin(lat1) - Math.Sin(lat2);
            double dx = Math.Cos(lon1) * Math.Cos(lat1) - Math.Cos(lat2);
            double dy = Math.Sin(lon1) * Math.Cos(lat1);
            
            return Math.Asin(Math.Sqrt(dx * dx + dy * dy + dz * dz) / 2) * 2 * EarthRadius;
        }

        /// <summary>
        /// Returns the distance between two coordinates, in kilometers.
        /// </summary>
        /// <param name="a">The first coordinate.</param>
        /// <param name="b">The second coordinate.</param>
        /// <param name="units">The unit of measurement the result should be in.</param>
        /// <returns>The distance between two coordinates.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static double GetDistanceBetweenTwoCoordinates(ICoordinates a, ICoordinates b, EDistanceUnits units = EDistanceUnits.Kilometers)
        {
            return GetDistanceBetweenTwoCoordinates(a.Latitude, a.Longitude, b.Latitude, b.Longitude);
        }
    }
}
