"""
FRAME DISPATCHER - We are dispatching the received frames to the needed functions


"""
import threading
import structlog
from modem_frametypes import FRAME_TYPE as FR_TYPE
import event_manager
from data_frame_factory import DataFrameFactory

from frame_handler import FrameHandler
from frame_handler_ping import PingFrameHandler
from frame_handler_cq import CQFrameHandler
from frame_handler_arq_session import ARQFrameHandler

class DISPATCHER():

    FRAME_HANDLER = {
        FR_TYPE.ARQ_SESSION_OPEN_ACK.value: {"class": ARQFrameHandler, "name": "ARQ OPEN ACK"},
        FR_TYPE.ARQ_SESSION_OPEN.value: {"class": ARQFrameHandler, "name": "ARQ Data Channel Open"},
        FR_TYPE.ARQ_SESSION_INFO_ACK.value: {"class": ARQFrameHandler, "name": "ARQ INFO ACK"},
        FR_TYPE.ARQ_SESSION_INFO.value: {"class": ARQFrameHandler, "name": "ARQ Data Channel Info"},
        FR_TYPE.ARQ_CONNECTION_CLOSE.value: {"class": ARQFrameHandler, "name": "ARQ CLOSE SESSION"},
        FR_TYPE.ARQ_CONNECTION_HB.value: {"class": ARQFrameHandler, "name": "ARQ HEARTBEAT"},
        FR_TYPE.ARQ_CONNECTION_OPEN.value: {"class": ARQFrameHandler, "name": "ARQ OPEN SESSION"},
        FR_TYPE.ARQ_STOP.value: {"class": ARQFrameHandler, "name": "ARQ STOP"},
        FR_TYPE.ARQ_STOP_ACK.value: {"class": ARQFrameHandler, "name": "ARQ STOP ACK"},
        FR_TYPE.BEACON.value: {"class": FrameHandler, "name": "BEACON"},
        FR_TYPE.ARQ_BURST_FRAME.value:{"class": ARQFrameHandler, "name": "BURST FRAME"},
        FR_TYPE.ARQ_BURST_ACK.value: {"class": ARQFrameHandler, "name":  "BURST ACK"},
        FR_TYPE.CQ.value: {"class": CQFrameHandler, "name":  "CQ"},
        FR_TYPE.PING_ACK.value: {"class": FrameHandler, "name":  "PING ACK"},
        FR_TYPE.PING.value: {"class": PingFrameHandler, "name":  "PING"},
        FR_TYPE.QRV.value: {"class": FrameHandler, "name":  "QRV"},
        FR_TYPE.IS_WRITING.value: {"class": FrameHandler, "name": "IS_WRITING"},
        FR_TYPE.FEC.value: {"class": FrameHandler, "name":  "FEC"},
        FR_TYPE.FEC_WAKEUP.value: {"class": FrameHandler, "name":  "FEC WAKEUP"},
    }

    def __init__(self, config, event_manager, states, modem):
        self.log = structlog.get_logger("frame_dispatcher")

        self.log.info("loading frame dispatcher.....\n")
        self.config = config
        self.states = states
        self.event_manager = event_manager

        self._initialize_handlers(config, states)

        self.modem = modem
        self.data_queue_received = modem.data_queue_received

        self.arq_sessions = []

    def _initialize_handlers(self, config, states):
        """Initializes various data handlers."""

        self.frame_factory = DataFrameFactory(config)

    def start(self):
        """Starts worker threads for transmit and receive operations."""
        threading.Thread(target=self.worker_receive, name="Receive Worker", daemon=True).start()

    def worker_receive(self) -> None:
        """Queue received data for processing"""
        while True:
            data = self.data_queue_received.get()
            self.new_process_data(
                data['payload'],
                data['freedv'],
                data['bytes_per_frame'],
                data['snr'],
                data['frequency_offset'],
            )

    def new_process_data(self, bytes_out, freedv, bytes_per_frame: int, snr, frequency_offset) -> None:
        # get frame as dictionary
        deconstructed_frame = self.frame_factory.deconstruct(bytes_out)
        frametype = deconstructed_frame["frame_type_int"]

        if frametype not in self.FRAME_HANDLER:
            self.log.warning(
                "[Modem] ARQ - other frame type", frametype=FR_TYPE(frametype).name)
            return
        
        # instantiate handler
        handler_class = self.FRAME_HANDLER[frametype]['class']
        handler: FrameHandler = handler_class(self.FRAME_HANDLER[frametype]['name'],
                                self.config,
                                self.states,
                                self.event_manager,
                                self.modem)
        handler.handle(deconstructed_frame, snr, frequency_offset, freedv, bytes_per_frame)

    def get_id_from_frame(self, data):
        if data[:1] == FR_TYPE.ARQ_SESSION_OPEN:
            return data[13:14]
        return None
