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

import re
import sys
import json
import gettext
import os
from ikabot import config
from decimal import *
from ikabot.config import *
from ikabot.helpers.getJson import *
from ikabot.helpers.gui import *

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

getcontext().prec = 30


def read(min=None, max=None, digit=False, msg=prompt, values=None, empty=False, additionalValues=None, default=None):  # user input
    """Reads input from user
    Parameters
    ----------
    min : int
        smallest number acceptable as input
    max : int
        greatest number acceptable as input
    digit : bool
        boolean indicating whether or not the input MUST be an int
    msg : str
        string printed before the user is asked for input
    values : list
        list of strings which are acceptable as input
    empty : bool
        a boolean indicating whether or not an empty string is acceptable as input
    additionalValues : list
        list of strings which are additional valid inputs. Can be used with digit = True to validate a string as an input among all digits

    Returns
    -------
    result : int | str
        int representing the user's choice
    """
    try:
        if len(config.predetermined_input) != 0:
            return config.predetermined_input.pop(0)
    except Exception:
        pass

    def _invalid():
        print('\033[1A\033[K', end="")  # remove line
        return read(min, max, digit, msg, values, additionalValues=additionalValues)

    try:
        read_input = input(msg)
    except EOFError:
        return _invalid()

    if additionalValues is not None and read_input in additionalValues:
        return read_input

    if read_input == '' and default is not None:
        return default

    if read_input == '' and empty is True:
        return read_input

    if digit is True or min is not None or max is not None:
        if read_input.isdigit() is False:
            return _invalid()
        else:
            try:
                read_input = eval(read_input)
            except SyntaxError:
                return _invalid()
    if min is not None and read_input < min:
        return _invalid()
    if max is not None and read_input > max:
        return _invalid()
    if values is not None and read_input not in values:
        return _invalid()
    return read_input


def chooseCity(session, foreign=False):
    """Prompts the user to chose a city
    Parameters
    ----------
    session : ikabot.web.session.Session
        Session object
    foreign : bool
        lets the user choose a foreign city

    Returns
    -------
    city : City
        a city object representing the chosen city
    """
    global menu_cities
    (ids, cities) = getIdsOfCities(session)
    if menu_cities == '':
        longest_city_name_length = 0
        for city_id in ids:
            length = len(cities[city_id]['name'])
            if length > longest_city_name_length:
                longest_city_name_length = length

        def pad(city_name):
            return ' ' * (longest_city_name_length - len(city_name) + 2)

        resources_abbreviations = {'1': _('(W)'), '2': _('(M)'), '3': _('(C)'), '4': _('(S)')}

        i = 0
        if foreign:
            print(_(' 0: foreign city'))
        else:
            print('')
        for city_id in ids:
            i += 1
            resource_index = cities[city_id]['tradegood']
            resource_abb = resources_abbreviations[resource_index]
            city_name = cities[city_id]['name']
            matches = re.findall(r'u[0-9a-f]{4}', city_name)
            for match in matches:
                to_unicode = '\\' + match
                to_unicode = to_unicode.encode().decode('unicode-escape')
                city_name = city_name.replace(match, to_unicode)
            num = ' ' + str(i) if i < 10 else str(i)
            menu_cities += '{}: {}{}{}\n'.format(num, city_name, pad(city_name), resource_abb)
        menu_cities = menu_cities[:-1]
    if foreign:
        print(_(' 0: foreign city'))
    print(menu_cities)

    if foreign:
        selected_city_index = read(min=0, max=len(ids))
    else:
        selected_city_index = read(min=1, max=len(ids))
    if selected_city_index == 0:
        return chooseForeignCity(session)
    else:
        html = session.get(city_url + ids[selected_city_index - 1])
        return getCity(html)


def chooseForeignCity(session):
    """Prompts the user to select an island, and a city on that island (is only used in chooseCity)
    Parameters
    ----------
    session : ikabot.web.session.Session
        Session object

    Returns
    -------
    city : City
        a city object representing the city the user chose
    """
    banner()
    x = read(msg='coordinate x:', digit=True)
    y = read(msg='coordinate y:', digit=True)
    print('')
    url = 'view=worldmap_iso&islandX={}&islandY={}&oldBackgroundView=island&islandWorldviewScale=1'.format(x, y)
    html = session.get(url)
    try:
        islands_json = re.search(r'jsonData = \'(.*?)\';', html).group(1)
        islands_json = json.loads(islands_json, strict=False)
        island_id = islands_json['data'][str(x)][str(y)][0]
    except Exception:
        print(_('Incorrect coordinates'))
        enter()
        banner()
        return chooseCity(session, foreign=True)
    html = session.get(island_url + island_id)
    island = getIsland(html)
    longest_city_name_length = 0
    for city in island['cities']:
        if city['type'] == 'city':
            city_name_length = len(city['name'])
            if city_name_length > longest_city_name_length:
                longest_city_name_length = city_name_length

    def pad(name):
        return ' ' * (longest_city_name_length - len(name) + 2)
    i = 0
    city_options = []
    for city in island['cities']:
        if city['type'] == 'city' and city['state'] == '' and city['Name'] != session.username:
            i += 1
            num = ' ' + str(i) if i < 10 else str(i)
            print('{}: {}{}({})'.format(num, city['name'], pad(city['name']), city['Name']))
            city_options.append(city)
    if i == 0:
        print(_('There are no cities where to send resources on this island'))
        enter()
        return chooseCity(session, foreign=True)
    selected_city_index = read(min=1, max=i)
    city = city_options[selected_city_index - 1]
    city['islandId'] = island['id']
    city['cityName'] = city['name']
    city['propia'] = False
    return city


def askForValue(text, max_val):
    """Displays text and asks the user to enter a value between 0 and max

    Parameters
    ----------
    text : str
        text to be displayed when asking the user for input
    max_val : int
        integer representing the number of input options

    Returns
    -------
    var : int
        integer representing the user's input
        if the user has inputed nothing, 0 will be returned instead
    """
    var = read(msg=text, min=0, max=max_val, default=0, additionalValues=['all', 'half'])
    if var == 'all':
        var = max_val
    elif var == 'half':
        var = max_val // 2
    return var


def getIdsOfCities(session, all=False):
    """Gets the user's cities
    Parameters
    ----------
    session : ikabot.web.session.Session
        Session object
    all : bool
        boolean indicating whether all cities should be returned, or only those that belong to the current user

    Returns
    -------
    (ids, cities) : tuple
        a tuple containing the a list of city IDs and a list of city objects
    """
    global cities_cache
    global ids_cache
    if ids_cache is None or cities_cache is None or session.padre is False:
        html = session.get()
        cities_cache = re.search(r'relatedCityData:\sJSON\.parse\(\'(.+?),\\"additionalInfo', html).group(1) + '}'
        cities_cache = cities_cache.replace('\\', '')
        cities_cache = cities_cache.replace('city_', '')
        cities_cache = json.loads(cities_cache, strict=False)

        ids_cache = [city_id for city_id in cities_cache]
        ids_cache = sorted(ids_cache)

    # {'coords': '[x:y] ', 'id': idCiudad, 'tradegood': '..', 'name': 'nomberCiudad', 'relationship': 'ownCity'|'occupiedCities'|..}
    if all is False:
        ids_own = [city_id for city_id in cities_cache if cities_cache[city_id]['relationship'] == 'ownCity']
        ids_other = [city_id for city_id in cities_cache if cities_cache[city_id]['relationship'] != 'ownCity']
        own_cities = cities_cache.copy()
        for id in ids_other:
            del own_cities[id]
        return ids_own, own_cities
    else:
        return ids_cache, cities_cache


def getIslandsIds(session):
    """Gets the IDs of islands the user has cities on
    Parameters
    ----------
    session : ikabot.web.session.Session
        Session object

    Returns
    -------
    islands_ids : list
        a list containing the IDs of the users islands
    """
    (cities_ids, cities) = getIdsOfCities(session)
    islands_ids = set()
    for city_id in cities_ids:
        html = session.get(city_url + city_id)
        city = getCity(html)
        island_id = city['islandId']
        islands_ids.add(island_id)
    return list(islands_ids)
