# Copyright (C) 2020-2022 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 segment to street names
"""

from typing import Optional

from here.content.content import Content, ContentBinding
from here.content.hmc.addressattributes import AddressAttributes
from here.content.hmc.baseattributes import RangedAttribute
from here.content.hmc.streetnames import StreetNames
from here.platform.adapter import Adapter


class SegmentToStreetName(ContentBinding):
    """
    class to associate segment with street name
    """

    def __init__(self, content: Content, aa: str, sn: str, adapter: Optional[Adapter] = None):
        """
        :param content: Content object
        :param aa: layer ID for address-attributes
        :param sn: layer ID for street-names
        :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.street_names = StreetNames(content, sn, adapter=self._default_adapter)

    def get_attribute(self, pid, adapter: Optional[Adapter] = None):
        """
        Get segment associated streetnames from AddressAttribute and StreetName
        :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 segment -> streetname or adapter-specific
        """

        content_adapter = self._legacy_hmc_content_adapter(adapter)

        # create AddressAttributes object
        aa = self.address_attributes.get_attribute(pid, "street_section")

        if aa is None:
            return None

        # get all partition Id's associated with street sections
        street_section_partition_ids = self.get_streetsection_partition_ids(aa)

        # create StreetNames object
        sn = {}

        # fill attributes
        for scpid in street_section_partition_ids:
            sn[scpid] = self.street_names.get_attribute(scpid, "street")

        # get street names associated with segment
        atts = self.associate_streetnames_streetsections(aa, sn)

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

    def get_streetsection_partition_ids(self, streetsections):
        """
        Get all partition IDs associated with street sections
        :param streetsections: a dict of segment to street section
        :return: a set of partition IDs
        """
        scpids = set()
        for sid, directions in streetsections.items():
            for d, ras in directions.items():
                for ra in ras:
                    scpids.add(ra.attribute.street_section_ref.partition_name)
        return scpids

    def associate_streetnames_streetsections(self, streetsections, streets):
        """
        Associate streetsections with streets
        :param streetsections: a dict of segment to street section
        :param streets: a dict of street section to street name
        :return: a dict of segment to street name
        """

        streetnames = {}

        for sid, directions in streetsections.items():
            for d, ras in directions.items():
                for ra in ras:
                    scpid = ra.attribute.street_section_ref.partition_name
                    scid = ra.attribute.street_section_ref.identifier
                    streetname = None
                    if scpid in streets and scid in streets[scpid]:
                        streetname = streets[scpid][scid].street_name
                    streetnames.setdefault(sid, {})
                    streetnames[sid].setdefault(d, [])
                    streetnames[sid][d].append(
                        RangedAttribute(ra.start_offset, ra.end_offset, streetname)
                    )

        return streetnames

    def get_tabular_data_streetnames(self, pid, atts):
        """
        Get tabular data
        :param pid: partition ID
        :param atts: a dict of segment -> attribute
        :return: columns, data
        """
        columns = (
            "partitionId",
            "segmentId",
            "direction",
            "start_offset",
            "end_offset",
            "attribute",
        )
        data = []
        for sid, directions in atts.items():
            for direction, ranges in directions.items():
                if direction != 1:
                    continue
                for ra in ranges:
                    data.append(
                        [pid, sid, direction, ra.start_offset, ra.end_offset, ra.attribute]
                    )
        return columns, data
