#!/usr/bin/python3.8
"""
@file   obsw_sequential_sender_receiver.py
@date   01.11.2019
@brief  Used to send multiple TCs in sequence and listen for replies after each sent TC
"""
import sys
import time

import config.obsw_config as g
from sendreceive.obsw_command_sender_receiver import CommandSenderReceiver
from sendreceive.obsw_tm_listener import TmListener
from comIF.obsw_com_interface import CommunicationInterface
from utility.obsw_tmtc_printer import TmTcPrinter
from utility.obsw_logger import get_logger
from tc.obsw_pus_tc_base import TcQueueT

LOGGER = get_logger()


class SequentialCommandSenderReceiver(CommandSenderReceiver):
    """
    Specific implementation of CommandSenderReceiver to send multiple telecommands in sequence
    """
    def __init__(self, com_interface: CommunicationInterface, tmtc_printer: TmTcPrinter,
                 tm_listener: TmListener, tc_queue: TcQueueT):
        """
        :param com_interface: CommunicationInterface object, passed on to CommandSenderReceiver
        :param tm_listener: TmListener object which runs in the background and receives
                            all Telemetry
        :param tmtc_printer: TmTcPrinter object, passed on to CommandSenderReceiver
        for this time period
        """
        super().__init__(com_interface=com_interface, tmtc_printer=tmtc_printer,
                         tm_listener=tm_listener)
        self._tc_queue = tc_queue
        self.__first_reply_received = False
        self.__all_replies_received = False
        self.__mode_op_finished = False

    def send_queue_tc_and_receive_tm_sequentially(self):
        """
        Primary function which is called for sequential transfer.
        :return:
        """
        self._tm_listener.mode_id = g.ModeList.ServiceTestMode
        self._tm_listener.event_mode_change.set()
        # tiny delay for tm listener
        time.sleep(0.1)
        self.__send_and_receive_first_packet()
        # this flag is set in the separate thread !
        try:
            self.__handle_tc_sending()
        except (KeyboardInterrupt, SystemExit):
            LOGGER.info("Closing TMTC Client")
            sys.exit()

    def __handle_tc_sending(self):
        while not self.__all_replies_received:
            while not self._tc_queue.__len__() == 0:
                self.__perform_next_tc_send()
                if self._tc_queue.__len__() == 0:
                    self._start_time = time.time()
            self._check_for_timeout()
            if not self.__mode_op_finished:
                self._tm_listener.event_mode_op_finished.set()
                self.__mode_op_finished = True
        LOGGER.info("SequentialSenderReceiver: All replies received!")

    def __perform_next_tc_send(self):
        if self._tm_listener.event_reply_received.is_set():
            self._reply_received = True
            self._tm_listener.event_reply_received.clear()
        # this flag is set in the separate receiver thread too
        if self._reply_received:
            self.print_tm_queue(self._tm_listener.retrieve_tm_packet_queue())
            self._tm_listener.clear_tm_packet_queue()
            self.__send_next_telecommand()
            self._reply_received = False
        # just calculate elapsed time if start time has already been set (= command has been sent)
        else:
            self._check_for_timeout()

    def __send_and_receive_first_packet(self):
        tc_queue_tuple = self._tc_queue.pop()
        if self.check_queue_entry(tc_queue_tuple):
            pus_packet, pus_packet_info = tc_queue_tuple
            self._com_interface.send_telecommand(pus_packet, pus_packet_info)
        else:
            self.__send_and_receive_first_packet()

    def __send_next_telecommand(self):
        tc_queue_tuple = self._tc_queue.pop()
        if self.check_queue_entry(tc_queue_tuple):
            self._start_time = time.time()
            pus_packet, pus_packet_info = tc_queue_tuple
            self._com_interface.send_telecommand(pus_packet, pus_packet_info)
        elif self._tc_queue.__len__() == 0:
            # Special case: Last queue entry is not a Telecommand
            self.__all_replies_received = True
        else:
            # If the queue entry was not a telecommand, send next telecommand
            self.__perform_next_tc_send()