#!/usr/bin/python2
# coding: utf8

from __future__ import absolute_import
from math import radians, cos, sin, asin, sqrt
from geocoder.location import Location

AVG_EARTH_RADIUS = 6371  # in km


def Distance(*args, **kwargs):
    total = 0.0
    last = None

    if len(args) == 1 and isinstance(args, (list, tuple)):
        args = args[0]

    if len(args) <= 1:
        raise ValueError("Distance needs at least two locations")

    for location in args:
        if last:
            distance = haversine(Location(last), Location(location), **kwargs)
            if distance:
                total += distance
        last = location

    return total


def haversine(point1, point2, **kwargs):
    """ Calculate the great-circle distance bewteen two points on the Earth surface.

    :input: two 2-tuples, containing the latitude and longitude of each point
    in decimal degrees.

    Example: haversine((45.7597, 4.8422), (48.8567, 2.3508))

    :output: Returns the distance bewteen the two points.
    The default unit is kilometers. Miles can be returned
    if the ``miles`` parameter is set to True.

    """

    lookup_units = {
        'miles': 'miles',
        'mile': 'miles',
        'mi': 'miles',
        'ml': 'miles',
        'kilometers': 'kilometers',
        'kilometres': 'kilometers',
        'kilometer': 'kilometers',
        'kilometre': 'kilometers',
        'km': 'kilometers',
        'meters': 'meters',
        'metres': 'meters',
        'meter': 'meters',
        'metre': 'meters',
        'm': 'meters',
        'feet': 'feet',
        'f': 'feet',
        'ft': 'feet',
    }

    if point1.ok and point2.ok:
        # convert all latitudes/longitudes from decimal degrees to radians
        lat1, lng1, lat2, lng2 = list(map(radians, point1.latlng + point2.latlng))

        # calculate haversine
        lat = lat2 - lat1
        lng = lng2 - lng1
        d = sin(lat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(lng / 2) ** 2
        h = 2 * AVG_EARTH_RADIUS * asin(sqrt(d))

        # Measurements
        units = kwargs.get('units', 'kilometers').lower()
        units_calculation = {
            'miles': h * 0.621371,
            'feet': h * 0.621371 * 5280,
            'meters': h * 1000,
            'kilometers': h,
        }

        if units in lookup_units:
            return units_calculation[lookup_units[units]]
        else:
            raise ValueError("Unknown units of measurement")

    else:
        print(u'[WARNING] Error calculating the following two locations.\n'
              'Points: {0} to {1}'.format(point1.location, point2.location))

if __name__ == '__main__':
    d = Distance('Ottawa, ON', 'Toronto, ON', 'Montreal, QC')
    print(d)
