#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import gettext
import traceback
from ikabot.config import *
from ikabot.helpers.botComm import *
from ikabot.helpers.pedirInfo import *
from ikabot.helpers.signals import setInfoSignal
from ikabot.helpers.getJson import getCity
from ikabot.helpers.planRoutes import executeRoutes
from ikabot.helpers.resources import *
from ikabot.helpers.varios import addThousandSeparator
from ikabot.helpers.process import set_child_mode
from ikabot.helpers.gui import banner

t = gettext.translation('distributeResources', localedir, languages=languages, fallback=True)
_ = t.gettext


def distributeResources(session, event, stdin_fd, predetermined_input):
    """
    Parameters
    ----------
    session : ikabot.web.session.Session
    event : multiprocessing.Event
    stdin_fd: int
    predetermined_input : multiprocessing.managers.SyncManager.list
    """
    sys.stdin = os.fdopen(stdin_fd)
    config.predetermined_input = predetermined_input
    try:
        banner()

        print(_('What resource do you want to distribute?'))
        print(_('(0) Exit'))
        for i in range(len(materials_names)):
            print('({:d}) {}'.format(i+1, materials_names[i]))
        resource = read(min=0, max=5)
        if resource == 0:
            event.set()  # give main process control before exiting
            return
        resource -= 1

        if resource == 0:
            evenly = True
        else:
            print('\nHow do you want to distribute the resources?')
            print('1) From cities that produce them to cities that do not')
            print('2) Distribute them evenly among all cities')
            type_distribution = read(min=1, max=2)
            evenly = type_distribution == 2

        if evenly:
            routes = distribute_evenly(session, resource)
        else:
            routes = distribute_unevenly(session, resource)

        if routes is None:
            event.set()
            return

        banner()
        print(_('\nThe following shipments will be made:\n'))
        for route in routes:
            print('{} -> {} : {} {}'.format(route[0]['name'], route[1]['name'], route[resource+3], materials_names[resource]))  # displays all routes to be executed in console

        print(_('\nProceed? [Y/n]'))
        rta = read(values=['y', 'Y', 'n', 'N', ''])
        if rta.lower() == 'n':
            event.set()
            return

    except KeyboardInterrupt:
        event.set()
        return

    set_child_mode(session)
    event.set()  # this is where we give back control to main process

    info = _('\nDistribute {}\n').format(materials_names[resource])
    setInfoSignal(session, info)

    try:
        executeRoutes(session, routes)  # plan trips for all the routes
    except Exception as e:
        msg = _('Error in:\n{}\nCause:\n{}').format(info, traceback.format_exc())
        sendToBot(session, msg)  # sends message to telegram bot
    finally:
        session.logout()


def distribute_evenly(session, resource_type):
    """
    Parameters
    ----------
    session : ikabot.web.session.Session
    resource_type : int
    """
    resourceTotal = 0
    (cityIDs, cities) = getIdsOfCities(session)

    originCities = {}
    destinationCities = {}
    allCities = {}
    for cityID in cityIDs:

        html = session.get(city_url + cityID)  # load html from the get request for that particular city
        city = getCity(html)  # convert the html to a city object

        resourceTotal += city['recursos'][resource_type]  # the cities resources are added to the total
        allCities[cityID] = city  # adds the city to all cities

    # if a city doesn't have enough storage to fit resourceAverage
    # ikabot will send enough resources to fill the store to the max
    # then, resourceAverage will be recalculated
    resourceAverage = resourceTotal // len(allCities)
    while True:

        len_prev = len(destinationCities)
        for cityID in allCities:
            if cityID in destinationCities:
                continue
            freeStorage = allCities[cityID]['freeSpaceForResources'][resource_type]
            storage = allCities[cityID]['storageCapacity']
            if storage < resourceAverage:
                destinationCities[cityID] = freeStorage
                resourceTotal -= storage

        resourceAverage = resourceTotal // (len(allCities) - len(destinationCities))

        if len_prev == len(destinationCities):
            for cityID in allCities:
                if cityID in destinationCities:
                    continue
                if allCities[cityID]['recursos'][resource_type] > resourceAverage:
                    originCities[cityID] = allCities[cityID]['recursos'][resource_type] - resourceAverage
                else:
                    destinationCities[cityID] = resourceAverage - allCities[cityID]['recursos'][resource_type]
            break

    originCities = {k: v for k, v in sorted(originCities.items(), key=lambda item: item[1], reverse=True)}  # sort origin cities in descending order
    destinationCities = {k: v for k, v in sorted(destinationCities.items(), key=lambda item: item[1])}  # sort destination cities in ascending order

    routes = []

    for originCityID in originCities:  # iterate through all origin city ids

        for destinationCityID in destinationCities:  # iterate through all destination city ids
            if originCities[originCityID] == 0 or destinationCities[destinationCityID] == 0:
                continue

            if originCities[originCityID] > destinationCities[destinationCityID]:  # if there's more resources above average in the origin city than resources below average in the destination city (origin city needs to have a surplus and destination city needs to have a deficit of resources for a route to be considered)
                toSend = destinationCities[destinationCityID]  # number of resources to send is the number of resources below average in destination city
            else:
                toSend = originCities[originCityID]  # send the amount of resources above average of the current origin city

            if toSend == 0:
                continue

            toSendArr = [0] * len(materials_names)
            toSendArr[resource_type] = toSend
            route = (allCities[originCityID], allCities[destinationCityID], allCities[destinationCityID]['islandId'], *toSendArr)
            routes.append(route)

            # ROUTE BLOCK
            if originCities[originCityID] > destinationCities[destinationCityID]:
                originCities[originCityID] -= destinationCities[destinationCityID]  # remove the sent amount from the origin city's surplus
                destinationCities[destinationCityID] = 0  # set the amount of resources below average in destination city to 0
            else:
                destinationCities[destinationCityID] -= originCities[originCityID]  # remove the sent amount from the amount of resources below average in current destination city
                originCities[originCityID] = 0  # set the amount of resources above average in origin city to 0

    return routes


def distribute_unevenly(session, resource_type):
    """
    Parameters
    ----------
    session : ikabot.web.session.Session
    resource_type : int
    """
    total_available_resources_from_all_cities = 0
    (cities_ids, cities) = getIdsOfCities(session)
    origin_cities = {}
    destination_cities = {}
    for destination_city_id in cities_ids:
        is_city_mining_this_resource = cities[destination_city_id]['tradegood'] == str(resource_type)
        if is_city_mining_this_resource:
            html = session.get(city_url + destination_city_id)
            city = getCity(html)
            if resource_type == 1:  # wine
                city['available_amount_of_resource'] = city['recursos'][resource_type] - city['consumo'] - 1
            else:
                city['available_amount_of_resource'] = city['recursos'][resource_type]
            if city['available_amount_of_resource'] < 0:
                city['available_amount_of_resource'] = 0
            total_available_resources_from_all_cities += city['available_amount_of_resource']
            origin_cities[destination_city_id] = city
        else:
            html = session.get(city_url + destination_city_id)
            city = getCity(html)
            city['free_storage_for_resource'] = city['freeSpaceForResources'][resource_type]
            if city['free_storage_for_resource'] > 0:
                destination_cities[destination_city_id] = city

    if total_available_resources_from_all_cities <= 0:
        print(_('\nThere are no resources to send.'))
        enter()
        return None
    if len(destination_cities) == 0:
        print(_('\nThere is no space available to send resources.'))
        enter()
        return None

    remaining_resources_to_be_sent_to_each_city = total_available_resources_from_all_cities // len(destination_cities)
    free_storage_available_per_city = [destination_cities[city]['free_storage_for_resource'] for city in destination_cities]
    total_free_storage_available_in_all_cities = sum(free_storage_available_per_city)
    remaining_resources_to_send = min(total_available_resources_from_all_cities, total_free_storage_available_in_all_cities)
    toSend = {}

    while remaining_resources_to_send > 0:
        len_prev = len(toSend)
        for city_id in destination_cities:
            city = destination_cities[city_id]
            if city_id not in toSend and city['free_storage_for_resource'] < remaining_resources_to_be_sent_to_each_city:
                toSend[city_id] = city['free_storage_for_resource']
                remaining_resources_to_send -= city['free_storage_for_resource']

        if len(toSend) == len_prev:
            for city_id in destination_cities:
                if city_id not in toSend:
                    toSend[city_id] = remaining_resources_to_be_sent_to_each_city
            break

        free_storage_available_per_city = [destination_cities[city]['free_storage_for_resource'] for city in destination_cities if city not in toSend]
        if len(free_storage_available_per_city) == 0:
            break
        total_free_storage_available_in_all_cities = sum(free_storage_available_per_city)
        remaining_resources_to_send = min(remaining_resources_to_send, total_free_storage_available_in_all_cities)
        remaining_resources_to_be_sent_to_each_city = remaining_resources_to_send // len(free_storage_available_per_city)

    routes = []
    for destination_city_id in destination_cities:
        destination_city = destination_cities[destination_city_id]
        island_id = destination_city['islandId']
        missing_resources = toSend[destination_city_id]
        for origin_city_id in origin_cities:
            if missing_resources == 0:
                break

            origin_city = origin_cities[origin_city_id]
            resources_available_in_this_city = origin_city['available_amount_of_resource']
            for route in routes:
                origin = route[0]
                resource = route[resource_type + 3]
                if origin['id'] == origin_city_id:
                    resources_available_in_this_city -= resource

            send_this_round = min(missing_resources, resources_available_in_this_city)
            available = destination_city['free_storage_for_resource']
            if available == 0 or send_this_round == 0:
                continue

            if available < send_this_round:
                missing_resources = 0
                send_this_round = available
            else:
                missing_resources -= send_this_round

            toSendArr = [0] * len(materials_names)
            toSendArr[resource_type] = send_this_round
            route = (origin_city, destination_city, island_id, *toSendArr)

            routes.append(route)

    return routes
