#!/usr/bin/python3

"""Find a zip code at the center of n zip codes."""

import sys
import math

# Zipcode||Latitude||Longitude||State||City||County
# 00501||40.817967||-73.045257||NY||HOLTSVILLE||SUFFOLK
by_zip = {}
by_lat_long = {}

# This is pretty much an invariant
# latitude_scale = 69.1
# This varies quite a lot; this number is kind of close for SoCal but known to be a bit off.  It's really for 40 degrees North (or
# South), of the equator, and we're more like 33 degrees.
# longitude_scale = 53
# see below for a better calculation - in the final approximate_distance_in_miles method


class Point:
    """A class to hold latitude and longitude pairs, along with zip code, city, stat and country."""

    def __init__(self, zip_code, latitude, longitude, state, city, county):
        """Initialize."""
        self.zip_code = zip_code
        self.latitude = float(latitude)
        self.longitude = float(longitude)
        self.state = state
        self.city = city
        self.county = county

    def __str__(self):
        """Convert to string."""
        tuple_ = (self.zip_code, self.latitude, self.longitude, self.state, self.city, self.county)
        return '%s / lat: %.2f / lon: %.2f / %s / %s / %s' % tuple_

    # based on "improved accuracy" at http://www.meridianworlddata.com/distance-calculation.asp
    def approximate_distance_in_miles(self, other_latitude, other_longitude):
        """Approximate the distance in miles, from one lat/long to another."""
        scale = 69.1
        x = scale * (other_latitude - self.latitude)
        y = scale * (other_longitude - self.longitude) * math.cos(self.latitude / 57.3)
        return (x ** 2 + y ** 2) ** 0.5


file = open('zipcodes_2006.txt', 'r')
for line in file:
    fields = line.rstrip().split('|')
    zip_code = fields[0]
    latitude = fields[2]
    longitude = fields[4]
    state = fields[6]
    city = fields[8]
    county = fields[10]
    p = Point(zip_code, latitude, longitude, state, city, county)
    by_zip[zip_code] = p
    by_lat_long[(latitude, longitude)] = p
file.close()

num_zips = 0
total_latitude = 0
total_longitude = 0
for zip_code in sys.argv[1:]:
    num_zips += 1
    total_latitude += by_zip[zip_code].latitude
    total_longitude += by_zip[zip_code].longitude
    print('got', by_zip[zip_code])

average_latitude = total_latitude / num_zips
average_longitude = total_longitude / num_zips


def closest_zip(by_zip, lat, long):
    """Find the closest zip code."""
    best_zip = -1
    best_distance = None
    for zip_code, point in by_zip.items():
        current_distance = point.approximate_distance_in_miles(lat, long)
        if best_distance is None or best_distance > current_distance:
            best_distance = current_distance
            best_zip = zip_code
    return best_zip


best_zip = closest_zip(by_zip, average_latitude, average_longitude)

print('result is', by_zip[best_zip])
print()
for zip_code in sys.argv[1:]:
    tuple_ = (
        zip_code,
        by_zip[best_zip].approximate_distance_in_miles(by_zip[zip_code].latitude, by_zip[zip_code].longitude),
        best_zip,
    )
    print('%s: about %.1f miles from %s as the crow flies' % tuple_)