# -*- coding: utf-8 -*-
"""
@file       gomspace_common.py
@brief      PDU2 tests
@details    All functions and classes common for all gomspace devices are defined in this file.
@author     J. Meier
@date       17.12.2020
"""
import enum
import struct
from typing import Union

from spacepackets.ecss import PusTelecommand
from tmtccmd.tc import DefaultPusQueueHelper
from tmtccmd.tc.pus_8_funccmd import make_fsfw_action_cmd
from tmtccmd.util import ObjectIdU32, ObjectIdBase


class GomspaceDeviceActionIds(enum.IntEnum):
    PING = 1
    REBOOT = 4
    PARAM_GET = 0
    PARAM_SET = 255
    WDT_RESET = 9
    REQUEST_HK_TABLE = 16
    REQUEST_CONFIG_TABLE = 17
    SAVE_TABLE = 18
    SAVE_TABLE_DEFAULT = 19
    LOAD_TABLE = 20
    PRINT_SWITCH_V_I = 32
    PRINT_LATCHUPS = 33


class ParamTypes(enum.Enum):
    U8 = 0
    U16 = 1
    U32 = 2
    I8 = 3
    I16 = 4
    I32 = 5
    FLOAT = 6
    STR = 7


class TableIds:
    CONFIG = 1
    HK = 4


class TableEntry:
    uint8_size = 1
    uint16_size = 2
    uint32_size = 4

    def __init__(self, parameter_address: bytes, parameter_size):
        self.parameter_address = parameter_address
        self.parameter_size = parameter_size


class Channel:
    on = 1
    off = 0


def pack_request_config_command(object_id: bytes) -> PusTelecommand:
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=GomspaceDeviceActionIds.REQUEST_CONFIG_TABLE
    )


def pack_get_param_command(
    object_id: bytes,
    table_id: int,
    memory_address: Union[int, bytes],
    parameter_size: int,
) -> PusTelecommand:
    """Function to generate a command to retrieve parameters like the temperature from a gomspace device.
    @param object_id:   The object id of the gomspace device handler.
    @param table_id:    The table id of the gomspace device
    @param memory_address:  Address offset within table of the value to read.
    @param parameter_size:  Size of the value to read. E.g. temperature is uint16_t and thus
        parameter_size is 2
    @return:    The command as bytearray.
    """
    app_data = struct.pack("!B", table_id)
    if isinstance(memory_address, int):
        app_data += struct.pack("!H", memory_address)
    else:
        app_data += memory_address
    app_data += struct.pack("!B", parameter_size)
    return make_fsfw_action_cmd(
        object_id=object_id,
        action_id=GomspaceDeviceActionIds.PARAM_GET,
        user_data=app_data,
    )


def pack_set_float_param_command(
    object_id: bytes, memory_address: bytes, parameter: float
) -> PusTelecommand:
    action_id = GomspaceDeviceActionIds.PARAM_SET
    app_data = bytearray()
    app_data += memory_address
    app_data.append(4)
    app_data += struct.pack("!f", parameter)
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=action_id, user_data=app_data
    )


def pack_set_u8_param_command(
    object_id: bytes, memory_address: bytes, parameter: int
) -> PusTelecommand:
    action_id = GomspaceDeviceActionIds.PARAM_SET
    app_data = bytearray()
    app_data += memory_address
    app_data.append(1)
    app_data.append(parameter)
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=action_id, user_data=app_data
    )


def pack_set_i8_param_command(
    object_id: bytes, memory_address: bytes, parameter: int
) -> PusTelecommand:
    action_id = GomspaceDeviceActionIds.PARAM_SET
    app_data = bytearray()
    app_data += memory_address
    app_data.append(1)
    app_data += struct.pack("!b", parameter)
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=action_id, user_data=app_data
    )


def pack_set_u16_param_command(
    object_id: bytes, memory_address: bytes, parameter: int
) -> PusTelecommand:
    action_id = GomspaceDeviceActionIds.PARAM_SET
    app_data = bytearray()
    app_data += memory_address
    app_data.append(2)
    app_data += struct.pack("!H", parameter)
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=action_id, user_data=app_data
    )


def pack_set_i16_param_command(
    object_id: bytes, memory_address: bytes, parameter: int
) -> PusTelecommand:
    action_id = GomspaceDeviceActionIds.PARAM_SET
    app_data = bytearray()
    app_data += memory_address
    app_data.append(2)
    app_data += struct.pack("!h", parameter)
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=action_id, user_data=app_data
    )


def pack_set_u32_param_command(object_id: bytes, memory_address: bytes, parameter: int):
    action_id = GomspaceDeviceActionIds.PARAM_SET
    app_data = bytearray()
    app_data += memory_address
    app_data.append(4)
    app_data += struct.pack("!I", parameter)
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=action_id, user_data=app_data
    )


def pack_set_i32_param_command(object_id: bytes, memory_address: bytes, parameter: int):
    action_id = GomspaceDeviceActionIds.PARAM_SET
    app_data = bytearray()
    app_data += memory_address
    app_data.append(4)
    app_data += struct.pack("!i", parameter)
    return make_fsfw_action_cmd(
        object_id=object_id, action_id=action_id, user_data=app_data
    )


def prompt_and_pack_get_param_command(q: DefaultPusQueueHelper, object_id: ObjectIdU32):
    table_id = int(input("Specify table ID: "))
    memory_address = int(input("Specify memory address: 0x"), 16)
    parameter_size = int(input("Specify parameter size: "))
    q.add_pus_tc(
        pack_get_param_command(
            object_id.as_bytes, table_id, memory_address, parameter_size
        )
    )


def prompt_and_pack_set_integer_param_command(
    q: DefaultPusQueueHelper, object_id: ObjectIdU32, ptype: ParamTypes
):
    memory_address = int(input("Specify memory address: 0x"), 16)
    memory_address = struct.pack("!H", memory_address)
    parameter = int(input("Specify parameter: "))
    if ptype == ParamTypes.U8:
        cmd = pack_set_u8_param_command(object_id.as_bytes, memory_address, parameter)
    elif ptype == ParamTypes.U16:
        cmd = pack_set_u16_param_command(object_id.as_bytes, memory_address, parameter)
    elif ptype == ParamTypes.U32:
        cmd = pack_set_u16_param_command(object_id.as_bytes, memory_address, parameter)
    elif ptype == ParamTypes.I8:
        cmd = pack_set_i8_param_command(object_id.as_bytes, memory_address, parameter)
    elif ptype == ParamTypes.I16:
        cmd = pack_set_i16_param_command(object_id.as_bytes, memory_address, parameter)
    elif ptype == ParamTypes.I32:
        cmd = pack_set_i32_param_command(object_id.as_bytes, memory_address, parameter)
    else:
        raise ValueError(f"Invalid parameter type {ptype} for this function")
    q.add_pus_tc(cmd)


def pack_ping_command(object_id: ObjectIdU32, data: bytearray) -> PusTelecommand:
    """ " Function to generate the command to ping a gomspace device
    @param object_id    Object Id of the gomspace device handler.
    @param data Bytearray containing the bytes to send to the gomspace device. For now the on board software
                supports only the handling of up to 33 bytes.
    @note   The ping request sends the specified data to a gompsace device. These
            data are simply copied by the device and then sent back.
    """
    return make_fsfw_action_cmd(
        object_id=object_id.as_bytes,
        action_id=GomspaceDeviceActionIds.PING,
        user_data=data,
    )


def pack_gnd_wdt_reset_command(object_id: ObjectIdBase) -> PusTelecommand:
    """ " Function to generate the command to reset the watchdog of a gomspace device.
    @param object_id    Object Id of the gomspace device handler.
    """
    return make_fsfw_action_cmd(
        object_id=object_id.as_bytes, action_id=GomspaceDeviceActionIds.WDT_RESET
    )


def pack_reboot_command(object_id: ObjectIdU32) -> PusTelecommand:
    """Function to generate the command which triggers a reboot of a gomspace device
    @param object_id    The object id of the gomspace device handler.
    """
    return make_fsfw_action_cmd(
        object_id=object_id.as_bytes, action_id=GomspaceDeviceActionIds.REBOOT
    )


def pack_request_full_hk_table_command(object_id: ObjectIdU32) -> PusTelecommand:
    """Function to generate the command to request the full housekeeping table from a gomspace
        device.
    @param object_id    The object id of the gomspace device handler.
    """
    return make_fsfw_action_cmd(
        object_id=object_id.as_bytes, action_id=GomspaceDeviceActionIds.REQUEST_HK_TABLE
    )