# Copyright (C) 2020-2021 HERE Global B.V. and its affiliate(s).
# All rights reserved.
#
# This software and other materials contain proprietary information
# controlled by HERE and are protected by applicable copyright legislation.
# Any use and utilization of this software and other materials and
# disclosure to any third parties is conditional upon having a separate
# agreement with HERE for the access, use, utilization or disclosure of this
# software. In the absence of such agreement, the use of the software is not
# allowed.

"""
Helper functions to handle multilayer attributes
"""
from typing import Optional

from here.content.content import Content, ContentBinding
from here.content.hmc.addressattributes import AddressAttributes
from here.content.hmc.administrativelocations import AdministrativeLocationsAttributes
from here.content.hmc.administrativeplaces import AdministrativePlaces
from here.platform.adapter import Adapter

admin_categories = (
    "country",
    "region",
    "state",
    "county",
    "city",
    "district",
    "sub_district",
    "postal_code",
    "further_zone",
)


class SegmentToAdminBoundary(ContentBinding):
    """
    class to associate segment with administrative boundary
    """

    def __init__(
        self, content: Content, aa: str, ap: str, al: str, adapter: Optional[Adapter] = None
    ):
        """
        :param content: Content object
        :param aa: layer ID for address-attributes
        :param ap: layer ID for administrative-places
        :param al: layer ID for administrative-locations
        :param adapter: the ``Adapter`` to transform data between different representations,
            ``None`` to use the adapter defined in ``content``.
        """
        super().__init__(content, adapter)

        # Attribute bindings used internally should always use the default adapter
        self.address_attributes = AddressAttributes(content, aa, adapter=self._default_adapter)
        self.admin_places = AdministrativePlaces(content, ap, adapter=self._default_adapter)
        self.admin_locations = AdministrativeLocationsAttributes(
            content, al, adapter=self._default_adapter
        )

    def get_attribute(self, pid, adapter: Optional[Adapter] = None):
        """
        Get administrative boundaries
        :param pid: partition ID
        :param adapter: the ``Adapter`` to transform data between different representations,
            ``None`` to use the adapter defined in the constructor or, if unset, in ``content``.
        :return: a dict of place -> location or adapter-specific
        """

        content_adapter = self._content_adapter(adapter)

        # get AddressAttributes for this pid
        atts = self.address_attributes.get_attribute(pid, "administrative_context_attribute")

        if atts is None:
            return None

        # get boundaries for each place name
        ab = self.associate_context_place_location(atts)

        if content_adapter is None:
            return ab
        else:
            columns, data = self.get_tabular_data_administrative_boundaries(pid, ab)
            return content_adapter.from_tabular(columns, data)

    def associate_context_place_location(self, acs):
        """
        Associate administrative context with location via place
        :param acs: administrative_context attribute
        :return: dict of place -> location
        """
        attributes = {}

        for sid, directions in acs.items():
            for d, ranges in directions.items():
                for ra in ranges:
                    for cat in admin_categories:
                        atts = getattr(ra.attribute.administrative_context, cat)
                        for att in atts:
                            place_pid = att.place_ref.partition_name
                            place_id = att.place_ref.identifier
                            ap = self.admin_places.get_attribute(place_pid, "place")
                            place = ap[place_id]
                            if place_pid in attributes and place_id in attributes[place_pid]:
                                continue
                            location_pid = place.location_ref.partition_name
                            location_id = place.location_ref.identifier
                            al = self.admin_locations.get_attribute(location_pid, "location")
                            location = al[location_id]
                            attributes.setdefault(place_pid, {})
                            attributes[place_pid][place_id] = location

        return attributes

    def get_tabular_data_administrative_boundaries(self, pid, atts):
        """
        Get tabular data
        :param pid: partition ID
        :param atts: a dict of name -> attribute
        :return: columns, data
        """
        data = []
        for place_pid, locations in atts.items():
            for place_id, location in locations.items():
                data.append([pid, place_pid, place_id, location])
        return ("partitionId", "place_pid", "place_id", "location"), data
