import os
import sys
import cv2
import numpy as np
from datetime import datetime

from PyQt6.QtWidgets import QVBoxLayout, QLabel, QGraphicsTextItem, QWidget
from PyQt6.QtGui import QImage
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QMouseEvent

from Model.ESP32Cam import ESP32Cam
from View.FrameView import FrameView


if getattr(sys, 'frozen', False):
    ASSETS_PATH = os.path.join(sys._MEIPASS, "assets")
else:
    ASSETS_PATH = os.path.join(os.path.dirname(__file__), "..", "assets") 


class CameraViewer(QWidget):
    """
    Class to create a viewer for the ESP32 camera.
    """

    def __init__(self, camera: ESP32Cam):
        super().__init__()
        self._camera = camera
        self._default_image_shown = True 

        ## << -- Timer Setup -- >>
        self._text_timer = QTimer()
        self._text_timer.timeout.connect(self._update_datetime_fps)
        self._text_timer.start(1000)

        # << -- CameraID Label Setup -- >>
        self._camera_id_label = QLabel(f"ESP32CAM-{camera.id}")
        self._camera_id_label.setStyleSheet("font-size: 14px;")
        self._camera_id_label.setAlignment(Qt.AlignmentFlag.AlignCenter)   

        # << -- DateTime Item Setup -- >>
        self._datetime_item = QGraphicsTextItem()
        self._datetime_item.setDefaultTextColor(Qt.GlobalColor.white)

        # << -- Framerate Item Setup -- >>
        self._framerate_item = QGraphicsTextItem()
        self._framerate_item.setDefaultTextColor(Qt.GlobalColor.white)

        # << -- FrameView Setup -- >>
        self._frame_view = FrameView()
        self.set_default_image()
        self._frame_view.setStyleSheet("FrameView { border: none; }")
        self._frame_view.scene.addItem(self._datetime_item)
        self._frame_view.scene.addItem(self._framerate_item)     

        # << -- Layout Setup -- >>
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._frame_view)
        self._layout.addWidget(self._camera_id_label)


    def _update_datetime(self):
        """Update the datetime text item."""
        if not self._default_image_shown:
            datetime_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self._datetime_item.setPlainText(datetime_str)
            pixmap = self._frame_view.pixmap
            x_datetime = pixmap.rect().width() - self._datetime_item.boundingRect().width() - 5
            y_datetime = pixmap.rect().height() - self._datetime_item.boundingRect().height() - 5
            self._datetime_item.setPos(x_datetime, y_datetime)


    def _update_fps(self):
        """Update the fps text item."""
        if not self._default_image_shown:
            fps = self._camera.framerate
            self._framerate_item.setPlainText(f"FPS: {fps}")
            self._framerate_item.setPos(5, 5)
            

    def _update_datetime_fps(self):
        """Update both datetime and fps text items."""
        self._update_datetime()
        self._update_fps()


    def update_frame(self):
        """Update the frame from the camera."""
        if self._default_image_shown:
            self._default_image_shown = False
  
        frame = self._camera.get_frame()
        if frame is not None:
            image = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format.Format_RGB888)
            self._frame_view.set_image(image)


    def set_default_image(self):
        """Set the default image to be displayed in the FrameView."""
        self._default_image_shown = True
        self._datetime_item.setPlainText("")
        self._framerate_item.setPlainText("")
        image_path = os.path.join(ASSETS_PATH, "no_image.png")
        with open(image_path, "rb") as f:
            image = np.frombuffer(f.read(), np.uint8)
            image = cv2.imdecode(image, cv2.IMREAD_COLOR)
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            image = QImage(image.data, image.shape[1], image.shape[0], QImage.Format.Format_RGB888)
            self._frame_view.set_image(image)


    def delete(self):
        """Delete the camera viewer and all its widgets."""
        self._frame_view.deleteLater()
        self._camera_id_label.deleteLater()
        self.deleteLater()

    def get_scene(self):
        """Get the scene from the FrameView."""
        return self._frame_view.scene
    