from django.db.models import signals
from .abstract_models import to_ascii, to_search
from .settings import *
from .signals import *
from .exceptions import *


def set_name_ascii(sender, instance=None, **kwargs):
    """
    Signal reciever that sets instance.name_ascii from instance.name.

    Ascii versions of names are often useful for autocompletes and search.
    """
    name_ascii = to_ascii(instance.name).strip()

    if name_ascii and not instance.name_ascii:
        instance.name_ascii = name_ascii


def set_display_name(sender, instance=None, **kwargs):
    """
    Set instance.display_name to instance.get_display_name(), avoid spawning
    queries during __str__().
    """
    instance.display_name = instance.get_display_name()


def city_country(sender, instance, **kwargs):
    if instance.region_id and not instance.country_id:
        instance.country = instance.region.country


def city_search_names(sender, instance, **kwargs):
    search_names = set()

    country_names = {instance.country.name, }
    if instance.country.alternate_names:
        for n in instance.country.alternate_names.split(';'):
            country_names.add(n)

    city_names = {instance.name, }
    if instance.alternate_names:
        for n in instance.alternate_names.split(';'):
            city_names.add(n)

    if instance.region_id:
        region_names = {instance.region.name, }
        if instance.region.alternate_names:
            for n in instance.region.alternate_names.split(';'):
                region_names.add(n)
    else:
        region_names = set()

    for city_name in city_names:
        for country_name in country_names:
            name = to_search(city_name + country_name)
            search_names.add(name)

            for region_name in region_names:
                name = to_search(city_name + region_name + country_name)
                search_names.add(name)

    instance.search_names = ' '.join(sorted(search_names))


def connect_default_signals(model_class):
    """
    Use this function to connect default signals to your custom model.
    It is called automatically, if default cities_light models are used,
    i.e. settings `CITIES_LIGHT_APP_NAME` is not changed.
    """
    if 'Country' in model_class.__name__:
        signals.pre_save.connect(set_name_ascii, sender=model_class)
    elif 'SubRegion' in model_class.__name__:
        signals.pre_save.connect(set_name_ascii, sender=model_class)
        signals.pre_save.connect(set_display_name, sender=model_class)
    elif 'Region' in model_class.__name__:
        signals.pre_save.connect(set_name_ascii, sender=model_class)
        signals.pre_save.connect(set_display_name, sender=model_class)
    elif 'City' in model_class.__name__:
        signals.pre_save.connect(set_name_ascii, sender=model_class)
        signals.pre_save.connect(set_display_name, sender=model_class)
        signals.pre_save.connect(city_country, sender=model_class)
        signals.pre_save.connect(city_search_names, sender=model_class)


def filter_non_cities(sender, items, **kwargs):
    """
    Exclude any **city** which feature code must not be included.
    By default, this receiver is connected to
    :py:func:`~cities_light.signals.city_items_pre_import`, it raises
    :py:class:`~cities_light.exceptions.InvalidItems` if the row feature code
    is not in the :py:data:`~cities_light.settings.INCLUDE_CITY_TYPES` setting.
    """
    if items[7] not in INCLUDE_CITY_TYPES:
        raise InvalidItems()


city_items_pre_import.connect(filter_non_cities)


def filter_non_included_countries_country(sender, items, **kwargs):
    """
    Exclude any **country** which country must not be included.
    This is slot is connected to the
    :py:func:`~cities_light.signals.country_items_pre_import` signal and does
    nothing by default.  To enable it, set the
    :py:data:`~cities_light.settings.INCLUDE_COUNTRIES` setting.
    """
    if INCLUDE_COUNTRIES is None:
        return

    if items[0].split('.')[0] not in INCLUDE_COUNTRIES:
        raise InvalidItems()


country_items_pre_import.connect(filter_non_included_countries_country)


def filter_non_included_countries_region(sender, items, **kwargs):
    """
    Exclude any **region** which country must not be included.
    This is slot is connected to the
    :py:func:`~cities_light.signals.region_items_pre_import` signal and does
    nothing by default.  To enable it, set the
    :py:data:`~cities_light.settings.INCLUDE_COUNTRIES` setting.
    """
    if INCLUDE_COUNTRIES is None:
        return

    if items[0].split('.')[0] not in INCLUDE_COUNTRIES:
        raise InvalidItems()


region_items_pre_import.connect(filter_non_included_countries_region)


def filter_non_included_countries_subregion(sender, items, **kwargs):
    """
    Exclude any **subregion** which country must not be included.
    This is slot is connected to the
    :py:func:`~cities_light.signals.subregion_items_pre_import` signal and does
    nothing by default.  To enable it, set the
    :py:data:`~cities_light.settings.INCLUDE_COUNTRIES` setting.
    """
    if INCLUDE_COUNTRIES is None:
        return

    if items[0].split('.')[0] not in INCLUDE_COUNTRIES:
        raise InvalidItems()


subregion_items_pre_import.connect(filter_non_included_countries_subregion)


def filter_non_included_countries_city(sender, items, **kwargs):
    """
    Exclude any **city** which country must not be included.
    This is slot is connected to the
    :py:func:`~cities_light.signals.city_items_pre_import` signal and does
    nothing by default.  To enable it, set the
    :py:data:`~cities_light.settings.INCLUDE_COUNTRIES` setting.
    """
    if INCLUDE_COUNTRIES is None:
        return

    if items[8].split('.')[0] not in INCLUDE_COUNTRIES:
        raise InvalidItems()


city_items_pre_import.connect(filter_non_included_countries_city)
