import datetime, json
import threading
import time
from itertools import zip_longest
import requests
from staticmap import StaticMap, CircleMarker
from colour import Color
import argparse
import oauthlib
import requests
from requests_oauthlib import OAuth1Session, OAuth1

token_url = 'https://account.api.here.com/oauth2/token'
matrix_url = "https://matrix.router.hereapi.com/v8/matrix"
matrix_status_url = "https://matrix.router.hereapi.com/v8/matrix/{0}/status"
matrix_retrieve_url = "https://matrix.router.hereapi.com/v8/matrix/{0}"

granularity = 0.0050
commute_range = 75
work_group_size = 100
color_scale_width = 60 # this is equal to minutes until full red saturation, 60 == 60 minutes
alpha = "70"
blot_size = 13
map_scale = 11
reverse = True

def get_bearer_token():
    data = {"grant_type": "client_credentials"}
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    oauth = OAuth1(client_key, client_secret=client_secret)
    r = requests.post(url=token_url, auth=oauth, headers=headers, data=data)
    access_token = json.loads(r.content)['access_token']
    return access_token


# Groups home_points into n groups, fills the non-fits with fillvalue
def group_elements(home_points, n, fillvalue=None):
    args = [iter(home_points)] * n  # this is some 9000IQ syntax
    return zip_longest(*args, fillvalue=fillvalue)


# This is called with the
class Worker(threading.Thread):
    def __init__(self, destination, coord_batch, graphical_map, access_token, backwards=False):
        threading.Thread.__init__(self)

        self.coord_batch = coord_batch
        self.access_token = access_token
        self.graphical_map = graphical_map
        self.destination = destination
        self.backwards = backwards

    def run(self):


        start_points = []
        for idx, tup in enumerate(self.coord_batch):
            if tup is not None:
                start_points.append({'lat': tup[0], 'lng': tup[1]})

        if self.backwards is True:
            origins = [self.destination]
            destinations = start_points
        else:
            origins = start_points
            destinations = [self.destination]

        request_payload = {
            "origins": origins,
            "destinations": destinations,
            "regionDefinition": {
                "type": "world",
                # "type": "circle",
                # "center": self.destination,
                # "radius": 10000
            }
        }

        headers = {
            'Authorization': 'Bearer {}'.format(self.access_token),
            'Content-Type': 'application/json'
        }

        response = requests.request("POST", matrix_url, headers=headers, data=json.dumps(request_payload))
        post_response = json.loads(response.text)
        matrix_id = post_response['matrixId']


        while True:
            response = requests.request("GET", matrix_status_url.format(matrix_id), headers=headers, allow_redirects=False)
            jsons = json.loads(response.text)
            if jsons['status'] == "inProgress" or jsons['status'] == "accepted":
                time.sleep(10)
                continue
            elif jsons['status'] == "completed":
                break
            else:
                print("big problems " + response.text)
                break

        headers = {
            'Authorization': 'Bearer {}'.format(self.access_token),
            # 'Content-Type': 'application/json',
            'Accept - Encoding': 'gzip'
        }

        response = requests.request("GET", matrix_retrieve_url.format(matrix_id), headers=headers)
        jsons = json.loads(response.text)

        green = Color("green")
        colors = list(green.range_to(Color("red"), color_scale_width))

        for idx, tup in enumerate(self.coord_batch):
            try:
                timelen = jsons["matrix"]["travelTimes"][idx]
            except:
                continue

            try:
                if jsons["matrix"]["errorCodes"][idx] != 0:
                    error = True
                else:
                    error = False
            except:
                error = False
                pass

            tup = (tup[1], tup[0])
            col_val = int((timelen / 60)) # set the color bucket to the number of minutes
            # so this means that full saturation will == the number of minutes when setting color_scale_width

            if error is True:
                marker = CircleMarker(tup, "blue", 6)
            elif col_val >= color_scale_width:
                # the + 70 here is just setting the aplha value
                marker = CircleMarker(tup, Color("red").get_hex_l() + alpha, blot_size)
            else:
                if col_val == color_scale_width / 2:
                    marker = CircleMarker(tup, colors[col_val].get_hex_l() + "FF", blot_size)
                else:
                    marker = CircleMarker(tup, colors[col_val].get_hex_l() + alpha, blot_size)

            self.graphical_map.add_marker(marker)

        print("Done with this one")


def entry():

    access_token = get_bearer_token()

    url = 'http://a.tile.osm.org/{z}/{x}/{y}.png'
    url = 'https://cartodb-basemaps-b.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}.png '
    graphical_map = StaticMap(3000, 3000, url_template=url)

    # Since we are testing commute-to, we set the destination of "work"
    destination_point = {'lat': 47.5121104, "lng": -121.8852897}

    # Populate this list with an complete xy grid of points centered on destination_point
    coord_grid = []

    for x in range(-commute_range, commute_range):
        for y in range(-commute_range, commute_range):
            x_point = destination_point['lat'] + granularity * x
            y_point = destination_point['lng'] + granularity * y
            coord_grid.append((x_point, y_point))

    coords_left_to_query = len(coord_grid)

    # dispatch the workers to the pool
    worker_pool = []
    for coord_batch in group_elements(coord_grid, work_group_size):
        w = Worker(destination_point, coord_batch, graphical_map, access_token, backwards=reverse)
        w.start()
        worker_pool.append(w)

    # Print out our progress as wel join up the workers
    for w in worker_pool:
        w.join()
        coords_left_to_query -= work_group_size
        print("Coords left to query: {}".format(coords_left_to_query))

    marker = CircleMarker((destination_point['lng'], destination_point['lat']), "red", 12)
    graphical_map.add_marker(marker)

    # finish up our work with the map
    image = graphical_map.render(zoom=map_scale)
    image.save('output-map.png')


# Program start

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--key", help="")
parser.add_argument("--secret", help="")
args = parser.parse_args()

client_key = args.key
client_secret = args.secret

entry()
