#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributors.

import time
from ctypes import c_bool
from multiprocessing import Queue
from multiprocessing.sharedctypes import Value
from queue import Empty

from fenrirscreenreader.core import debug
from fenrirscreenreader.core.eventData import FenrirEventType


class EventManager:
    def __init__(self):
        self.running = Value(c_bool, True)
        # Bounded queue to prevent memory exhaustion
        self._eventQueue = Queue(maxsize=100)
        self.clean_event_queue()

    def initialize(self, environment):
        self.env = environment

    def shutdown(self):
        self.clean_event_queue()

    def proceed_event_loop(self):
        event = self._eventQueue.get()
        st = time.time()
        self.event_dispatcher(event)
        # print('NET loop ' + str(time.time() - st))

    def event_dispatcher(self, event):
        self.env["runtime"]["DebugManager"].write_debug_out(
            "EventManager:event_dispatcher:start: event: "
            + str(event["Type"]),
            debug.DebugLevel.INFO,
        )

        if not event:
            return
        if not event["Type"]:
            return
        if event["Type"] == FenrirEventType.ignore:
            return
        elif event["Type"] == FenrirEventType.stop_main_loop:
            self.handle_stop_main_loop(event)
        elif event["Type"] == FenrirEventType.screen_update:
            self.env["runtime"]["FenrirManager"].handle_screen_update(event)
        elif event["Type"] == FenrirEventType.keyboard_input:
            self.env["runtime"]["FenrirManager"].handle_input(event)
        elif event["Type"] == FenrirEventType.plug_input_device:
            self.env["runtime"]["FenrirManager"].handle_plug_input_device(
                event
            )
        elif event["Type"] == FenrirEventType.screen_changed:
            self.env["runtime"]["FenrirManager"].handle_screen_change(event)
        elif event["Type"] == FenrirEventType.heart_beat:
            self.env["runtime"]["FenrirManager"].handle_heart_beat(event)
        elif event["Type"] == FenrirEventType.execute_command:
            self.env["runtime"]["FenrirManager"].handle_execute_command(event)
        elif event["Type"] == FenrirEventType.byte_input:
            self.env["runtime"]["FenrirManager"].handle_byte_input(event)
        elif event["Type"] == FenrirEventType.remote_incomming:
            self.env["runtime"]["FenrirManager"].handle_remote_incomming(event)

    def is_main_event_loop_running(self):
        return self.running.value == 1

    def start_main_event_loop(self):
        self.running.value = 1
        while self.is_main_event_loop_running():
            self.proceed_event_loop()

    def handle_stop_main_loop(self, event):
        self.running.value = 0
        time.sleep(0.1)

    def stop_main_event_loop(self):
        self._eventQueue.put(
            {"Type": FenrirEventType.stop_main_loop, "data": None}
        )

    def clean_event_queue(self):
        if self._eventQueue.empty():
            return
        try:
            while True:
                self._eventQueue.get_nowait()
        except Empty:
            pass

    def get_event_queue(self):
        return self._eventQueue

    def get_running(self):
        return self.running

    def get_event_queue_size(self):
        return self._eventQueue.qsize()

    def put_to_event_queue(self, event, data):
        if not isinstance(event, FenrirEventType):
            return False
        if event == FenrirEventType.ignore:
            return False
        # Use bounded queue - if full, this will block briefly or drop older
        # events
        try:
            self._eventQueue.put({"Type": event, "data": data}, timeout=0.1)
        except Exception as e:
            # Queue full - drop oldest event and add new one for critical
            # events
            if event in [
                FenrirEventType.screen_update,
                FenrirEventType.keyboard_input,
            ]:
                try:
                    self._eventQueue.get_nowait()  # Remove oldest
                    self._eventQueue.put(
                        {"Type": event, "data": data}, timeout=0.1
                    )
                except BaseException:
                    pass  # If still can't add, drop the event
            # For non-critical events, just drop them if queue is full
        return True
