# This file was generated by the "yardl" tool. DO NOT EDIT.

# pyright: reportUnusedImport=false

import abc
import collections.abc
import datetime
import typing

import numpy as np
import numpy.typing as npt

from .types import *
from .yardl_types import ProtocolError
from . import yardl_types as yardl

class PrdExperimentWriterBase(abc.ABC):
    """Abstract writer for the PrdExperiment protocol."""


    def __init__(self) -> None:
        self._state = 0

    schema = r"""{"protocol":{"name":"PrdExperiment","sequence":[{"name":"header","type":"Prd.Header"},{"name":"timeBlocks","type":{"stream":{"items":"Prd.TimeBlock"}}}]},"types":[{"name":"CoincidenceEvent","fields":[{"name":"detector1Id","type":"uint32"},{"name":"detector2Id","type":"uint32"},{"name":"tofIdx","type":"uint32"},{"name":"energy1Idx","type":"uint32"},{"name":"energy2Idx","type":"uint32"}]},{"name":"Detector","fields":[{"name":"id","type":"uint32"},{"name":"x","type":"float32"},{"name":"y","type":"float32"},{"name":"z","type":"float32"}]},{"name":"ExamInformation","fields":[{"name":"subject","type":"Prd.Subject"},{"name":"institution","type":"Prd.Institution"},{"name":"protocol","type":[null,"string"]},{"name":"startOfAcquisition","type":[null,"datetime"]}]},{"name":"Header","fields":[{"name":"scanner","type":"Prd.ScannerInformation"},{"name":"exam","type":[null,"Prd.ExamInformation"]}]},{"name":"Institution","fields":[{"name":"name","type":"string"},{"name":"address","type":"string"}]},{"name":"ScannerInformation","fields":[{"name":"modelName","type":[null,"string"]},{"name":"detectors","type":{"vector":{"items":"Prd.Detector"}}},{"name":"tofBinEdges","type":{"array":{"items":"float32","dimensions":1}}},{"name":"tofResolution","type":"float32"},{"name":"energyBinEdges","type":{"array":{"items":"float32","dimensions":1}}},{"name":"energyResolutionAt511","type":"float32"},{"name":"listmodeTimeBlockDuration","type":"uint32"}]},{"name":"Subject","fields":[{"name":"name","type":[null,"string"]},{"name":"id","type":"string"}]},{"name":"TimeBlock","fields":[{"name":"id","type":"uint32"},{"name":"promptEvents","type":{"vector":{"items":"Prd.CoincidenceEvent"}}},{"name":"delayedEvents","type":[null,{"vector":{"items":"Prd.CoincidenceEvent"}}]}]}]}"""

    def __enter__(self):
        return self

    def __exit__(self, exc_type: typing.Optional[type[BaseException]], exc: typing.Optional[BaseException], traceback: object) -> None:
        if exc is None and self._state == 3:
            try:
                self._end_stream()
                return
            finally:
                self.close()
        self.close()
        if exc is None and self._state != 4:
            expected_method = self._state_to_method_name((self._state + 1) & ~1)
            raise ProtocolError(f"Protocol writer closed before all steps were called. Expected to call to '{expected_method}'.")

    def write_header(self, value: Header) -> None:
        """Ordinal 0"""

        if self._state != 0:
            self._raise_unexpected_state(0)

        self._write_header(value)
        self._state = 2

    def write_time_blocks(self, value: collections.abc.Iterable[TimeBlock]) -> None:
        """Ordinal 1"""

        if self._state & ~1 != 2:
            self._raise_unexpected_state(2)

        self._write_time_blocks(value)
        self._state = 3

    @abc.abstractmethod
    def _write_header(self, value: Header) -> None:
        raise NotImplementedError()

    @abc.abstractmethod
    def _write_time_blocks(self, value: collections.abc.Iterable[TimeBlock]) -> None:
        raise NotImplementedError()

    @abc.abstractmethod
    def close(self) -> None:
        pass

    @abc.abstractmethod
    def _end_stream(self) -> None:
        pass

    def _raise_unexpected_state(self, actual: int) -> None:
        expected_method = self._state_to_method_name(self._state)
        actual_method = self._state_to_method_name(actual)
        raise ProtocolError(f"Expected to call to '{expected_method}' but received call to '{actual_method}'.")

    def _state_to_method_name(self, state: int) -> str:
        if state == 0:
            return 'write_header'
        if state == 2:
            return 'write_time_blocks'
        return "<unknown>"

class PrdExperimentReaderBase(abc.ABC):
    """Abstract reader for the PrdExperiment protocol."""


    def __init__(self) -> None:
        self._state = 0

    schema = PrdExperimentWriterBase.schema

    def __enter__(self):
        return self

    def __exit__(self, exc_type: typing.Optional[type[BaseException]], exc: typing.Optional[BaseException], traceback: object) -> None:
        self.close()
        if exc is None and self._state != 4:
            if self._state % 2 == 1:
                previous_method = self._state_to_method_name(self._state - 1)
                raise ProtocolError(f"Protocol reader closed before all data was consumed. The iterable returned by '{previous_method}' was not fully consumed.")
            else:
                expected_method = self._state_to_method_name(self._state)
                raise ProtocolError(f"Protocol reader closed before all data was consumed. Expected call to '{expected_method}'.")
            	

    @abc.abstractmethod
    def close(self) -> None:
        raise NotImplementedError()

    def read_header(self) -> Header:
        """Ordinal 0"""

        if self._state != 0:
            self._raise_unexpected_state(0)

        value = self._read_header()
        self._state = 2
        return value

    def read_time_blocks(self) -> collections.abc.Iterable[TimeBlock]:
        """Ordinal 1"""

        if self._state != 2:
            self._raise_unexpected_state(2)

        value = self._read_time_blocks()
        self._state = 3
        return self._wrap_iterable(value, 4)

    def copy_to(self, writer: PrdExperimentWriterBase) -> None:
        writer.write_header(self.read_header())
        writer.write_time_blocks(self.read_time_blocks())

    @abc.abstractmethod
    def _read_header(self) -> Header:
        raise NotImplementedError()

    @abc.abstractmethod
    def _read_time_blocks(self) -> collections.abc.Iterable[TimeBlock]:
        raise NotImplementedError()

    T = typing.TypeVar('T')
    def _wrap_iterable(self, iterable: collections.abc.Iterable[T], final_state: int) -> collections.abc.Iterable[T]:
        yield from iterable
        self._state = final_state

    def _raise_unexpected_state(self, actual: int) -> None:
        actual_method = self._state_to_method_name(actual)
        if self._state % 2 == 1:
            previous_method = self._state_to_method_name(self._state - 1)
            raise ProtocolError(f"Received call to '{actual_method}' but the iterable returned by '{previous_method}' was not fully consumed.")
        else:
            expected_method = self._state_to_method_name(self._state)
            raise ProtocolError(f"Expected to call to '{expected_method}' but received call to '{actual_method}'.")
        	
    def _state_to_method_name(self, state: int) -> str:
        if state == 0:
            return 'read_header'
        if state == 2:
            return 'read_time_blocks'
        return "<unknown>"

