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

"""
Bindings for the HMC Topology Geometry.
"""
from dataclasses import dataclass
from typing import List, Mapping

from geojson import LineString, Point
from google.protobuf.message import Message
from here.content.base import ContentIndexer, ContentPartition, SingleLayerBinding
from here.content.content import hmc_schema_version
from here.content.utils.proto import parse_linestring, parse_point, parse_ref
from here.platform.adapter import Identifier, Partition, Ref


@dataclass
class Node:
    """Topology node."""

    partition_id: Partition
    node_id: Identifier
    segment_ref: List[Ref]
    geometry: Point
    z_level: int

    indexer = ContentIndexer(partition="partition_id", identifier="node_id")


@dataclass
class Segment:
    """Topology segment."""

    partition_id: Partition
    segment_id: Identifier
    start_node_ref: Ref
    end_node_ref: Ref
    geometry: LineString
    z_level: List[int]
    length: float

    indexer = ContentIndexer(partition="partition_id", identifier="segment_id")


class TopologyGeometry(SingleLayerBinding):
    """
    Bindings for the HMC Topology Geometry.
    """

    @classmethod
    def schema_hrn(cls) -> List[str]:
        """
        :return: the HRN of schema this binding can parse and index
        """
        return [f"hrn:here:schema:::com.here.schema.rib:topology-geometry_v2:{hmc_schema_version}"]

    @classmethod
    def object_types(cls) -> Mapping[str, type]:
        """
        :return: the types of features supported by this binding
        """
        return {"node": Node, "segment": Segment}

    @classmethod
    def parse_and_index(cls, partition_id: str, msg: Message) -> ContentPartition:
        """
        Parse and index decoded message.

        :param partition_id: identifier of the partition
        :param msg: decoded content from the layer, in the form of a Protobuf message
        :return: the content of a partition parsed and indexed in its own data structure
        """
        assert hasattr(msg, "node")
        assert hasattr(msg, "segment")

        node = [
            Node(
                partition_id=Partition(partition_id),
                node_id=Identifier(n.identifier),
                segment_ref=[parse_ref(r) for r in n.segment_ref],
                geometry=parse_point(n.geometry),
                z_level=n.geometry.z_level,
            )
            for n in msg.node  # type: ignore
        ]
        segment = [
            Segment(
                partition_id=Partition(partition_id),
                segment_id=Identifier(s.identifier),
                start_node_ref=parse_ref(s.start_node_ref),
                end_node_ref=parse_ref(s.end_node_ref),
                geometry=parse_linestring(s.geometry),
                z_level=[p.z_level for p in s.geometry.point],
                length=s.length,
            )
            for s in msg.segment  # type: ignore
        ]

        return ContentPartition(cls, node=node, segment=segment)
