# -*- coding: utf-8 -*-
"""
@file   star_tracker.py
@brief  Star tracker commanding
@author J. Meier
@date   14.08.2021
"""
import datetime
import enum
import logging
import struct

from eive_tmtc.config.definitions import CustomServiceList
from eive_tmtc.pus_tm.defs import PrintWrapper
from eive_tmtc.utility.input_helper import InputHelper
from spacepackets.ecss.tc import PusTelecommand
from tmtccmd.config import TmtcDefinitionWrapper, OpCodeEntry
from tmtccmd.config.tmtc import tmtc_definitions_provider
from tmtccmd.tc.pus_3_fsfw_hk import create_request_one_diag_command, make_sid
from tmtccmd.tc import DefaultPusQueueHelper
from tmtccmd.tc.pus_200_fsfw_mode import pack_mode_data, Mode
from tmtccmd.util import ObjectIdU32
from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter
from eive_tmtc.config.object_ids import STR_ASSEMBLY, STAR_TRACKER_ID

_LOGGER = logging.getLogger(__name__)


class StarTrackerActionId(enum.IntEnum):
    PING = 0
    BOOT = 1
    REQ_VERSION = 2
    REQ_INTERFACE = 3
    REQ_TIME = 4
    UNLOCK = 6
    SWITCH_TO_BOOTLOADER_PROGRAM = 7
    REQ_POWER = 11
    TAKE_IMAGE = 15
    DOWNLOAD_IMAGE = 9
    UPLOAD_IMAGE = 10
    DOWNLOAD_CENTROID = 16
    UPLOAD_CENTROID = 17
    SUBSCRIPTION = 18
    IMAGE_PROCESSOR = 19
    REQ_SOLUTION = 24
    REQ_TEMPERATURE = 25
    REQ_HISTOGRAM = 28
    REQ_CONTRAST = 29
    LIMITS = 40
    MOUNTING = 41
    CAMERA = 42
    BLOB = 43
    CENTROIDING = 44
    LISA = 45
    MATCHING = 46
    TRACKING = 47
    VALIDATION = 48
    ALGO = 49
    CHECKSUM = 50
    FLASH_READ = 51
    FLASH_WRITE = 52
    DOWNLOAD_MATCHED_STAR = 53
    STOP_STR_HELPER = 55
    RESET_ERROR = 56
    CHANGE_DOWNLOAD_IMAGE = 57
    SET_JSON_FILE_NAME = 58
    SET_FLASH_READ_FILENAME = 59
    SET_TIME = 60
    DOWNLOAD_DBIMAGE = 61
    DOWNLOAD_BLOBPIXEL = 62
    DOWNLOAD_FPGA_IMAGE = 63
    CHANGE_FPGA_DOWNLOAD_FILE = 64
    UPLOAD_FPGA_IMAGE = 65
    FPGA_ACTION = 66
    REQ_CAMERA_PARAMS = 67
    REQ_LIMITS = 68
    REQ_LOG_LEVEL = 69
    REQ_MOUNTING = 70
    REQ_IMAGE_PROCESSOR = 71
    REQ_CENTROIDING = 72
    REQ_LISA = 73
    REQ_MATCHING = 74
    REQ_TRACKING = 75
    REQ_VALIDATION = 76
    REQ_ALGO = 77
    REQ_SUBSCRIPTION = 78
    REQ_LOG_SUBSCRIPTION = 79
    REQ_DEBUG_CAMERA = 80
    LOGLEVEL = 81
    LOG_SUBSCRIPTION = 82
    DEBUG_CAMERA = 83
    FIRMWARE_UPDATE = 84


class OpCodes:
    ON_BOOTLOADER = "on_bootloader"
    ON_FIRMWARE = "on_firmware"
    NORMAL = "nml"
    OFF = "off"
    PING = "ping"
    REQUEST_SOLUTION_SET_HK = "hk_req_sol"
    REQUEST_SOLUTION_SET_ACTION = "action_req_sol"
    TAKE_IMAGE = "take_image"
    UPLOAD_IMAGE = "upload_image"
    SET_IMG_PROCESSOR_MODE = "set_img_proc_mode"
    FW_UPDATE = "fw_update"


class Info:
    REQUEST_SOLUTION_SET_HK = "Request Solution Set HK once"
    REQUEST_SOLUTION_SET_ACTION = "Request Solution Set Action"
    UPLOAD_IMAGE = "Upload Image"
    TAKE_IMAGE = "Take Image"
    SET_IMG_PROCESSOR_MODE = "Set Image Processor Mode"
    FW_UPDATE = "Firmware Update"


class SetId(enum.IntEnum):
    VERSION = 2
    INTERFACE = 3
    POWER = 11
    TEMPERATURE = 25
    SOLUTION = 24
    HISTOGRAM = 28
    CHECKSUM = 50
    CAMERA = 67
    LIMITS = 68
    CENTROIDING = 72
    LISA = 73


class FileDefs:
    download_path = "/mnt/sd0/startracker"
    json_file = "/mnt/sd0/startracker/full.json"
    egse_ground_config = "/home/pi/arcsec/json/ground-config.json"
    egse_flight_config = "/home/pi/arcsec/json/flight-config.json"
    egse_solution_upload_img_config = "/home/pi/arcsec/json/upload-image-solution.json"
    egse_histogram_upload_img_config = (
        "/home/pi/arcsec/json/upload-image-histogram.json"
    )
    q7s_ground_config = "/mnt/sd0/startracker/ground-config.json"
    q7s_flight_config = "/mnt/sd0/startracker/flight-config.json"
    firmware2_1 = "/home/pi/arcsec/firmware/sagitta-2-1.bin"
    firmware22_1 = "/home/pi/arcsec/firmware/sagitta-22-1.bin"
    firmware_origin = "/home/pi/arcsec/firmware/sagitta-origin.bin"
    FW_SLOT_Q7S = "/mnt/sd0/startracker/updates/sagitta-update.bin"


json_dict = {
    "1": ("Q7S flight config", FileDefs.q7s_flight_config),
    "2": ("Q7S ground config", FileDefs.q7s_ground_config),
    "3": ("EGSE flight config", FileDefs.egse_flight_config),
    "4": ("EGSE ground config", FileDefs.egse_ground_config),
    "5": (
        "EGSE get solution, upload image config",
        FileDefs.egse_solution_upload_img_config,
    ),
    "6": (
        "EGSE get histogram, upload image config",
        FileDefs.egse_solution_upload_img_config,
    ),
}

FW_DICT = {
    "0": ("Firmware Update Q7S", FileDefs.FW_SLOT_Q7S),
    "1": ("Firmware Major = 2, Minor = 1", FileDefs.firmware2_1),
    "2": ("Firmware Major = 22, Minor = 1", FileDefs.firmware22_1),
    "3": ("Firmware Origin", FileDefs.firmware_origin),
}

UPLOAD_IMAGE_DICT = {
    "0": ("custom path", "Custom Path"),
    "1": ("q7s gemma", "/mnt/sd0/startracker/gemma.bin"),
    "2": ("egse gemma", "/home/pi/arcsec/star-images/gemma.bin"),
    "3": ("q7s polaris", "/mnt/sd0/startracker/polaris.bin"),
    "4": ("egse polaris", "/home/pi/arcsec/star-images/polaris.bin"),
}


class StartRegion:
    # Definition according to datasheet (which turned out to be partially faulty)
    BOOTLOADER = 0
    STAR_TRACKER_FIRMWARE = 1


class PartitionSize:
    # Size of most recent firmware image
    STAR_TRACKER_FIRMWARE = 464572


class Submode(enum.IntEnum):
    DEFAULT = 0
    BOOTLOADER = 1
    FIRMWARE = 2


def prompt_object_id_mode_cmd() -> bytes:
    cmd_assy = input("Command Assembly (0) or Device Handler (1) ?: ")
    if cmd_assy == "0":
        return STR_ASSEMBLY
    else:
        return STAR_TRACKER_ID


def pack_star_tracker_commands(  # noqa C901
    object_id: ObjectIdU32, q: DefaultPusQueueHelper, op_code: str
):
    q.add_log_cmd(
        f"Generate command for star tracker with object id: {object_id.as_hex_string}"
    )
    obyt = object_id.as_bytes
    if op_code == OpCodes.ON_BOOTLOADER:
        q.add_log_cmd("Star tracker: Mode On, Submode Bootloader")
        data = pack_mode_data(prompt_object_id_mode_cmd(), Mode.ON, Submode.BOOTLOADER)
        q.add_pus_tc(PusTelecommand(service=200, subservice=1, app_data=data))
    if op_code == OpCodes.ON_FIRMWARE:
        q.add_log_cmd("Star tracker: Mode On, Submode Firmware")
        data = pack_mode_data(prompt_object_id_mode_cmd(), Mode.ON, Submode.FIRMWARE)
        q.add_pus_tc(PusTelecommand(service=200, subservice=1, app_data=data))
    if op_code == OpCodes.NORMAL:
        q.add_log_cmd("Star tracker: Mode Normal")
        data = pack_mode_data(prompt_object_id_mode_cmd(), Mode.NORMAL, 0)
        q.add_pus_tc(PusTelecommand(service=200, subservice=1, app_data=data))
    if op_code == OpCodes.OFF:
        q.add_log_cmd("Star tracker: Mode Off")
        data = pack_mode_data(prompt_object_id_mode_cmd(), Mode.OFF, 0)
        q.add_pus_tc(PusTelecommand(service=200, subservice=1, app_data=data))
    if op_code == OpCodes.REQUEST_SOLUTION_SET_HK:
        q.add_log_cmd(Info.REQUEST_SOLUTION_SET_HK)
        q.add_pus_tc(create_request_one_diag_command(make_sid(obyt, SetId.SOLUTION)))
    if op_code == OpCodes.REQUEST_SOLUTION_SET_ACTION:
        q.add_log_cmd("Star tracker: Request solution")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_SOLUTION)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "4":
        q.add_log_cmd("Star tracker: Mode Raw")
        data = pack_mode_data(obyt, Mode.RAW, 0)
        q.add_pus_tc(PusTelecommand(service=200, subservice=1, app_data=data))
    if op_code == OpCodes.PING:
        q.add_log_cmd("Star tracker: Ping")
        data = obyt + struct.pack("!I", StarTrackerActionId.PING)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "6":
        q.add_log_cmd("Star tracker:  Switch to bootloader program")
        data = obyt + struct.pack(
            "!I", StarTrackerActionId.SWITCH_TO_BOOTLOADER_PROGRAM
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "7":
        q.add_log_cmd("Star tracker: Temperature request")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_TEMPERATURE)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "8":
        q.add_log_cmd("Star tracker: Request version")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_VERSION)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "9":
        q.add_log_cmd("Star tracker: Request interface")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_INTERFACE)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "10":
        q.add_log_cmd("Star tracker: Request power")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_POWER)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "11":
        q.add_log_cmd("Star tracker: Set subscription parameters")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.SUBSCRIPTION)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "12":
        q.add_log_cmd("Star tracker: Boot")
        data = obyt + struct.pack("!I", StarTrackerActionId.BOOT)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "13":
        q.add_log_cmd("Star tracker: Request time")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_TIME)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == OpCodes.UPLOAD_IMAGE:
        q.add_log_cmd("Star tracker: Upload image")
        image = get_upload_image()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.UPLOAD_IMAGE)
            + bytearray(image, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "16":
        q.add_log_cmd("Star tracker: Download image")
        path = input("Specify storage location (default - /mnt/sd0/startracker): ")
        if not path:
            path = FileDefs.download_path
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.DOWNLOAD_IMAGE)
            + bytearray(path, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "17":
        q.add_log_cmd("Star tracker: Set limits")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.LIMITS)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "18":
        q.add_log_cmd("Star tracker: Set tracking parameters")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.TRACKING)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "19":
        q.add_log_cmd("Star tracker: Mounting")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.MOUNTING)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "20":
        q.add_log_cmd("Star tracker: Camera")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.CAMERA)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "22":
        q.add_log_cmd("Star tracker: Centroiding")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.CENTROIDING)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "23":
        q.add_log_cmd("Star tracker: LISA")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.LISA)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "24":
        q.add_log_cmd("Star tracker: Matching")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.MATCHING)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "25":
        q.add_log_cmd("Star tracker: Validation")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.VALIDATION)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "26":
        q.add_log_cmd("Star tracker: Algo")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.ALGO)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == OpCodes.TAKE_IMAGE:
        q.add_log_cmd("Star tracker: Take image")
        actionid = int(
            input("Specify parameter ID (4: take image, 7: get histogram): ")
        )
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.TAKE_IMAGE)
            + struct.pack("!B", actionid)
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "28":
        q.add_log_cmd("Star tracker: Stop str helper")
        data = obyt + struct.pack("!I", StarTrackerActionId.STOP_STR_HELPER)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "30":
        q.add_log_cmd("Star tracker: Set name of download image")
        filename = input("Specify download image name: ")
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.CHANGE_DOWNLOAD_IMAGE)
            + bytearray(filename, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "31":
        q.add_log_cmd("Star tracker: Request histogram")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_HISTOGRAM)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "32":
        q.add_log_cmd("Star tracker: Request contrast")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_CONTRAST)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "33":
        q.add_log_cmd("Star tracker: Set json filename")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.SET_JSON_FILE_NAME)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "35":
        q.add_log_cmd("Star tracker: Flash read")
        data = pack_read_command(obyt)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "36":
        q.add_log_cmd("Star tracker: Set flash read filename")
        filename = input("Specify filename: ")
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.SET_FLASH_READ_FILENAME)
            + bytearray(filename, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "37":
        q.add_log_cmd("Star tracker: Get checksum")
        data = pack_checksum_command(obyt)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "38":
        q.add_log_cmd("Star tracker: Set time")
        unix_time = 1640783543
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.SET_TIME)
            + struct.pack("!Q", unix_time)
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "39":
        q.add_log_cmd("Star tracker: Download Centroid")
        id = 0
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.DOWNLOAD_CENTROID)
            + struct.pack("!B", id)
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "41":
        q.add_log_cmd("Star tracker: Download matched star")
        id = 0
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.DOWNLOAD_MATCHED_STAR)
            + struct.pack("!B", id)
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "42":
        q.add_log_cmd("Star tracker: Download DB Image")
        id = 0
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.DOWNLOAD_DBIMAGE)
            + struct.pack("!B", id)
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "43":
        q.add_log_cmd("Star tracker: Download Blob Pixel")
        id = 0
        type = 1  # 0 - normal, 1 - fast
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.DOWNLOAD_BLOBPIXEL)
            + struct.pack("!B", id)
            + struct.pack("!B", type)
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "44":
        q.add_log_cmd("Star tracker: Download FPGA Image")
        position = int(input("Start position: "))
        length = int(input("Size to download: "))
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.DOWNLOAD_FPGA_IMAGE)
            + struct.pack("!I", position)
            + struct.pack("!I", length)
            + bytearray(FileDefs.downloadFpgaImagePath, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "45":
        q.add_log_cmd("Star tracker: Change donwload FPGA image file name")
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.CHANGE_FPGA_DOWNLOAD_FILE)
            + bytearray(FileDefs.downloadFpgaImageName, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "46":
        q.add_log_cmd("Star tracker: Upload FPGA image")
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.UPLOAD_FPGA_IMAGE)
            + bytearray(FileDefs.uploadFpgaImageName, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "47":
        q.add_log_cmd("Star tracker: FPGA action")
        id = 3
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.FPGA_ACTION)
            + struct.pack("!B", id)
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "48":
        q.add_log_cmd("Star tracker: Unlock")
        data = obyt + struct.pack("!I", StarTrackerActionId.UNLOCK)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "49":
        q.add_log_cmd("Star tracker: Request camera parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_CAMERA_PARAMS)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "50":
        q.add_log_cmd("Star tracker: Request limits")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_LIMITS)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == OpCodes.SET_IMG_PROCESSOR_MODE:
        q.add_log_cmd(Info.SET_IMG_PROCESSOR_MODE)
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.IMAGE_PROCESSOR)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "52":
        q.add_log_cmd("Star tracker: EGSE load ground config camera parameters")
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.CAMERA)
            + bytearray(FileDefs.egse_ground_config, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "53":
        q.add_log_cmd("Star tracker: EGSE load flight config camera parameters")
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.CAMERA)
            + bytearray(FileDefs.egse_flight_config, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "54":
        q.add_log_cmd("Star tracker: Request log level parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_LOG_LEVEL)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "55":
        q.add_log_cmd("Star tracker: Request mounting parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_MOUNTING)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "56":
        q.add_log_cmd("Star tracker: Request image processor parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_IMAGE_PROCESSOR)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "57":
        q.add_log_cmd("Star tracker: Request centroiding parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_CENTROIDING)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "58":
        q.add_log_cmd("Star tracker: Request lisa parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_LISA)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "59":
        q.add_log_cmd("Star tracker: Request matching parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_MATCHING)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "60":
        q.add_log_cmd("Star tracker: Request tracking parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_TRACKING)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "61":
        q.add_log_cmd("Star tracker: Request validation parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_VALIDATION)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "62":
        q.add_log_cmd("Star tracker: Request algo parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_ALGO)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "63":
        q.add_log_cmd("Star tracker: Request subscription parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_SUBSCRIPTION)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "64":
        q.add_log_cmd("Star tracker: Request log subscription parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_LOG_SUBSCRIPTION)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "65":
        q.add_log_cmd("Star tracker: Request debug camera parameters")
        data = obyt + struct.pack("!I", StarTrackerActionId.REQ_DEBUG_CAMERA)
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "66":
        q.add_log_cmd("Star tracker: Set log level parameters")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.LOGLEVEL)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "67":
        q.add_log_cmd("Star tracker: Set log subscription parameters")

        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.LOG_SUBSCRIPTION)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == "68":
        q.add_log_cmd("Star tracker: Set debug camera parameters")
        json_file = get_config_file()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.DEBUG_CAMERA)
            + bytearray(json_file, "utf-8")
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))
    if op_code == OpCodes.FW_UPDATE:
        q.add_log_cmd(Info.FW_UPDATE)
        firmware = get_firmware()
        data = (
            obyt
            + struct.pack("!I", StarTrackerActionId.FIRMWARE_UPDATE)
            + firmware.encode()
        )
        q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data))


def pack_read_command(object_id: bytes) -> bytearray:
    start_region = StartRegion.STAR_TRACKER_FIRMWARE
    size = PartitionSize.STAR_TRACKER_FIRMWARE
    path = input("Specify storage location (default - /mnt/sd0/startracker): ")
    if not path:
        path = FileDefs.download_path
    data = (
        object_id
        + struct.pack("!I", StarTrackerActionId.FLASH_READ)
        + struct.pack("!B", start_region)
        + struct.pack("!I", size)
        + bytearray(path, "utf-8")
    )
    return bytearray(data)


def pack_checksum_command(object_id: bytes) -> bytearray:
    start_region = StartRegion.STAR_TRACKER_FIRMWARE
    address = 0
    size = PartitionSize.STAR_TRACKER_FIRMWARE
    data = (
        object_id
        + struct.pack("!I", StarTrackerActionId.CHECKSUM)
        + struct.pack("!B", start_region)
        + struct.pack("!I", address)
        + struct.pack("!I", size)
    )
    return bytearray(data)


def get_config_file() -> str:
    _LOGGER.info("Specify json file")
    input_helper = InputHelper(json_dict)
    key = input_helper.get_key()
    json_file = json_dict[key][1]
    return json_file


def get_firmware() -> str:
    _LOGGER.info("Specify firmware file")
    bin_select = int(input("Use hardcoded paths (0) or specify path manually (1) ?: "))
    if bin_select == 0:
        input_helper = InputHelper(FW_DICT)
        key = input_helper.get_key()
        firmware = FW_DICT[key][1]
    else:
        firmware = input("Specify absolute path of the firmware update file: ")
    return firmware


def get_upload_image() -> str:
    _LOGGER.info("Specify image to upload")
    input_helper = InputHelper(UPLOAD_IMAGE_DICT)
    key = input_helper.get_key()
    if UPLOAD_IMAGE_DICT[key][0] == "custom path":
        image = input("Please specify custom absolute path: ")
    else:
        image = UPLOAD_IMAGE_DICT[key][1]
    return image


def handle_str_hk_data(set_id: int, hk_data: bytes, pw: PrintWrapper):
    pw.dlog(f"Received STR HK set with set ID {set_id}")
    if set_id == SetId.SOLUTION:
        handle_solution_set(hk_data, pw)
    elif set_id == SetId.TEMPERATURE:
        handle_temperature_set(hk_data, pw)
    else:
        _LOGGER.warning(f"HK parsing for Star Tracker set ID {set_id} unimplemented")


def unpack_time_hk(hk_data: bytes, current_idx: int, pw: PrintWrapper) -> int:
    ticks_time_fmt = "!IQ"
    fmt_len = struct.calcsize(ticks_time_fmt)
    (ticks, unix_time) = struct.unpack(
        ticks_time_fmt, hk_data[current_idx : current_idx + fmt_len]
    )
    unix_as_dt = datetime.datetime.fromtimestamp(
        int(round(unix_time / 10e6)), tz=datetime.timezone.utc
    )
    pw.dlog(f"Ticks: {ticks} | UNIX time: {unix_time}")
    pw.dlog(f"UNIX as datetime: {unix_as_dt}")
    current_idx += fmt_len
    return current_idx


def handle_temperature_set(hk_data: bytes, pw: PrintWrapper):
    pw.dlog("Received temperature set")
    if len(hk_data) < 24:
        _LOGGER.warning(f"Temperature dataset HK with length {len(hk_data)} too short")
    current_idx = unpack_time_hk(hk_data, 0, pw)
    temps_fmt = "!fff"
    fmt_len = struct.calcsize(temps_fmt)
    (mcu_temp, cmos_temp, fpga_temp) = struct.unpack(
        temps_fmt, hk_data[current_idx : current_idx + fmt_len]
    )
    pw.dlog(f"MCU Temperature: {mcu_temp}")
    pw.dlog(f"CMOS Temperature: {cmos_temp}")
    pw.dlog(f"FPGA Temperature: {fpga_temp}")
    current_idx += fmt_len
    FsfwTmTcPrinter.get_validity_buffer(hk_data[current_idx:], 5)


def handle_solution_set(hk_data: bytes, pw: PrintWrapper):
    pw.dlog("Received solution set")
    if len(hk_data) < 78:
        _LOGGER.warning(
            f"Solution dataset HK data with length {len(hk_data)} too short"
        )
        return
    current_idx = unpack_time_hk(hk_data, 0, pw)
    calib_quaternions_fmt = "!ffff"
    fmt_len = struct.calcsize(calib_quaternions_fmt)
    (calib_q_w, calib_q_x, calib_q_y, calib_q_z) = struct.unpack(
        calib_quaternions_fmt, hk_data[current_idx : current_idx + fmt_len]
    )
    pw.dlog("Calibrated Quaternions")
    pw.dlog(f"Quaternion w: {calib_q_w}")
    pw.dlog(f"Quaternion x: {calib_q_x}")
    pw.dlog(f"Quaternion y: {calib_q_y}")
    pw.dlog(f"Quaternion z: {calib_q_z}")
    current_idx += fmt_len
    track_fmt = "!fffff"
    fmt_len = struct.calcsize(track_fmt)
    (track_confidence, track_q_w, track_q_x, track_q_y, track_q_z) = struct.unpack(
        track_fmt, hk_data[current_idx : current_idx + fmt_len]
    )
    pw.dlog(f"Track Confidence: {track_confidence}")
    pw.dlog(f"Track QW: {track_q_w}")
    pw.dlog(f"Track QX: {track_q_x}")
    pw.dlog(f"Track QY: {track_q_y}")
    pw.dlog(f"Track QZ: {track_q_z}")
    current_idx += fmt_len
    track_removed = hk_data[current_idx]
    pw.dlog(f"Number of stars removed from tracking solution: {track_removed}")
    current_idx += 1
    stars_centroided = hk_data[current_idx]
    pw.dlog(f"Centroided stars: {stars_centroided}")
    current_idx += 1
    stars_matched_database = hk_data[current_idx]
    pw.dlog(f"Stars matched: {stars_matched_database}")
    current_idx += 1
    # Result of LISA: Lost in space algorithm
    lisa_fmt = "!fffffB"
    fmt_len = struct.calcsize(lisa_fmt)
    (
        lisa_q_w,
        lisa_q_x,
        lisa_q_y,
        lisa_q_z,
        lisa_percentage_close_stars,
        lisa_number_close_stars,
    ) = struct.unpack(lisa_fmt, hk_data[current_idx : current_idx + fmt_len])
    pw.dlog(f"LISA QW: {lisa_q_w}")
    pw.dlog(f"LISA QX: {lisa_q_x}")
    pw.dlog(f"LISA QY: {lisa_q_y}")
    pw.dlog(f"LISA QZ: {lisa_q_z}")
    pw.dlog(
        f"Percentage of close stars in LISA solution: {lisa_percentage_close_stars}"
    )
    pw.dlog(f"Number of close stars in LISA solution: {lisa_number_close_stars}")
    current_idx += fmt_len
    str_mode = hk_data[current_idx]
    pw.dlog(f"STR mode: {str_mode}")
    current_idx += 1
    is_trustworthy = hk_data[current_idx]
    pw.dlog(f"Trustworthy solution: {is_trustworthy}")
    current_idx += 1
    stable_count = struct.unpack("!I", hk_data[current_idx : current_idx + 4])[0]
    pw.dlog(f"Stable count: {stable_count}")
    current_idx += 4
    solution_strategy = hk_data[current_idx]
    pw.dlog(f"Solution strategy: {solution_strategy}")
    current_idx += 1
    FsfwTmTcPrinter.get_validity_buffer(hk_data[current_idx:], 23)


@tmtc_definitions_provider
def add_str_cmds(defs: TmtcDefinitionWrapper):
    oce = OpCodeEntry()
    oce.add(OpCodes.ON_BOOTLOADER, "Mode On, Submode Bootloader")
    oce.add(OpCodes.ON_FIRMWARE, "Mode On, Submode Firmware")
    oce.add(OpCodes.NORMAL, "Mode Normal")
    oce.add(OpCodes.OFF, "Mode Off")
    # oce.add("4", "Star Tracker: Mode Raw")
    oce.add(OpCodes.PING, "Star Tracker: Ping")
    oce.add(OpCodes.TAKE_IMAGE, "Take Image")
    oce.add(OpCodes.REQUEST_SOLUTION_SET_HK, Info.REQUEST_SOLUTION_SET_HK)
    oce.add(OpCodes.REQUEST_SOLUTION_SET_ACTION, Info.REQUEST_SOLUTION_SET_ACTION)
    oce.add(OpCodes.UPLOAD_IMAGE, Info.UPLOAD_IMAGE)
    oce.add(OpCodes.SET_IMG_PROCESSOR_MODE, Info.SET_IMG_PROCESSOR_MODE)
    oce.add("6", "Star Tracker: Switch to bootloader program")
    oce.add("7", "Star Tracker: Request temperature")
    oce.add("8", "Star Tracker: Request version")
    oce.add("9", "Star Tracker: Request interface")
    oce.add("10", "Star Tracker: Request power")
    oce.add("11", "Star Tracker: Set subscription parameters")
    oce.add("12", "Star Tracker: Boot image (requires bootloader mode)")
    oce.add("13", "Star Tracker: Request time")
    oce.add("14", "Star Tracker: Request solution")
    oce.add("16", "Star Tracker: Download image")
    oce.add("17", "Star Tracker: Set limit parameters")
    oce.add("17", "Star Tracker: Set limit parameters")
    oce.add("18", "Star Tracker: Set tracking parameters")
    oce.add("19", "Star Tracker: Set mounting parameters")
    oce.add("20", "Star Tracker: Set camera parameters")
    oce.add("22", "Star Tracker: Set centroiding parameters")
    oce.add("23", "Star Tracker: Set LISA parameters")
    oce.add("24", "Star Tracker: Set matching parameters")
    oce.add("25", "Star Tracker: Set validation parameters")
    oce.add("26", "Star Tracker: Set algo parameters")
    oce.add("28", "Star Tracker: Stop str helper")
    oce.add("30", "Star Tracker: Set name of download image")
    oce.add("31", "Star Tracker: Request histogram")
    oce.add("32", "Star Tracker: Request contrast")
    oce.add("33", "Star Tracker: Set json filename")
    oce.add("35", "Star Tracker: Flash read")
    oce.add("36", "Star Tracker: Set flash read filename")
    oce.add("37", "Star Tracker: Get checksum")
    oce.add("49", "Star Tracker: Request camera parameter")
    oce.add("50", "Star Tracker: Request limits")
    oce.add("52", "Star Tracker: (EGSE only) Load camera ground config")
    oce.add("53", "Star Tracker: (EGSE only) Load camera flight config")
    oce.add("54", "Star Tracker: Request log level parameters")
    oce.add("55", "Star Tracker: Request mounting parameters")
    oce.add("56", "Star Tracker: Request image processor parameters")
    oce.add("57", "Star Tracker: Request centroiding parameters")
    oce.add("58", "Star Tracker: Request lisa parameters")
    oce.add("59", "Star Tracker: Request matching parameters")
    oce.add("60", "Star Tracker: Request tracking parameters")
    oce.add("61", "Star Tracker: Request validation parameters")
    oce.add("62", "Star Tracker: Request algo parameters")
    oce.add("63", "Star Tracker: Request subscription parameters")
    oce.add("64", "Star Tracker: Request log subscription parameters")
    oce.add("65", "Star Tracker: Request debug camera parameters")
    oce.add("66", "Star Tracker: Set log level parameters")
    oce.add("67", "Star Tracker: Set log subscription parameters")
    oce.add("68", "Star Tracker: Set debug camera parameters")
    oce.add(OpCodes.FW_UPDATE, Info.FW_UPDATE)
    oce.add("70", "Star Tracker: Disable timestamp generation")
    oce.add("71", "Star Tracker: Enable timestamp generation")
    defs.add_service(CustomServiceList.STAR_TRACKER.value, "Star Tracker", oce)