##
#  Copyright (c) 2015, Tyler Finethy, David Jurgens
#
#  All rights reserved. See LICENSE file for details
##

"""
Geocoder and Reverse-Geocoder to be used by the Geolocation Project
Allows for multiple dataset inputs
"""

import os, os.path
import csv
import re
import logging
import sys
import gzip
import json

LOGGER = logging.getLogger(os.path.basename(__file__))

class Geocoder(object):

    """
    Geocoder to be used on the Geolocation Inference Project.
    """
    def __init__(self,dataset="geonames"):
        """
        Initializes the "geocoder" dictionary from geonames
        """
        self.abbv_to_state = state_abbv_data()
        self.state_abbv_regex = re.compile(r'(\b' + (r'\b|\b'.join(self.abbv_to_state.keys())) + r'\b)')
        self.lc_name_to_location = {}

        LOGGER.debug("Geocoder loading city-location mapping from %s" % (dataset))


        if dataset == "geonames":
            data = geonames_data()

            line_no = 0
            for line in data[1:]:
                #TODO this city name should be formatted the same as incoming tweets
                city_name = line[0].lower()
                if not city_name:
                    continue

                line_no += 1
                if line_no % 1000000 == 0:
                    LOGGER.debug("currently read %d locations from %s" %
                                 (line_no, dataset))

                lat = float(line[1])
                lon = float(line[2])
                self.lc_name_to_location[city_name] = (lat, lon)


        else:
            raise NotImplementedError(dataset)


        LOGGER.debug("Geocoder loaded %d locations from %s" %
                     (len(self.lc_name_to_location), dataset))


    def geocode_noisy(self, location_name):
        """
        Returns the latitude and longitude (tuple) of a noisy location name
        (e.g., the location field of a social media user's profile).  If your
        input isn't cleaned, you probably want this method instead of geocode().
        """

        usaRegex = re.compile("\\busa\\b")
        usRegex = re.compile("\\bus\\b")
        ukRegex = re.compile("\\buk\\b")
        
        name = location_name.lower()
        name = name.strip()

        # Correct for a few common noisy prefices
        if name.startswith("the city of "):
            name = name[12:] #.substring("the city of ".length())
        if name.startswith("downtown "):
            name = name[9:] #.substring("downtown ".length())

        # Swap out the three common contry abbrevations
        name = re.sub(usaRegex, "united states", name)
        name = re.sub(usRegex, "united states", name)
        name = re.sub(ukRegex, "united kingdom", name)

        # Substitute out state names from the US
        matches = re.search(self.state_abbv_regex, name)
        if not matches is None:
            abbv = matches.group(0)
            expanded = name[:matches.start(0)] + self.abbv_to_state[abbv] + name[matches.end(0):]
            #print "%s:: %s -> %s" % (abbv, name, expanded)
            name = expanded

        # Once we've matched abbreivations, lower case for all further
        # comparisons
        name = name.lower()

        if name == "washington, d.c." or name == "washington dc" or name == "washington, dc":
            return (38.904722, -77.016389)

        # Strip off all the cruft on either side
        name = re.sub(r'^[\W+]+', " ", name)
        name = re.sub(r'[\W+]+$', " ", name)
        name = name.strip()

        # Rename the dict for brevity since we're going to referencing it a lot
        # in the next section
        locs = self.lc_name_to_location

        # Last ditch effort: just try matching the whole name and hope it's
        # a single unambiguous city match
        if name in locs:
            return locs[name]

        try:
            if name.find(':') > 0:
                idx = name.find(':')
                coords = name[idx+1:].replace(' ','').split(',')