# Copyright (C) 2019-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.

"""HERE Platform Python SDK, Content access module
"""

from typing import Optional

from here.platform import Platform
from here.platform.adapter import Adapter, ContentAdapter
from here.platform.adapter_default import DefaultContentAdapter

hmc_schema_version = "2.180.1"


class Content:
    """
    Represent the source used by content bindings to access the data.
    """

    def __init__(
        self,
        platform: Platform,
        catalog_hrn: str,
        version: Optional[int] = None,
    ):
        """
        Initialize the content source.

        Platform is provided for the bindings to access the platform.
        Catalog HRN and version, when applicable, to select the content source.

        :param platform: access to the HERE platform
        :param catalog_hrn: which instance of the content to read
        :param version: which version of the content to read, the latest if not specified
        :raises Exception: If catalog is empty.
        """

        # The catalog HRN, as passed
        self.catalog_hrn = catalog_hrn

        # This is the catalog used to access the data, adapter is never applied to it
        # This catalog is for internal use only of the content and content binding
        self._catalog = platform.get_catalog(catalog_hrn, adapter=platform.default_adapter)

        # Platform instance, for internal use of content and content binding
        self._platform = platform

        # The version must be set or detected once and in one place, otherwise
        # we risk operating on two different versions if the catalog is modified
        # between multiple `latest_version` calls.
        self.version: int = (
            version or self._catalog.latest_version() or self._catalog.configuration.version
        )
        if not self.version:
            raise Exception(f"Catalog {catalog_hrn} is empty.")
        # todo: check that a version exists if specified from outside
        # todo: generalize also for catalogs without versioned layers to support volatile layers


class ContentBinding:
    """
    Base class for all the bindings.
    """

    def __init__(self, content: Content, adapter: Optional[Adapter] = None):
        """
        Initialize the content binding.

        :param content: the content to bind to
        :param adapter: adapter to expose the content in a specific format. If not specified,
            the adapter defined in the ``Platform`` instance used for ``Content``
            is used by default in all the functions of the content binding.
        """
        self.content: Content = content

        # The following two are for implementations of this interface
        self._catalog = content._catalog
        self._version: int = content.version
        self._content_adapter = (adapter or content._platform.adapter).content_adapter

        # The following two are needed by legacy HMC bindings only, to be removed
        self._adapter = adapter
        self._default_adapter = content._platform.default_adapter

    # The following function should also be removed when legacy HMC bindings are removed
    def _legacy_hmc_content_adapter(self, adapter: Optional[Adapter]) -> Optional[ContentAdapter]:
        """
        Select an adapter and return its content adapter.

        Each binding function can override the selection with a preferred adapter.
        If not specified, the default adapter of the binding is selected.
        If this is also not specified, the adapter is taken from the platform instance.

        It never returns :class:`DefaultContentAdapter`. ``None`` is returned in this case.
        This is temporary and needed for backward compatibility with existing bindings.

        :param adapter: the preferred adapter to select, if available.
        :return: the content adapter associated with the selected adapter
        """
        candidate = (adapter or self._adapter or self.content._platform.adapter).content_adapter
        return candidate if not isinstance(candidate, DefaultContentAdapter) else None
