import abc
import dataclasses
import itertools
import math
from enum import Enum
from numbers import Number
from typing import ClassVar, Iterator, Optional

from pojagi_dsp.channel import ASignal


Segment = Enum("Segment", [
    "R",
    "S",
    "S_T",
    "T",
    "U",
    "T_P",
    "P",
    "P_R",
    "Q",
])


@dataclasses.dataclass(frozen=True)
class Segments:
    """
    The ECG wave is a kind of impulse with 8 to 9 segments.

    Segment pointers based on https://geekymedics.com/how-to-read-an-ecg/
    """
    # ClassVar means it's not part of the constructor (i.e., `__init__` args).
    R: ClassVar[int] = 0
    """R is constant 0"""
    S: int
    S_T: int  # i.e., J point
    T: int
    T_P: int
    P: int
    P_R: int  # actually, only goes from P-Q, confusingly
    Q: int

    U: Optional[int] = None
    """Rare, but if found, comes before T_P."""

    def merge(self, other: "Segments", weight: float):
        """
        Merge two `Segments`, with a given weight.
        :param:`other` - the other `Segments` to merge
        :param:`weight` - a value between 0.0 and 1.0 to indicate how much
        weight to give to `other`.
        """
        self_weight = 1 - weight
        return Segments(
            **{
                k: math.floor(lv * self_weight + getattr(other, k) * weight)
                if lv is not None
                else getattr(other, k)

                for k, lv in (
                    (f.name, getattr(self, f.name))
                    for f in dataclasses.fields(self)
                )
            },
        )


class AECGChannel(ASignal[Number]):

    @property
    @abc.abstractproperty
    def heart_rate(self) -> float:
        """Frequency of impulses/waves in bpm."""
        ...

    @property
    @abc.abstractproperty
    def wavelength(self) -> int:
        """
        The number of samples in a complete impulse/wave cycle.

        Formula: `srate / (heart_rate / 60)` where `heart_rate / 60` is the
        heart rate converted from bpm to Hz.
        """
        ...

    @property
    @abc.abstractproperty
    def segments(self) -> Segments:
        """The analytical segments of the impulse/wave."""
        ...

    @property
    def wave(self) -> Iterator[Number]:
        """
        Returns an iterator over a single ECG impulse/wave.
        """
        return itertools.islice(self, 0, self.wavelength)
