# This Python file uses the following encoding: utf-8
import os
from pathlib import Path
import sys

from PyQt5.QtCore import QPointF, Qt, QRectF, QSizeF, QT_VERSION_STR, QSize
from PyQt5.QtGui import QPainter, QColor, QImage, QPixmap, QWindow, QIcon
from PyQt5.QtWidgets import QApplication, QGraphicsView, QMainWindow, QGraphicsPixmapItem, QGraphicsScene
from PyQt5.QtWidgets import QGraphicsEllipseItem, QGraphicsSimpleTextItem
# from PyQt5.QtWidgets import QPushButton, QLabel, QSlider, QFileDialog
# from PyQt5.QtWidgets import QWidget, QGroupBox, QLineEdit, QSizePolicy
# from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QGridLayout
# from PyQt5.QtWidgets import QListWidgetItem
# from PyQt5.QtCore import QObject, pyqtSignal

# 包含其他目录中的模块
from pathlib import Path

sys.path.append(str(Path(__file__).absolute().parent.parent))

# 自定义Scene场景
from CustomScene import CustomScene
from CustomItem import CustomItem, ShapeType
from CustomRuler import CustomRuler

# 定义图片处理软件功能列表
from enum import Enum


class Operation(Enum):
    Operation_DEFAULT = 0
    Operation_WINDOW_GRID = 1
    Operation_IMAGE_MOVE = 2
    Operation_IMAGE_ZOOM = 3
    Operation_IMAGE_WL = 4
    Operation_IMAGE_ROTATE = 5
    Operation_IMAGE_RESTORE = 6
    Operation_IMAGE_PAGE = 7
    Operation_IMAGE_DISPLAY = 8
    Operation_IMAGE_SHOT = 9
    Operation_IMAGE_EXPORT = 10
    Operation_IMAGE_DRAW = 11


class CustomView(QGraphicsView):
    """
    CustomView 是针对 QGraphicsView的继承，主要重写鼠标事件
    """
    init_custom_item = False
    def __init__(self, *args, **kwargs):
        image = kwargs.pop('image', None)
        background = kwargs.pop('background', None)
        super(CustomView, self).__init__(*args, **kwargs)

        # 设置QGraphicsView基础属性
        self.setCursor(Qt.OpenHandCursor)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform)
        self.setCacheMode(self.CacheBackground)
        self.setViewportUpdateMode(self.SmartViewportUpdate)

        # 设置背景
        self.set_background(background)
        # 在View中增加一个 CustomScene， 响应一些键盘按键，例如del删除， 选中Item等
        self.m_scene = CustomScene()
        self.setScene(self.m_scene)

        # scene中存在一张图，QGraphicsPixmapItem，用于图片分析
        self.m_image_item = QGraphicsPixmapItem()  # 放置图像
        self.m_image_item.setFlags(QGraphicsPixmapItem.ItemIsFocusable | QGraphicsPixmapItem.ItemIsMovable)

        # 移动鼠标事件，默认是要先press，下面Flag设置可以在mouseMove中获取坐标，实际应该是hover中获取坐标
        # self.m_image_item.setAcceptHoverEvents(True)

        # 将Pixmap放入Scene
        self.m_scene.addItem(self.m_image_item)

        # 预留比例尺
        # self.m_ruler = CustomRuler()
        # self.m_scene.addItem(self.m_ruler)

        # 获取可用区域，将尺寸设置为 rect的 2/3
        rect = QApplication.instance().desktop().availableGeometry(self)
        self.resize(int(rect.width() * 2 / 3), int(rect.height() * 2 / 3))

        # 图片相关， 设置图片到场景中
        self.m_pixmap = None
        self.m_pixmap_original = None
        self.m_original_flag = True

        # 可以传入三种参数, 路径、QPixmap、 QImage
        # 直接从构造函数中传入的image参数
        self.set_pixmap(image)

        # 图片缩放比例
        self.m_scale_delta = 0.1  # 缩放

        # 记录旋转的角度，用于复位QGraphicsView的旋转
        self.m_rotate_value = 0

        # 默认无动作
        self.m_draw_enable = False
        self.m_operation = Operation.Operation_DEFAULT
        self.m_start_draw = False
        self.m_shape_type = ShapeType.ShapeType_Null
        self.m_point_cnt = 0

        # 提示标签
        self.m_note_label_lt = None
        self.m_note_label_rt = None
        self.m_note_label_lb = None
        self.m_note_label_rb = None

        # item列表
        self.m_custom_item_list = []
        self.m_custom_item_current = None
    """
        ####################################自定义函数############################################
        自定义函数通过下划线连接
    """
    def set_operation(self, operation, shape):
        """
        设置操作类型，通过在ImageProcess中点击对应的按钮
        :param operation:
        :param shape:
        :return:
        """
        self.m_operation = operation
        self.m_shape_type = shape

    def add_custom_item(self, item):
        """
        将在widget中创建的CustomItem加到列表
        :param item:
        :return:
        """
        self.m_custom_item_list.append(item)
        self.m_custom_item_current = item

    def get_custom_item(self, index):
        """
        返回CustomItem
        :return:
        """
        if (index < len(self.m_custom_item_list)) and (0 <= index):
            return self.m_custom_item_list[index]
        else:
            return None

    def set_note_labels(self, label1, label2, label3, label4):
        """
        设置四个角的标签显示图片信息
        :param label1:
        :param label2:
        :param label3:
        :param label4:
        :return:
        """
        self.m_note_label_lt = label1
        self.m_note_label_rt = label2
        self.m_note_label_lb = label3
        self.m_note_label_rb = label4

    def set_background(self, color):
        """设置背景颜色
        :param color: 背景颜色
        :type color: QColor or str or GlobalColor
        """
        if isinstance(color, QColor):
            self.setBackgroundBrush(color)
        elif isinstance(color, (str, Qt.GlobalColor)):
            color = QColor(color)
            if color.isValid():
                self.setBackgroundBrush(color)

    def set_pixmap(self, pixmap, fit=True):
        """加载图片
        :param pixmap: 图片或者图片路径
        :param fit: 是否适应
        :type pixmap: QPixmap or QImage or str
        :type fit: bool
        """
        if isinstance(pixmap, QPixmap):
            self.m_pixmap = pixmap
        elif isinstance(pixmap, QImage):
            self.m_pixmap = QPixmap.fromImage(pixmap)
        elif isinstance(pixmap, str) and os.path.isfile(pixmap):
            self.m_pixmap = QPixmap(pixmap)
        else:
            return
        if self.m_original_flag:
            self.m_pixmap_original = self.m_pixmap.copy()
            self.m_original_flag = False

        self.m_image_item.setPixmap(self.m_pixmap)
        self.m_image_item.update()

        # 设置view的场景范围
        self.set_scene_boundary()

        # 将场景缩放到 view 合适填充位置
        if fit:
            self.fit_in_view(QRectF(self.m_image_item.pos(), QSizeF(self.m_pixmap.size())), Qt.KeepAspectRatio)
        self.update()

    def set_scene_boundary(self):
        """
        设置view视域
        :return:
        """
        if not self.m_pixmap:
            return
        self.setSceneRect(QRectF(QPointF(0, 0), QPointF(self.m_pixmap.width(), self.m_pixmap.height())))

    def fit_in_view(self, rect, flags=Qt.KeepAspectRatioByExpanding):
        """居中适应
        :param rect: 矩形范围
        :param flags:
        :return:
        """
        if not self.scene() or rect.isNull():
            return
        unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
        self.scale(1 / unity.width(), 1 / unity.height())
        view_rect = self.viewport().rect()
        scene_rect = self.transform().mapRect(rect)
        x_ratio = view_rect.width() / scene_rect.width()
        y_ratio = view_rect.height() / scene_rect.height()
        if flags == Qt.KeepAspectRatio:
            x_ratio = y_ratio = min(x_ratio, y_ratio)
        elif flags == Qt.KeepAspectRatioByExpanding:
            x_ratio = y_ratio = max(x_ratio, y_ratio)
        self.scale(x_ratio, y_ratio)
        self.centerOn(rect.center())

    def zoom_in(self):
        """放大"""
        self.zoom(1 + self.m_scale_delta)

    def zoom_out(self):
        """缩小"""
        self.zoom(1 - self.m_scale_delta)

    def zoom(self, factor):
        """缩放
        :param factor: 缩放的比例因子
        """
        _factor = self.transform().scale(
            factor, factor).mapRect(QRectF(0, 0, 1, 1)).width()
        if _factor < 0.07 or _factor > 100:
            # 防止过大过小
            return
        self.scale(factor, factor)

    def rotate_clockwise(self):
        """
        顺时针90旋转
        :return:
        """
        self.rotate(90)
        self.m_rotate_value = self.m_rotate_value + 90

    def rotate_anti_clockwise(self):
        """缩小"""
        self.rotate(-90)
        self.m_rotate_value = self.m_rotate_value - 90

    """
        ####################################基类函数 重载############################################
    """
    def wheelEvent(self, event):
        """
        通过滑轮缩放图片
        :param event:
        :return:
        """
        if event.angleDelta().y() > 0:
            self.zoom_in()
        else:
            self.zoom_out()

        super(CustomView, self).wheelEvent(event)



    def mousePressEvent(self, event):
        if event.buttons() == Qt.MouseButton.LeftButton:

            # 如果点击的是图片，则将其他所有item的选择状态清除
            if self.itemAt(event.pos()) == self.m_image_item:
                self.m_draw_enable = True
                for item in self.m_scene.items():
                    # 过滤掉图片
                    if self.m_image_item != item:
                        # 清除图形项drag flag
                        item.m_drag_flag = False
            else:
                self.m_draw_enable = False

            if self.m_draw_enable:
                # 只有鼠标点击最底层的Image才开始绘图，有其他Item时候不绘制
                if self.m_operation == Operation.Operation_IMAGE_DRAW:
                    print("Operation_IMAGE_DRAW")
                    self.m_image_item.setFlag(QGraphicsPixmapItem.ItemIsMovable, False)
                    self.m_start_draw = True

                    # init_custom_item 保证只初始化一个图形项，合适打开跟具体图型相关
                    if not CustomView.init_custom_item:
                        # 创建一个CustomItem， 并开启绘制使能
                        self.m_custom_item_current = CustomItem(w=3072, h=3072, shape=self.m_shape_type)
                        self.m_custom_item_current.m_draw_enable = True
                        self.m_custom_item_current.m_current_index = 0
                        # 将Item加入到view 的list中
                        self.add_custom_item(self.m_custom_item_current)
                        CustomView.init_custom_item = True

                    # 这三类图形只需要两个点位
                    if self.m_shape_type in [ShapeType.ShapeType_Line, ShapeType.ShapeType_Rectangle,
                                                 ShapeType.ShapeType_Ellipse]:
                        # 将 point加入到列表中
                        self.m_custom_item_current.update_point_list(self.m_point_cnt, self.mapToScene(event.pos()))
                        self.m_scene.addItem(self.m_custom_item_current)
                        self.m_point_cnt = self.m_point_cnt + 1
                    if self.m_shape_type in [ShapeType.ShapeType_Angle]:
                        # 角度的坐标点需要在Release中增加，Move中修改
                        print("Angel start")
                    if self.m_shape_type in [ShapeType.ShapeType_Polygon]:
                        # 多边形的坐标点需要在Release中增加，Move中修改，然后双击结束绘制
                        print("Angel start")

                    self.m_scene.update()

                elif self.m_operation == Operation.Operation_WINDOW_GRID:
                    print("Operation_WINDOW_GRID")
                elif self.m_operation == Operation.Operation_IMAGE_MOVE:
                    print("Operation_IMAGE_MOVE")
                elif self.m_operation == Operation.Operation_IMAGE_ZOOM:
                    print("Operation_IMAGE_ZOOM")
                elif self.m_operation == Operation.Operation_IMAGE_WL:
                    print("Operation_IMAGE_WL")
                elif self.m_operation == Operation.Operation_IMAGE_ROTATE:
                    print("Operation_IMAGE_ROTATE")
                elif self.m_operation == Operation.Operation_IMAGE_RESTORE:
                    print("Operation_IMAGE_RESTORE")
                elif self.m_operation == Operation.Operation_IMAGE_DISPLAY:
                    print("Operation_IMAGE_DISPLAY")
                elif self.m_operation == Operation.Operation_IMAGE_SHOT:
                    print("Operation_IMAGE_SHOT")
                elif self.m_operation == Operation.Operation_IMAGE_EXPORT:
                    print("Operation_IMAGE_EXPORT")
                else:
                    print("Operation_Default")
        elif event.buttons() == Qt.MouseButton.RightButton:
            if self.m_shape_type in [ShapeType.ShapeType_Polygon]:
                self.m_point_cnt = 0
                self.m_custom_item_current.m_current_index = 0
                self.m_start_draw = False
                # 打开可以继续绘制
                CustomView.init_custom_item = False

        event.accept()
        super(CustomView, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self.m_draw_enable:
            if self.m_operation == Operation.Operation_IMAGE_DRAW:
                print("Operation_IMAGE_MOVE")
                if self.m_start_draw:
                    # 将 point加入到列表中
                    self.m_custom_item_current.update_point_list(self.m_point_cnt, self.mapToScene(event.pos()))
                    self.m_custom_item_current.m_current_index = self.m_point_cnt
                    self.m_scene.update()

            elif self.m_operation == Operation.Operation_WINDOW_GRID:
                print("Operation_WINDOW_GRID")
            elif self.m_operation == Operation.Operation_IMAGE_MOVE:
                print("Operation_IMAGE_MOVE")
            elif self.m_operation == Operation.Operation_IMAGE_ZOOM:
                print("Operation_IMAGE_ZOOM")
            elif self.m_operation == Operation.Operation_IMAGE_WL:
                print("Operation_IMAGE_WL")
            elif self.m_operation == Operation.Operation_IMAGE_ROTATE:
                print("Operation_IMAGE_ROTATE")
            elif self.m_operation == Operation.Operation_IMAGE_RESTORE:
                print("Operation_IMAGE_RESTORE")
            elif self.m_operation == Operation.Operation_IMAGE_DISPLAY:
                print("Operation_IMAGE_DISPLAY")
            elif self.m_operation == Operation.Operation_IMAGE_SHOT:
                print("Operation_IMAGE_SHOT")
            elif self.m_operation == Operation.Operation_IMAGE_EXPORT:
                print("Operation_IMAGE_EXPORT")
            else:
                print("Operation_Default")

            # 实时显示场景坐标
            if None != self.m_note_label_lt:
                self.m_note_label_lt.setText("X: %d Y: %d Value: 6666" % (
                    int(self.mapToScene(event.pos()).x()), int(self.mapToScene(event.pos()).y())))
                self.m_note_label_rt.setText("X: %d Y: %d Value: 6666" % (
                    int(self.mapToScene(event.pos()).x()), int(self.mapToScene(event.pos()).y())))
                self.m_note_label_lb.setText("X: %d Y: %d Value: 6666" % (
                    int(self.mapToScene(event.pos()).x()), int(self.mapToScene(event.pos()).y())))
                self.m_note_label_rb.setText("X: %d Y: %d Value: 6666" % (
                    int(self.mapToScene(event.pos()).x()), int(self.mapToScene(event.pos()).y())))

        event.accept()
        super(CustomView, self).mouseMoveEvent(event)
        self.update()

    def mouseReleaseEvent(self, event):
        if self.m_draw_enable:
            if self.m_operation == Operation.Operation_IMAGE_DRAW:

                # 这三类图形只需要两个点位
                if self.m_shape_type in [ShapeType.ShapeType_Line, ShapeType.ShapeType_Rectangle,
                                         ShapeType.ShapeType_Ellipse]:
                    print("2 points")
                    self.m_custom_item_current.update_point_list(self.m_point_cnt, self.mapToScene(event.pos()))
                    self.m_point_cnt = 0
                    self.m_custom_item_current.m_current_index = 0
                    self.m_start_draw = False
                    # 打开可以继续绘制
                    CustomView.init_custom_item = False

                if self.m_shape_type in [ShapeType.ShapeType_Angle]:
                    # 将 point加入到列表中
                    self.m_custom_item_current.update_point_list(self.m_point_cnt, self.mapToScene(event.pos()))
                    self.m_scene.addItem(self.m_custom_item_current)
                    self.m_point_cnt = self.m_point_cnt + 1

                    # 三个点结束
                    if self.m_point_cnt == 3:
                        self.m_point_cnt = 0
                        self.m_custom_item_current.m_current_index = 0
                        self.m_start_draw = False
                        # 打开可以继续绘制
                        CustomView.init_custom_item = False

                if self.m_shape_type in [ShapeType.ShapeType_Polygon]:
                    # 多边形的坐标点需要在Release中增加，Move中修改，然后双击结束绘制
                    # 将 point加入到列表中
                    self.m_custom_item_current.update_point_list(self.m_point_cnt, self.mapToScene(event.pos()))
                    self.m_scene.addItem(self.m_custom_item_current)
                    self.m_point_cnt = self.m_point_cnt + 1

                # 打开Image可以移动
                self.m_image_item.setFlag(QGraphicsPixmapItem.ItemIsMovable, True)

        event.accept()
        super(CustomView, self).mouseReleaseEvent(event)

