# 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.

"""
Provide access to HERE Map Content catalogs.
"""
from enum import Enum
from typing import Optional, Union

from here.content.content import Content
from here.content.hmc.adasattributes import AdasAttributes
from here.content.hmc.addressattributes import AddressAttributes
from here.content.hmc.administrativelocations import AdministrativeLocationsAttributes
from here.content.hmc.administrativeplaces import AdministrativePlaces
from here.content.hmc.advancednavigationattributes import AdvancedNavigationAttributes
from here.content.hmc.baseattributes import BaseLayer
from here.content.hmc.navigationattributes import NavigationAttributes
from here.content.hmc.roadattributes import RoadAttributes
from here.content.hmc.streetnames import StreetNames
from here.content.hmc.topologygeometry import TopologyGeometry
from here.content.hmc.trafficpatterns import TrafficPatterns
from here.platform import Platform
from here.platform.adapter import Adapter


class HMC(Content):
    """
    Entry point to provide access to HERE Map Content catalogs.
    """

    def __init__(
        self,
        platform: Platform,
        catalog_hrn: str = "hrn:here:data::olp-here:rib-2",
        version: Optional[int] = None,
        adapter: Optional[Adapter] = None,
    ):
        """
        Initialize HMC, selecting which version to use.

        :param platform: access to the HERE platform
        :param catalog_hrn: which instance of HMC to read
        :param version: which version of HMC to read, latest if not specified
        :param adapter: the ``Adapter`` to transform data between different representations,
          ``None`` to use the default adapter of the platform.
        """
        super().__init__(platform, catalog_hrn, version, adapter)

        # Feature layers we expose
        self.admin_places = AdministrativePlaces(self, "administrative-places")
        self.street_names = StreetNames(self, "street-names")
        self.topology_geometry = TopologyGeometry(self, "topology-geometry")

        # Attributes layers we expose
        self.adas_attributes = AdasAttributes(self, "adas-attributes")
        self.address_attributes = AddressAttributes(self, "address-attributes")
        self.admin_locations = AdministrativeLocationsAttributes(self, "administrative-locations")
        self.advanced_navigation_attributes = AdvancedNavigationAttributes(
            self, "advanced-navigation-attributes"
        )
        self.navigation_attributes = NavigationAttributes(self, "navigation-attributes")
        self.road_attributes = RoadAttributes(self, "road-attributes")
        self.traffic_patterns = TrafficPatterns(self, "traffic-patterns")


# UNUSED


class FeatureLayer(BaseLayer):
    """
    A HERE Map Content layer containing map features.

    Features are objects with an identifier, located in a specific partition.
    """

    # TODO: add and model the adapter, that should be specified once in the HMC or Content class

    # TODO: the following will be extended to support reading multiple feature types
    #  at the same time, when a list/tuple of feature types is provided.
    #  In this case, a list/tuple of dictionaries is returned. (not 100% sure yet)

    def read_features(self, partition: str, feature_type: Union[Enum, str]) -> dict:
        """
        Read features from one partition of the content.

        Fetch, decode and return the features of a selected type stored
        in a partition. The features are returned indexed by their identifier.

        The types of the features supported by a specific subclass
        are specified in an enumerator in the same subclass.

        :param partition: id of the partition to read
        :param feature_type: the type of feature to read
        :return: a dictionary containing features, indexed by their id.
          The id and the type of the features are determined by the feature type.
          When an adapter is used, the return type is adapter-specific.  # noqa
        :raises ValueError: in case the feature type is not supported.  # noqa
        """
        ...


# UNUSED


class AttributeLayer(BaseLayer):
    """
    A HERE Map Content layer containing map attributes.

    Attributes are properties that refer to a feature.
    The reference identify the feature via its identifier and partition where it is located.
    A reference may have its own properties as well.
    """

    # TODO: add and model the adapter, that should be specified once in the HMC or Content class

    # TODO: the following will be extended to support reading multiple attributes
    #  at the same time, when a list/tuple of attribute names is provided.
    #  In this case, a list/tuple of list is returned. (not 100% sure yet)

    def read_attributes(self, partition: str, attribute_name: Union[Enum, str]) -> list:
        """
        Read attributes from one partition of the content.

        Fetch, decode and return selected attributes stored in a partition.
        The attributes are returned referencing the feature they apply to.
        The reference may have additional properties, like direction or side.
        Multiple attributes may refer to the same feature, and/or attributes
        may refer to multiple features, depending on the type of the attribute.

        The attributes supported by a specific subclass
        are specified in an enumerator in the same subclass.

        :param partition: id of the partition to read
        :param attribute_name: the name of the attribute to read
        :return: a list containing the attributes with references to features.
          Entries in the list are tuples of 4 elements: partition ids of the referenced features,
          identifiers of referenced feature, properties of the references and attributes.
          The details of the feature references, the reference properties
          and the attribute are determined by the attribute type.
          When an adapter is used, the return type is adapter-specific.  # noqa
        :raises ValueError: in case the attribute is not supported.  # noqa
        """
        ...
