"""The Gateway Package support.

This module contains the classes that:
    - Defines the gateway package sent to cloud by the Gateway device.
    - Support the PackageQueue used to receive Driver collected packages.
"""

# ============================ imports =========================================
from dataclasses import dataclass
from txp.common.edge import EdgeDescriptor
from txp.common.edge import SignalsTableByName, Signal
from txp.common.configuration import SamplingWindow
from typing import Union
from typing import List
import txp.common.protos.gateway_package_pb2 as package_proto


# ==============================================================================
# Definition of the GatewayPackage class
# ==============================================================================


@dataclass
class GatewayPackageMetadata:
    """

    The Package ID follows the following format:
        "{epoch timestamp}_{part_index}"

    The part_index is a consequence of packages split, in order to keep size under
    250Kb.

    Args:
        edge_descriptor: The Edge descriptor of the edge device associated with this Package.
        sampling_windows: The sampling configuration windows used by the driver when it was
            collecting the package.
    """

    edge_descriptor: EdgeDescriptor
    sampling_window: SamplingWindow
    package_id: str
    """package_id: an unique string ID for this package, with the format:
    {epoch timestamp}_{part_index}
    """
    previous_part_index: int
    """
    The integer index of the previous package in the sequence generated by the package split.
    """
    tenant_id: str
    """
    The tenant_id string value of the tenant to which the Gateway belong.
    """


@dataclass
class GatewayPackage:
    """This GatewayPackage encapsulates the information exchanged between
    the Gateway and the Cloud.

    The package contains a series of samples collected during a Sampling Time (Ts)
    period.

    Args:
        configuration_id: a string indicating the version of the config.
        signals: A list of sampled signals from an edge device.
        metadata: The package metadata.
    """
    configuration_id: str
    signals: List[Signal]
    metadata: GatewayPackageMetadata

    def get_proto(self) -> package_proto.GatewayPackageProto:
        metadata_proto = package_proto.GatewayPackageMetadataProto()
        metadata_proto.edge_descriptor.CopyFrom(self.metadata.edge_descriptor.get_proto())
        metadata_proto.sampling_window.CopyFrom(self.metadata.sampling_window.get_proto())
        metadata_proto.package_id = self.metadata.package_id
        metadata_proto.previous_part_index = self.metadata.previous_part_index
        metadata_proto.tenant_id = self.metadata.tenant_id

        signals_proto = list(map(
            lambda signal: signal.get_proto(),
            self.signals
        ))
        proto = package_proto.GatewayPackageProto(configuration_id=self.configuration_id)
        proto.metadata.CopyFrom(metadata_proto)
        proto.signals.extend(signals_proto)
        return proto

    @classmethod
    def build_from_proto(cls, proto: package_proto.GatewayPackageProto) -> "GatewayPackage":
        edge_descriptor = EdgeDescriptor.build_from_proto(proto.metadata.edge_descriptor)
        sampling_window = SamplingWindow.build_from_proto(proto.metadata.sampling_window)
        package_id = proto.metadata.package_id
        previous_part_index = proto.metadata.previous_part_index
        tenant_id = proto.metadata.tenant_id
        metadata = GatewayPackageMetadata(edge_descriptor, sampling_window,
                                          package_id, previous_part_index, tenant_id)
        signals: List = []
        for signal_proto in proto.signals:
            signal_name = str(signal_proto.perception_name)
            signal = SignalsTableByName(signal_name).signal.build_from_proto(signal_proto)
            signals.append(signal)

        configuration_id = proto.configuration_id

        package = GatewayPackage(
            configuration_id, signals, metadata
        )
        return package
