import pandas as pd    # For the dataframe
import math            # For math


class UniformMapGenerator:

    def __init__(self):
        pass

    def get_uniform_coordinate_map(self,
                                   lat_min,
                                   lat_max,
                                   lng_min,
                                   lng_max,
                                   lat_res,
                                   lng_res,
                                   debug=False):
        """
        Outputs a pandas.DataFrame object that consists of a point for each
        latitude longitude pair generated by iterating through each latitude
        between lat_min and lat_max with a step size of lat_res and each
        longitude between lng_min and lng_max with a step size of lng_res.

        Args:
            lat_min (float): minimum latitude
            lat_max (float): maximum latitude
            lng_min (float): minimum longitude
            lng_max (float): maximum longitude
            lat_res (float): latitude resolution. This is the distance between
                generated points in degrees for latitude.
            lng_res (float): longitude resolution. This is hte distance between
                generated points in degrees for longitude
            debug (boolean): if true, debug text is output to the console
        Returns:
            pandas.DataFrame with two columns: latitude, longitude
        """

        latitude_num = self.get_number_of_intervals(lat_min, lat_max, lat_res)
        longitude_num = self.get_number_of_intervals(lng_min, lng_max, lng_res)
        total_coordinate_rows = latitude_num * longitude_num

        if debug:
            print('Latitude Quantity: ' + str(latitude_num))
            print('Longitude Quantity: ' + str(longitude_num))
            print('Total Output Rows: ' + str(total_coordinate_rows))

        output_df = self.instantiate_output_dataframe(total_coordinate_rows)

        # Iterate through our latitudes and longitudes, adding all pairs
        # to the output array
        row_num = 0
        for lat in self.get_next_coordinate(lat_min, lat_max, lat_res):
            for lng in self.get_next_coordinate(lng_min, lng_max, lng_res):
                # Add the lat/lng pair to the output dataframe
                output_df.loc[row_num] = [lat, lng]
                row_num += 1
        return output_df

    # Returns the number of intervals (int) between minimum and maximum that
    # include the minimum and maximum where each interval is 'resolution'
    # distance apart
    def get_number_of_intervals(self, minimum, maximum, resolution):
        return int(math.ceil((maximum - minimum) / resolution + 1))

    def instantiate_output_dataframe(self, total_rows):
        return pd.DataFrame(columns=['latitude', 'longitude'],
                            index=xrange(0, total_rows))

    # This functions identically to numpy.arange, except it is a generator
    def get_next_coordinate(self, beginning, end, interval):
        i = beginning
        while i < end + interval:
            yield i
            i += interval
