#  Copyright (c) ETH Zurich, SIS ID and HVL D-ITET
#
"""
Masked version of LJM Communication for testing purposes.
"""

import logging
from collections import defaultdict
from collections.abc import Sequence
from numbers import Real
from queue import Queue
from typing import Union

from hvl_ccb.comm.labjack_ljm import LJMCommunication

logger = logging.getLogger(__name__)


class MaskedLJMCommunication(LJMCommunication):
    """
    Masks the LJMCommunication protocol disabling read and write to be able to test
    the functionality of devices without
    """

    def __init__(self, configuration) -> None:
        super().__init__(configuration)

        self._read_buffer = defaultdict(Queue)
        self._write_buffer = Queue()

    def read_name(self, *names: str, return_num_type=float):
        try:
            out = tuple(
                (
                    self._cast_read_value(
                        name,
                        self._read_buffer[name].get(),
                        return_num_type=return_num_type,
                    )
                    if not self._read_buffer[name].empty()
                    else return_num_type(0)
                )
                for name in names
            )
        except ValueError as e:
            raise TypeError from e

        return out[0] if len(out) == 1 else out

    def write_name(
        self, name: Union[Sequence[str], str], value: Union[Sequence[object], object]
    ) -> None:
        if isinstance(name, str):
            name = [name]
            value = [value]

        for pair in zip(name, value):
            self._write_buffer.put(pair)

    def write_address(
        self, address: Union[Sequence[int], int], value: Union[Sequence[object], object]
    ) -> None:
        pass

    def put_name(self, name: str, value: object) -> None:
        # Note: labjack library returns `float` (`ctypes.c_double`?), even when `int`
        #       value is expected, but allow also in tests mock values with invalid type
        if isinstance(value, Real):
            value = float(value)
        self._read_buffer[name].put(value)

    def get_written(self):
        return self._write_buffer.get() if not self._write_buffer.empty() else None
