import enum

from eive_tmtc.config.definitions import CustomServiceList
from eive_tmtc.config.object_ids import COM_SUBSYSTEM_ID
from eive_tmtc.tmtc.com.syrlinks_handler import Datarate
from tmtccmd.pus.s20_fsfw_param_defs import create_scalar_u8_parameter

from .defs import Mode as ComMode

from tmtccmd.config.tmtc import (
    tmtc_definitions_provider,
    TmtcDefinitionWrapper,
    OpCodeEntry,
)
from tmtccmd.tmtc import service_provider
from tmtccmd.tmtc.decorator import ServiceProviderParams
from tmtccmd.pus.s200_fsfw_mode import (
    create_mode_command,
    create_read_mode_command,
    create_announce_mode_command,
    create_announce_mode_recursive_command,
)
from tmtccmd.pus.s20_fsfw_param import (
    create_load_param_cmd,
)

from tmtccmd.pus.s20_fsfw_param import create_scalar_u32_parameter


class ParameterId(enum.IntEnum):
    DATARATE = 0
    TRANSMITTER_TIMEOUT = 1


class OpCode:
    RX_ONLY = "rx_only"
    TX_AND_RX_DEF_RATE = "rx_and_tx_default_rate"
    TX_AND_RX_LOW_RATE = "rx_and_tx_low_rate"
    TX_AND_RX_HIGH_RATE = "rx_and_tx_high_rate"
    TX_AND_RX_CARRIER_WAVE = "rx_and_tx_carrier_wave"
    UPDATE_DEFAULT_DATARATE_LOW = "update_default_rate_low"
    UPDATE_DEFAULT_DATARATE_HIGH = "update_default_rate_high"
    CHANGE_TRANSMITTER_TIMEOUT = "change_transmitter_timeout"
    READ_MODE = "read_mode"
    ANNOUNCE_MODE = "announce_mode"
    ANNOUNCE_MODE_RECURSIVE = "announce_mode_recursive"


class Info:
    RX_ONLY = "Syrlinks RX Only"
    TX_AND_RX_DEF_DATARATE = "Syrlinks with TX default datarate"
    TX_AND_RX_LOW_DATARATE = "Syrlinks with TX low datarate (BPSK modulation)"
    TX_AND_RX_HIGH_DATARATE = "Syrlinks with TX high datarate (0QPSK modulation)"
    TX_AND_RX_CARRIER_WAVE = "Syrlinks with TX carrier wave"
    UPDATE_DEFAULT_DATARATE_LOW = "Configure default low datarate (BPSK modulation)"
    UPDATE_DEFAULT_DATARATE_HIGH = "Configure default high datarate (0QPSK modulation)"
    CHANGE_TRANSMITTER_TIMEOUT = "Changes the transmitter timeout"
    READ_MODE = "Read Mode"
    ANNOUNCE_MODE = "Announce Mode"
    ANNOUNCE_MODE_RECURSIVE = "Announce mode recursively"


@service_provider(CustomServiceList.COM_SS)
def build_com_subsystem_cmd(p: ServiceProviderParams):  # noqa C901
    q = p.queue_helper
    o = p.op_code
    prefix = "COM Subsystem"
    if o == OpCode.RX_ONLY:
        q.add_log_cmd(Info.RX_ONLY)
        q.add_pus_tc(create_mode_command(COM_SUBSYSTEM_ID, ComMode.RX_ONLY, 0))
    elif o == OpCode.TX_AND_RX_DEF_RATE:
        q.add_log_cmd(Info.TX_AND_RX_DEF_DATARATE)
        q.add_pus_tc(
            create_mode_command(COM_SUBSYSTEM_ID, ComMode.RX_AND_TX_DEF_DATARATE, 0)
        )
    elif o == OpCode.TX_AND_RX_LOW_RATE:
        q.add_log_cmd(Info.TX_AND_RX_LOW_DATARATE)
        q.add_pus_tc(
            create_mode_command(COM_SUBSYSTEM_ID, ComMode.RX_AND_TX_LOW_DATARATE, 0)
        )
    elif o == OpCode.TX_AND_RX_HIGH_RATE:
        q.add_log_cmd(Info.TX_AND_RX_HIGH_DATARATE)
        q.add_pus_tc(
            create_mode_command(COM_SUBSYSTEM_ID, ComMode.RX_AND_TX_HIGH_DATARATE, 0)
        )
    if o == OpCode.UPDATE_DEFAULT_DATARATE_LOW:
        q.add_log_cmd(f"{prefix}: {Info.UPDATE_DEFAULT_DATARATE_LOW}")
        q.add_pus_tc(
            create_load_param_cmd(
                create_scalar_u8_parameter(
                    COM_SUBSYSTEM_ID,
                    0,
                    ParameterId.DATARATE,
                    Datarate.LOW_RATE_MODULATION_BPSK,
                )
            )
        )
    if o == OpCode.UPDATE_DEFAULT_DATARATE_HIGH:
        q.add_log_cmd(f"{prefix}: {Info.UPDATE_DEFAULT_DATARATE_HIGH}")
        q.add_pus_tc(
            create_load_param_cmd(
                create_scalar_u8_parameter(
                    COM_SUBSYSTEM_ID,
                    0,
                    ParameterId.DATARATE,
                    Datarate.HIGH_RATE_MODULATION_0QPSK,
                )
            )
        )
    elif o == OpCode.TX_AND_RX_CARRIER_WAVE:
        q.add_log_cmd(Info.TX_AND_RX_CARRIER_WAVE)
        q.add_pus_tc(
            create_mode_command(COM_SUBSYSTEM_ID, ComMode.RX_AND_TX_CARRIER_WAVE, 0)
        )
    elif o == OpCode.CHANGE_TRANSMITTER_TIMEOUT:
        timeout = int(input("Specify timeout to set [ms]: "))
        q.add_log_cmd(Info.CHANGE_TRANSMITTER_TIMEOUT)
        q.add_pus_tc(
            create_load_param_cmd(
                create_scalar_u32_parameter(
                    COM_SUBSYSTEM_ID,
                    0,
                    ParameterId.TRANSMITTER_TIMEOUT,
                    timeout,
                )
            )
        )
    elif o == OpCode.READ_MODE:
        q.add_log_cmd(Info.READ_MODE)
        q.add_pus_tc(create_read_mode_command(COM_SUBSYSTEM_ID))
    elif o == OpCode.ANNOUNCE_MODE:
        q.add_log_cmd(Info.ANNOUNCE_MODE)
        q.add_pus_tc(create_announce_mode_command(COM_SUBSYSTEM_ID))
    elif o == OpCode.ANNOUNCE_MODE_RECURSIVE:
        q.add_log_cmd(Info.ANNOUNCE_MODE_RECURSIVE)
        q.add_pus_tc(create_announce_mode_recursive_command(COM_SUBSYSTEM_ID))


@tmtc_definitions_provider
def add_com_subsystem_cmds(defs: TmtcDefinitionWrapper):
    oce = OpCodeEntry()
    oce.add(OpCode.RX_ONLY, Info.RX_ONLY)
    oce.add(OpCode.TX_AND_RX_LOW_RATE, Info.TX_AND_RX_LOW_DATARATE)
    oce.add(OpCode.TX_AND_RX_HIGH_RATE, Info.TX_AND_RX_HIGH_DATARATE)
    oce.add(OpCode.TX_AND_RX_DEF_RATE, Info.TX_AND_RX_DEF_DATARATE)
    oce.add(OpCode.UPDATE_DEFAULT_DATARATE_LOW, Info.UPDATE_DEFAULT_DATARATE_LOW)
    oce.add(OpCode.UPDATE_DEFAULT_DATARATE_HIGH, Info.UPDATE_DEFAULT_DATARATE_HIGH)
    oce.add(OpCode.CHANGE_TRANSMITTER_TIMEOUT, Info.CHANGE_TRANSMITTER_TIMEOUT)
    oce.add(OpCode.READ_MODE, Info.READ_MODE)
    oce.add(OpCode.ANNOUNCE_MODE, Info.ANNOUNCE_MODE)
    oce.add(OpCode.ANNOUNCE_MODE_RECURSIVE, Info.ANNOUNCE_MODE_RECURSIVE)
    defs.add_service(CustomServiceList.COM_SS, "COM Subsystem", oce)