import click
import pandas as pd
import requests
import json
import os
import sys
from openpyxl import Workbook
from carpalrouting.grouping import LocationGrouping, DriverGrouping
from carpalrouting.optimize import Routing
from carpalrouting.models import Driver, Location, Coordinates, GeoArea

@click.command()
@click.option('--file', help='The absolute path of setting.json file')
def cli(file):
    if not file:
        print('Error. No schema file provided.')
    else:
        try:
            with open(file) as setting_file:
                print('Loading setting file...')
                settings = json.load(setting_file)

                print('Reading driver data...')
                drivers = settings.get('drivers')

                print('Reading location data...')
                df = pd.read_excel(settings.get('inputFile'))
                locations = {}
                for index, row in df.iterrows():
                    pickup_address = row['pickup_location_address']
                    pickup_window = tuple(map_time_to_seconds(row['pickup_time_window']))

                    if pickup_address in locations:                
                        new_item = {'address': row['delivery_address'],
                                    'coordinates': map_address_to_coordinates(row['delivery_address'], settings.get('googleAPIKey')),
                                    'delivery_window': map_time_to_seconds(row['delivery_time_window']),
                                    'capacity': row['total_weight'],
                                    'order_time': 0}
                        if pickup_window in locations[pickup_address]:             
                            locations[pickup_address][pickup_window].append(new_item)
                        else:
                            locations[pickup_address][pickup_window] = [{
                                'address': pickup_address,
                                'coordinates': map_address_to_coordinates(pickup_address, settings.get('googleAPIKey')),
                                'delivery_window': map_time_to_seconds(row['pickup_time_window']),
                                'capacity': 0,
                                'order_time': 0
                            }]
                            locations[pickup_address][pickup_window].append(new_item)
                    else:
                        locations[pickup_address] = {}

                        locations[pickup_address][pickup_window] = [{
                            'address': pickup_address,
                            'coordinates': map_address_to_coordinates(pickup_address, settings.get('googleAPIKey')),
                            'delivery_window': map_time_to_seconds(row['pickup_time_window']),
                            'capacity': 0,
                            'order_time': 0
                        }]

                        locations[pickup_address][pickup_window].append({
                            'address': row['delivery_address'],
                            'coordinates': map_address_to_coordinates(row['delivery_address'], settings.get('googleAPIKey')),
                            'delivery_window': map_time_to_seconds(row['delivery_time_window']),
                            'capacity':row['total_weight'],
                            'order_time': 0
                        })

                schedules = DriverGrouping().group_drivers_by_time_slot(drivers=[Driver(id=item.get('id'),
                                                                                        time_slots=item.get('time_slots'),
                                                                                        capacity=item.get('capacity'),
                                                                                        speed=item.get('speed')) for item in drivers])
                                                    
                data = LocationGrouping().group_locations(locations, schedules)

                print('Start route optimization...')
                book = Workbook()
                sheet = book.active
                
                for pickup_address, pickup_windows in data.items():
                    for pickup_window, obj in pickup_windows.items():
                        locs = obj.get('locations')
                        driver_ids = obj.get('driver_ids')
                        while locs and driver_ids:
                            driver = None 
                            for item in drivers:
                                if item.get('id') == driver_ids[0]:
                                    driver = item
                                    break

                            #find the indices for all filtered locations in data object
                            filtered_indices = []
                            for k, v in enumerate(locs):
                                if v.capacity <= driver.get('capacity'):
                                    filtered_indices.append(k)
                                    
                            indices = Routing(locations=[loc for loc in locs if loc.capacity <= driver.get('capacity')],
                                              num_vehicles=len(locs)).generate_routes(vehicle_capacity=driver.get('capacity'),
                                                                                      speed=driver.get('speed'),
                                                                                      service_time_unit=settings['serviceTime'])
                            if len(indices) == 0:
                            
                                break
                            else:
                                #remove driver from available driver list only if a route has been built
                                driver_ids.pop(0)                            

                                routes = []
                                for item in indices[0]:
                                    routes.append(locs[filtered_indices[item]])
                                    if filtered_indices[item] != 0:
                                        locs[filtered_indices[item]] = None

                                locs = list(filter(lambda x: x is not None, locs))
                                sheet.append(tuple([item.address for item in routes]))
                                                                
                        obj['locations'] = locs if len(locs) > 1 else [] 
                book.save(settings.get('outputFile'))

                if settings['publicDriver']['required']:
                    print('Using public drivers...')

                    for pickup_address, pickup_windows in data.items():
                        for pickup_window, obj in pickup_windows.items():
                            locs = obj.get('locations')

                            if locs:
                                indices = Routing(locations=locs,
                                                  num_vehicles=len(locs)).generate_routes(vehicle_capacity=settings['publicDriver']['specification']['capacity'],
                                                                                          speed=settings['publicDriver']['specification']['speed'],
                                                                                          service_time_unit=settings['serviceTime'])
                                if len(indices) == 0:                    
                                    break
                                else:                                                
                                    for idx_list in indices:
                                        routes = []

                                        for item in idx_list:
                                            routes.append(locs[item])
                                            if item != 0:
                                                locs[item] = None

                                        sheet.append(tuple([item.address for item in routes]))
                                        
                                    locs = list(filter(lambda x: x is not None, locs))
                                    obj['locations'] = locs if len(locs) > 1 else []
                    book.save(settings.get('outputFile'))
        except Exception as e:
            print('Error. Unable to load the setting file.')
            sys.exit()

def map_address_to_coordinates(address, key):
    response = requests.get('https://maps.googleapis.com/maps/api/geocode/json?address={}&key={}'.format(address, key))
    content = json.loads(response.__dict__.get('_content'))
    if content.get('results'):
        loc = content.get('results')[0].get('geometry').get('location')
        return [loc.get('lat'), loc.get('lng')]
    else:
        return [0, 0]

def map_time_to_seconds(time):
    window = time.split('-')
    start = window[0].split(':')
    end = window[1].split(':')

    return [int(start[0])*3600+int(start[1])*60, int(end[0])*3600+int(end[1])*60]

if __name__ == '__main__':
    cli()