# 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 segment to environmental zones
"""

from typing import Optional

from here.content.content import Content, ContentBinding
from here.content.hmc.baseattributes import RangedAttribute
from here.content.hmc.environmentalzones import EnvironmentalZonesAttributes
from here.content.hmc.navigationattributes import NavigationAttributes
from here.platform.adapter import Adapter


class SegmentToEnvironmentalZone(ContentBinding):
    """
    class to associate segment with environmental zones
    """

    def __init__(self, content: Content, na: str, ez: str, adapter: Optional[Adapter] = None):
        """
        :param content: Content object
        :param na: layer ID for navigation-attributes
        :param ez: layer ID for environmental-zones
        :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.navigation_attributes = NavigationAttributes(
            content, na, adapter=self._default_adapter
        )
        self.environmental_zones = EnvironmentalZonesAttributes(
            content, ez, adapter=self._default_adapter
        )

    def get_attribute(self, pid: str, adapter: Optional[Adapter] = None):
        """
        Get segment associated with environmental zones from NavigationAttribute and
        EnvironmentalZonesAttributes
        :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 -> environmental_zone_condition and
        environmental zones combined or adapter-specific. Note that environmental_zone_condition
        has applies_to attribute. This is combined with envirionmental zones attributes as dict.
        """

        content_adapter = self._content_adapter(adapter)

        # get environmental_zone_condition from NavigationAttributes
        ezc = self.navigation_attributes.get_attribute(pid, "environmental_zone_condition")

        if ezc is None:
            return None

        # get street names associated with segment
        atts = self.associate_ezc_ez(ezc)

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

    def associate_ezc_ez(self, ezc: dict):
        """
        Associate environmental_zone_condition with environmental_zone
        :param ezc: a dict of segment to environmental_zone_condition
        :return: a dict of segment to environmental_zone_condition and
        environmental_zone combined
        """

        atts: dict = {}

        for sid, directions in ezc.items():
            for d, ras in directions.items():
                for ra in ras:
                    ezpid = ra.attribute.environmental_zone_ref.partition_name
                    ezid = ra.attribute.environmental_zone_ref.identifier
                    ez = self.environmental_zones.get_attribute(ezpid, "environmental_zone")
                    new_ez = {
                        "applies_to": ra.attribute.applies_to,
                        "environmental_zone": ez[ezid],
                    }
                    atts.setdefault(sid, {})
                    atts[sid].setdefault(d, [])
                    atts[sid][d].append(RangedAttribute(ra.start_offset, ra.end_offset, new_ez))

        return atts

    def get_tabular_data_envzones(self, pid: str, atts: dict):
        """
        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
