from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsRectItem
from PyQt5.QtGui import QPixmap
from lrm.exceptions import lrm_slot, lrm_slot_param
from PyQt5.QtCore import QRectF
from PyQt5.QtCore import Qt
from TabsHandler import Tabs
from PyQt5.QtGui import QBrush, QColor
import json
from Properties import Property
import xml.etree.ElementTree as ET
import xml.dom.minidom as minidom


class ScreenshotHandler(Tabs):
    def __init__(self, parent, json_with_units) -> None:
        self.parent = parent
        self.start = None
        self.temp_rect = None
        self.clicked = {}
        self.property = []
        self.rectangles = {}
        self.rect_item = None
        self.unit_name = ""
        self.json_with_units = json_with_units
        Tabs.__init__(self)

    @lrm_slot_param
    def display_picture(self, file_name, tab_name):
        Tabs.add_tab(self, tab_name)

        # Create a QGraphicsView and QGraphicsScene
        self.graphics_view = QGraphicsView(self.parent.tab_widget.currentWidget())
        self.graphics_scene = QGraphicsScene()
        self.graphics_view.setScene(self.graphics_scene)

        # Load the image file and add it to the scene
        image = QPixmap(file_name)
        
        # Determine the size of the borders of the QTabWidget
        outer_size = self.parent.tab_widget.currentWidget().frameGeometry().size()
        inner_size = self.parent.tab_widget.currentWidget().geometry().size()
        border_size = outer_size - inner_size
        
        # Set the size of the QGraphicsView to match the inner size of the QTabWidget
        self.graphics_view.setFixedSize(inner_size)

        # Set the position of the QGraphicsView to align it with the QTabWidget
        self.graphics_view.move(border_size.width() // 2, border_size.height() // 2)
        
        # Set the size of the image to match the size of the QGraphicsView
        image = image.scaled(inner_size, Qt.KeepAspectRatio)

        # Add the image to the scene and center it
        pixmap_item = self.graphics_scene.addPixmap(image)
        pixmap_item.setPos((self.graphics_view.width() - pixmap_item.boundingRect().width()) / 2,
                           (self.graphics_view.height() - pixmap_item.boundingRect().height()) / 2)
        
        # Set the QGraphicsView to handle mouse events
        self.graphics_view.setMouseTracking(True)
        self.graphics_view.mousePressEvent = self.mousePressEvent
        self.graphics_view.mouseMoveEvent = self.mouseMoveEvent
        self.graphics_view.mouseReleaseEvent = self.mouseReleaseEvent
        self.graphics_view.keyPressEvent = self.keyPressEvent

        # Create a layout and add the QGraphicsView to it
        layout = QVBoxLayout()
        layout.addWidget(self.graphics_view)

        # Set the layout to the tab widget
        self.parent.tab_widget.currentWidget().setLayout(layout)

        # This is only to pass the screenshot file name to the new icon dialog, should be implemented in a better way
        self.screenshot_file_name = file_name
    
    @lrm_slot_param
    def load_existing_rectangles(self, json_file_name):
        with open(json_file_name, "r") as rect:
            rect = json.load(rect)

        rect_items = []
        # rectangles = self.unpack_masks(rect) # This contains maks name and dictionary with offsets.
        offsets = self.unpack_offsets(self.unpack_masks(rect)) # This contains only list of dictionaries with offsets.
        mask_names = [mask_name for mask_name in self.unpack_masks(rect)]

        # Actually draw retnagles on screen
        for rect_size_and_position in offsets:
            rect_item = QGraphicsRectItem(QRectF(rect_size_and_position["OffsetX"], rect_size_and_position["OffsetY"], rect_size_and_position["SizeX"], rect_size_and_position["SizeY"]))
            rect_item.setPen(QtGui.QPen(QtCore.Qt.red))
            self.graphics_scene.addItem(rect_item)
            rect_items.append(rect_item)

        for index, value in enumerate(rect):
            # self.rectangle_interaction_object = RectangleInteraction(rect_items[index], self.parent, self.call_update_save, self.cancel_event)
            self.rectangle_interaction_object = self.rectangle_interaction_object = RectangleInteraction(rect_items[index], self.parent, self.cancel_event, self.unit_name, self.json_with_units, self.screenshot_file_name)
            self.rectangle_interaction_object.feed_existing_data(mask_names[index], value)

            # This adds the actual rectangles to the instance variable.
            if self.current_tab not in self.rectangles:
                self.rectangles[self.current_tab] = []
            self.rectangles[self.current_tab].append(self.rectangle_interaction_object)

    def unpack_masks(self, data: list):
        rectangles = []
        
        def look_for_maks(dictionary):

            for key in dictionary:
                for index, value in enumerate(dictionary[key]):
                    if isinstance(value, dict):
                        for key_2 in value:
                            if key_2 == "Mask":
                                rectangles.append(dictionary[key][index][key_2])
        
        for dictionary in data:
            look_for_maks(dictionary)

        # This contains maks name and dictionary with offsets.
        return rectangles
    
    def unpack_offsets(self, data):
        offsets = []
        for rectangle in data:
            for key in rectangle:
                offsets.append(rectangle[key])
        
        # This contains maks name and dictionary with offsets.
        return offsets

    @lrm_slot_param
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.leftMousePressEvent(event)

    # Handle mouse press event
    @lrm_slot_param
    def leftMousePressEvent(self, event):
        # Get the starting point of the rectangle
        self.start = event.pos()

        if self.current_tab not in self.rectangles:
            return

        for rectangle_interaction in self.rectangles[self.current_tab]:
            if self.rect_item is not None:
                self.rect_item.setBrush(QBrush(QColor(0, 0, 0, 0)))
            self.graphic_rect_item = rectangle_interaction
            self.rect_item = rectangle_interaction.rectangle
            if event.pos() in self.rect_item.rect():
                if self.rect_item not in self.clicked:
                    self.rect_item.setBrush(QBrush(QColor(0, 255, 0, 50)))
                    self.clicked[self.rect_item] = True
                    break
                elif self.clicked[self.rect_item]:
                    rectangle_interaction.make_dialog_visible()
                    self.call_update_save()
                    self.add_star_to_tab_name()
                    self.clicked[self.rect_item] = False
                    self.rect_item.setBrush(QBrush(QColor(0, 0, 0, 0)))
                    break
                elif not self.clicked[self.rect_item]:
                    self.rect_item.setBrush(QBrush(QColor(0, 255, 0, 50)))
                    for val in self.clicked:
                        self.clicked[val] = False
                    self.clicked[self.rect_item] = True
                    break
            else:
                self.rect_item.setBrush(QBrush(QColor(0, 0, 0, 0)))
                if self.rect_item in self.clicked:
                    self.clicked[self.rect_item] = False

    # Handle mouse move event
    @lrm_slot_param
    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            if self.start:
                # Get the current point of the mouse
                end = event.pos()

                # Create the temporary rectangle
                x = min(self.start.x(), end.x())
                y = min(self.start.y(), end.y())
                width = abs(self.start.x() - end.x())
                height = abs(self.start.y() - end.y())
                temp_rect = QGraphicsRectItem(QRectF(x, y, width, height))

                # Remove the old temporary rectangle and add the new one
                if self.temp_rect is not None:
                    self.graphics_scene.removeItem(self.temp_rect)
                self.temp_rect = temp_rect
                self.graphics_scene.addItem(self.temp_rect)

    # Handle mouse release event
    @lrm_slot_param
    def mouseReleaseEvent(self, event):
        if self.temp_rect:
            self.graphics_scene.removeItem(self.temp_rect)
            self.temp_rect = None

            # Get the end point of the rectangle
            end = event.pos()

            # Draw the rectangle on the QGraphicsScene
            rect = QGraphicsRectItem(QRectF(self.start, end))
            rectangle = rect.rect()
            normalized_rect = rectangle.normalized()
            rect.setRect(normalized_rect)
            rect.setPen(QtGui.QPen(QtCore.Qt.red))
            self.graphics_scene.addItem(rect)

            self.rectangle_interaction_object = RectangleInteraction(rect, self.parent, self.cancel_event, self.unit_name, self.json_with_units, self.screenshot_file_name)

            # Initialize property dialog window that will be responsible for adding stuff after the user draws a rectangle
            self.rectangle_interaction_object.make_dialog_visible()

            # This handles the actual rectangles
            if self.current_tab not in self.rectangles:
                self.rectangles[self.current_tab] = []
            self.rectangles[self.current_tab].append(self.rectangle_interaction_object)
            
            # Make the save buttons visible.
            self.call_update_save()

            self.start = None

            # Add star to the end of tab name to display that there has been changes in the screen.
            self.add_star_to_tab_name()

    @lrm_slot
    def add_star_to_tab_name(self):
        # Add star to the end of tab name to display that there has been changes in the screen.
        if "*" != self.parent.tab_widget.tabText(self.current_tab)[-1]:
            tab_name = self.parent.tab_widget.tabText(self.current_tab)
            tab_name += "*"
            self.parent.tab_widget.setTabText(self.current_tab, tab_name)

    # Handle key press event
    @lrm_slot_param
    def keyPressEvent(self, event):
        if self.rect_item not in self.clicked:
            return

        if event.key() == Qt.Key_Backspace:
            if self.clicked[self.rect_item]:
                self.rectangles[self.current_tab].remove(self.graphic_rect_item)
                self.graphics_scene.removeItem(self.rect_item)

                # Make sure to only have the save button visible when there's a rectangle
                self.call_update_save()

                # Delete the rectangle reference from self.clicked
                del self.clicked[self.rect_item]

                # Add star to the end of tab name to display that there has been changes in the screen.
                self.add_star_to_tab_name()
    
    @lrm_slot
    def cancel_event(self):
        if len(self.rectangles[self.current_tab][-1].dialog_window.elements) < 1:
            rectangle_interaction = self.rectangles[self.current_tab].pop()
            self.graphics_scene.removeItem(rectangle_interaction.rectangle)
            if "*" == self.parent.tab_widget.tabText(self.current_tab)[-1]:
                tab_name = self.parent.tab_widget.tabText(self.current_tab)
                tab_name = tab_name.replace("*", "")
                self.parent.tab_widget.setTabText(self.current_tab, tab_name)


class RectangleInteraction:
    def __init__(self, rectangle, parent, cancel_event, unit_name, json_with_units, screenshot_file_name) -> None:
        self.rectangle = rectangle
        self.unit_name = unit_name
        self.json_with_units = json_with_units
        self.dialog_window = Property(parent, self.rectangle, cancel_event, self.mask_to_rectangle, self.save_new_mask_to_xml, screenshot_file_name)

    @lrm_slot
    def mask_to_rectangle(self):
        for index, value in enumerate(self.dialog_window.elements[self.dialog_window.action_key]):
            if isinstance(value, dict) and value.get('Mask'):
                for mask_name, mask_info in value['Mask'].items():
                    offset_x = mask_info.get('OffsetX')
                    offset_y = mask_info.get('OffsetY')
                    size_x = mask_info.get('SizeX')
                    size_y = mask_info.get('SizeY')

                    self.rectangle.setRect(offset_x, offset_y, size_x, size_y)

    @lrm_slot
    def make_dialog_visible(self):
        self.dialog_window.show()
    
    @lrm_slot_param
    def feed_existing_data(self, mask_name, data):
        self.dialog_window.feed_existing_data(list(mask_name.keys())[0], data)

    def save_new_mask_to_xml(self, offsetX, offsetY, sizeX, sizeY, new_mask_name):
        name = new_mask_name
        root = ET.Element("MASK", name=name)
        elements_elem = ET.SubElement(root, "ELEMENTS")
        element_elem = ET.SubElement(elements_elem, "ELEMENT", type="Rectangle", index="0")
        for offset_key, offset_value in zip(("OffsetX", "OffsetY", "SizeX", "SizeY"), (offsetX, offsetY, sizeX, sizeY)):
            ET.SubElement(element_elem, offset_key).text = str(offset_value)
        
        # Create the XML document with encoding declaration
        xml_doc = ET.tostring(root, encoding='utf-8', xml_declaration=True)
        dom = minidom.parseString(xml_doc)
        xml_doc = dom.toprettyxml(indent='\t', encoding='utf-8').decode()
        
        # Write the XML document to a file that will be saved into the path that is specified in the units.json.
        for unit in self.json_with_units:
            if unit == self.unit_name:
                path = self.json_with_units[unit].get("masks")

                with open(f"{path}/{name}.xml", "wb") as f:
                    f.write(xml_doc.encode())
                
                return
