import numpy
from blissdata.redis_engine.store import DataStore
from ewokscore import Task
from ewoksdata.data.bliss import iter_bliss_scan_data


class StopScan(
    Task,
    input_names=[
        "filename",
        "scan_number",
        "lima_limits",
        "scalar_counter_limits",
        "redis_url",
        "response_stream_name",
    ],
    optional_input_names=[
        "scan_key",
        "retry_timeout",
        "retry_period",
    ],
):
    """Stream events while processing data. Currently only one event is send:
    when the summed data of all channels reached their limit. Other events
    can be send in the future such as a request to move a motor.
    """

    def run(self):
        sumdata = None
        event_stream = _EventStream(
            self.inputs.redis_url, self.inputs.response_stream_name
        )
        limits = {**self.inputs.lima_limits, **self.inputs.scalar_counter_limits}

        for scan_point_data_dict in iter_bliss_scan_data(
            self.inputs.filename,
            self.inputs.scan_number,
            scan_key=self.get_input_value("scan_key", None),
            lima_names=list(self.inputs.lima_limits),
            counter_names=list(self.inputs.scalar_counter_limits),
            retry_timeout=self.get_input_value("retry_timeout", None),
            retry_period=self.get_input_value("retry_period", None),
        ):
            if sumdata is None:
                sumdata = scan_point_data_dict
            else:
                for name in sumdata:
                    sumdata[name] = sumdata[name] + scan_point_data_dict[name]

            limits_reached = {
                k: numpy.nanmax(v) > limits[k] for k, v in sumdata.items()
            }
            if all(limits_reached.values()):
                event = {"type": "stop"}
                event_stream.send_event(event)
                break


class _EventStream:
    MAXLEN = 100

    def __init__(self, redis_url: str, stream_name: str):
        self._data_store = DataStore(redis_url)
        self._stream_name = stream_name

    def send_event(self, event: dict):
        self._data_store._redis.xadd(
            self._stream_name, event, maxlen=self.MAXLEN, approximate=True
        )
