"""Converter for DSP domain models to/from proto."""

from __future__ import annotations

from typing import cast

from radiens_core._generated import common_pb2
from radiens_core.models.dsp import DSPGroup, DSPParams, DSPType, FilterStage, FreqSpecBand
from radiens_core.models.ports import Port


def proto_to_domain_dsp_params(proto: common_pb2.DSPParams) -> DSPParams:
    """Convert DSPParams from proto to domain model."""

    freq_spec_band = None
    if proto.HasField("freqSpecBand"):
        freq_spec_band = FreqSpecBand(
            low_freq=proto.freqSpecBand.lowFreq,
            high_freq=proto.freqSpecBand.highFreq,
        )

    return DSPParams(
        type=DSPType(proto.type),
        stage=FilterStage(proto.stage),
        freq=proto.freq if proto.HasField("freq") else None,
        freq_spec_band=freq_spec_band,
        ref_ntv_chan_idx=proto.refNtvChanIdx if proto.HasField("refNtvChanIdx") else None,
        notch_bandwidth=proto.notchBandwidth if proto.HasField("notchBandwidth") else None,
        user_label=proto.userLabel,
        port=Port(proto.port) if proto.HasField("port") else None,
        target_ntv_chan_idx=proto.targetNtvChanIdx if proto.HasField("targetNtvChanIdx") else None,
        is_aux=proto.isAux,
        aux_chan_idx=proto.auxChanIdx if proto.HasField("auxChanIdx") else None,
        filter_order=proto.filterOrder if proto.HasField("filterOrder") else None,
        zero_phase=proto.zeroPhase,
        force_sos=proto.forceSOS,
    )


def domain_to_proto_dsp_params(params: DSPParams) -> common_pb2.DSPParams:
    """Convert DSPParams from domain model to proto."""

    proto = common_pb2.DSPParams(
        type=cast("common_pb2.DSPType.ValueType", params.type.value),
        stage=cast("common_pb2.FilterStage.ValueType", params.stage.value),
        userLabel=params.user_label,
        isAux=params.is_aux,
        zeroPhase=params.zero_phase,
        forceSOS=params.force_sos,
    )

    if params.freq is not None:
        proto.freq = params.freq

    if params.freq_spec_band is not None:
        proto.freqSpecBand.lowFreq = params.freq_spec_band.low_freq
        proto.freqSpecBand.highFreq = params.freq_spec_band.high_freq

    if params.ref_ntv_chan_idx is not None:
        proto.refNtvChanIdx = params.ref_ntv_chan_idx

    if params.notch_bandwidth is not None:
        proto.notchBandwidth = params.notch_bandwidth

    if params.port is not None:
        proto.port = cast("common_pb2.Port.ValueType", params.port.value)

    if params.target_ntv_chan_idx is not None:
        proto.targetNtvChanIdx = params.target_ntv_chan_idx

    if params.aux_chan_idx is not None:
        proto.auxChanIdx = params.aux_chan_idx

    if params.filter_order is not None:
        proto.filterOrder = params.filter_order

    return proto


def proto_to_domain_dsp_group(proto: common_pb2.DSPGroup) -> DSPGroup:
    """Convert DSPGroup from proto to domain model."""

    return DSPGroup(
        hardware=[proto_to_domain_dsp_params(p) for p in proto.hardware],
        software_stage1=[proto_to_domain_dsp_params(p) for p in proto.softwareStage1],
        software_stage2=[proto_to_domain_dsp_params(p) for p in proto.softwareStage2],
        spike_sorter=[proto_to_domain_dsp_params(p) for p in proto.spikeSorter],
    )
