from abc import ABC, abstractmethod
from typing import Iterable

from .utils import Quantity, Unit, Second


class BaseReader(ABC):
    """Base class for all readers.

    :param quantities: List of quantities to read.

    .. attribute quantities: List of quantities to read.
    """

    def __init__(self, quantities: Iterable[type[Quantity]]) -> None:
        """Initialize the reader with the specified quantities."""
        self.quantities = quantities

    @property
    def derived_quantities(self) -> list[type[Quantity]]:
        """Return a list of quantities derived from the reading streams."""
        return []

    @property
    @abstractmethod
    def tags(self) -> list[str]:
        """Return a list of tags for each reading stream."""
        pass

    @property
    def derived_tags(self) -> list[str]:
        """Return a list of tags with quentities derived from the reading streams.

        For example, if one of the quantities is energy, the derived quantity
        could be power.
        """
        return []

    @abstractmethod
    def read(self) -> Iterable:
        """Read the quantities of interest."""
        pass

    def compute_derived(
        self, time_series, data_series, time_unit: Second = Second()
    ) -> list:
        """Compute the derived quantities from the time series and data series.

        The data series is expected to be a sequence generated by successive
        calls to the :meth:`read` method.

        :param time_series: One-dimensional array of time readings.
        :param data_series: One- or Two-dimensional array of data readings.
            The first dimension is expected to be the time dimension.
        :param time_unit: Unit of the time readings. Defaults to seconds.
        """
        return []

    @abstractmethod
    def get_unit(self, quantity: type[Quantity]) -> Unit:
        """Get the unit for a given quantity."""
        pass
