from collections.abc import Collection, Sequence
from concurrent.futures import Future
from pathlib import Path
from typing import Any, Protocol, Union, runtime_checkable

from quel_ic_config.ad9082 import LinkStatus, NcoFtw
from quel_ic_config.exstickge_coap_tempctrl_client import Quel1seTempctrlState
from quel_ic_config.quel1_thermistor import Quel1Thermistor
from quel_ic_config.quel_config_common import Quel1BoxType

Quel1AnyLineType = Union[int, str]


@runtime_checkable
class Quel1AnyConfigSubsystem(Protocol):
    def initialize(self) -> None: ...

    # configuration info
    def get_default_config_filename(self) -> Path: ...

    def get_num_ics(self) -> dict[str, int]: ...

    # introspecting box-info
    @property
    def boxtype(self) -> Quel1BoxType: ...

    @property
    def ipaddr_css(self) -> str: ...

    @property
    def has_lock(self) -> bool: ...

    # introspecting group-line-channel
    def get_all_groups(self) -> set[int]: ...

    def get_all_any_lines(self) -> set[tuple[int, Quel1AnyLineType]]: ...

    def get_all_lines_of_group(self, group: int) -> set[int]: ...

    def get_all_rlines_of_group(self, group: int) -> set[str]: ...

    def get_num_channels_of_line(self, group: int, line: int) -> int: ...

    def get_num_rchannels_of_rline(self, group: int, rline: str) -> int: ...

    # introspecting hardware/firmware info
    def get_num_ic(self, ic_name: str) -> int: ...

    def get_all_mxfes(self) -> set[int]: ...

    def get_rline_from_adc_idx(self, mxfe_idx: int, adc_idx: int) -> Union[tuple[int, str], None]: ...

    # low-level APIs for controlling ICs
    def validate_chip_id(self, mxfe_idx: int) -> bool: ...

    @property
    def allow_dual_modulus_nco(self) -> bool: ...

    @allow_dual_modulus_nco.setter
    def allow_dual_modulus_nco(self, v: bool) -> None: ...

    def get_link_status(self, mxfe_idx: int) -> tuple[LinkStatus, int]: ...

    def get_crc_error_counts(self, mxfe_idx: int) -> list[int]: ...

    def get_main_interpolation_rate(self, mxfe_idx: int) -> int: ...

    def get_channel_interpolation_rate(self, mxfe_idx: int) -> int: ...

    def get_dac_idx(self, group: int, line: int) -> tuple[int, int]: ...

    def get_fduc_idx(self, group: int, line: int, channel: int) -> tuple[int, int]: ...

    def get_adc_idx(self, group: int, rline: str) -> tuple[int, int]: ...

    def get_fddc_idx(self, group: int, rline: str, rchannel: int) -> tuple[int, int]: ...

    def get_virtual_adc_select(self, mxfe_idx: int) -> list[int]: ...

    def set_fullscale_current(self, group: int, line: int, fsc: int) -> None: ...

    def get_fullscale_current(self, group: int, line: int) -> int: ...

    def is_equal_fullscale_current(self, group: int, line: int, fsc0: int, fsc1: int) -> bool: ...

    def set_dac_cnco(
        self, group: int, line: int, freq_in_hz: Union[float, None] = None, ftw: Union[NcoFtw, None] = None
    ) -> None: ...

    def get_dac_cnco(self, group: int, line: int) -> float: ...

    def is_equivalent_dac_cnco(self, group: int, line: int, freq0: float, freq1: float) -> bool: ...

    def set_dac_fnco(
        self,
        group: int,
        line: int,
        channel: int,
        freq_in_hz: Union[float, None] = None,
        ftw: Union[NcoFtw, None] = None,
    ) -> None: ...

    def get_dac_fnco(self, group: int, line: int, channel: int) -> float: ...

    def is_equivalent_dac_fnco(self, group: int, line: int, freq0: float, freq1: float) -> bool: ...

    def set_adc_cnco(
        self, group: int, rline: str, freq_in_hz: Union[float, None] = None, ftw: Union[NcoFtw, None] = None
    ): ...

    def get_adc_cnco(self, group: int, rline: str) -> float: ...

    def is_equivalent_adc_cnco(self, group: int, rline: str, freq0: float, freq1: float) -> bool: ...

    def set_adc_fnco(
        self,
        group: int,
        rline: str,
        rchannel: int,
        freq_in_hz: Union[float, None] = None,
        ftw: Union[NcoFtw, None] = None,
    ): ...

    def get_adc_fnco(self, group: int, rline: str, rchannel: int) -> float: ...

    def is_equivalent_adc_fnco(self, group: int, rline: str, freq0: float, freq1: float) -> bool: ...

    def set_pair_cnco(
        self, group_dac: int, line_dac: int, group_adc: int, rline_adc: str, freq_in_hz: float
    ) -> None: ...

    def get_lo_multiplier(self, group: int, line: Quel1AnyLineType) -> int: ...

    def set_lo_multiplier(self, group: int, line: Quel1AnyLineType, freq_multiplier: int) -> bool: ...

    def get_divider_ratio(self, group: int, line: Quel1AnyLineType) -> int: ...

    def set_divider_ratio(self, group: int, line: Quel1AnyLineType, divide_ratio: int) -> None: ...

    # def get_vatt(self, group: int, line: int) -> int: ...

    def set_vatt(self, group: int, line: int, vatt: int) -> None: ...

    def get_sideband(self, group: int, line: int) -> str: ...

    def set_sideband(self, group: int, line: int, sideband: str) -> None: ...

    def pass_line(self, group: int, line: Quel1AnyLineType) -> None: ...

    def is_passed_line(self, group: int, line: Quel1AnyLineType) -> bool: ...

    def block_line(self, group: int, line: Quel1AnyLineType) -> None: ...

    def is_blocked_line(self, group: int, line: Quel1AnyLineType) -> bool: ...

    def activate_monitor_loop(self, group: int) -> None: ...

    def deactivate_monitor_loop(self, group: int) -> None: ...

    def is_loopedback_monitor(self, group: int) -> bool: ...

    def activate_read_loop(self, group: int) -> None: ...

    def deactivate_read_loop(self, group: int) -> None: ...

    def is_loopedback_read(self, group: int) -> bool: ...

    def is_subordinate_rfswitch(self, group: int, line: Union[int, str]) -> bool: ...

    # high-level APIs
    def configure_all_mxfe_clocks(
        self, param: dict[str, Any], *, ignore_lock_failure_of_lmx2594: Union[Collection[int], None] = None
    ) -> None: ...

    def configure_mxfe(
        self,
        mxfe_idx: int,
        param: dict[str, Any],
        *,
        hard_reset: bool = False,
        soft_reset: bool = False,
        use_204b: bool = True,
        use_bg_cal: bool = False,
        ignore_crc_error: bool = False,
    ) -> bool: ...

    def configure_peripherals(
        self,
        param: dict[str, Any],
        *,
        ignore_access_failure_of_adrf6780: Union[Collection[int], None] = None,
        ignore_lock_failure_of_lmx2594: Union[Collection[int], None] = None,
    ) -> None: ...

    def reconnect_mxfe(
        self,
        mxfe_idx: int,
        *,
        ignore_crc_error: bool = False,
    ) -> bool: ...

    def check_link_status(
        self, mxfe_idx: int, mxfe_init: bool = False, ignore_crc_error: bool = False
    ) -> tuple[bool, int, str]: ...

    def clear_crc_error(self, mxfe_idx: int) -> None: ...

    def dump_channel(self, group: int, line: int, channel: int) -> dict[str, Any]: ...

    def dump_line(self, group: int, line: int) -> dict[str, Any]: ...

    def dump_rchannel(self, group: int, rline: str, rchannel: int) -> dict[str, Any]: ...

    def dump_rline(self, group: int, rline: str) -> dict[str, Any]: ...

    # high-level APIs for temperature control
    @property
    def tempctrl_auto_start_at_linkup(self) -> bool: ...

    def get_tempctrl_state(self) -> Quel1seTempctrlState: ...

    def get_tempctrl_state_count(self) -> int: ...

    def start_tempctrl(self, new_count: Union[int, None] = None) -> None: ...

    def get_mxfe_temperature_range(self, mxfe_idx: int) -> tuple[int, int]: ...


@runtime_checkable
class Quel1seAnyConfigSubsystem(Quel1AnyConfigSubsystem, Protocol):
    @classmethod
    def get_thermistor_desc(cls) -> dict[tuple[int, int], Quel1Thermistor]: ...

    @classmethod
    def get_actuator_desc(cls) -> dict[str, tuple[str, int]]: ...

    def get_tempctrl_temperature(self) -> Future[dict[str, float]]: ...

    def get_tempctrl_actuator_output(self) -> dict[str, dict[str, float]]: ...

    def get_tempctrl_setpoint(self) -> dict[str, list[float]]: ...

    def set_tempctrl_setpoint(self, fan: Sequence[float], heater: Sequence[float]): ...
