from PySide6.QtCore import QEvent, Qt, Signal
from PySide6.QtGui import QBrush, QPen
from PySide6.QtWidgets import (
    QDialog,
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QMessageBox,
    QToolTip,
)


class BuildItem(QGraphicsRectItem):
    WIDTH = 30

    def __init__(self, future_build_info, x=0, y=0, selectable=True):
        QGraphicsRectItem.__init__(self, x, y, self.WIDTH, self.WIDTH)
        self.future_build_info = future_build_info
        if selectable:
            self.setFlags(BuildItem.ItemIsSelectable | BuildItem.ItemIsFocusable)
        else:
            self.setFlags(BuildItem.ItemIsFocusable)

    def __str__(self):
        return "Build %s" % self.future_build_info.data


class SkipChooserScene(QGraphicsScene):
    COLUMNS = 7
    SPACE = 10

    def __init__(self, build_range):
        QGraphicsScene.__init__(self)
        self.from_range(build_range)

    def from_range(self, build_range):
        self.build_range = build_range
        mid = build_range.mid_point()
        bounds = (0, len(build_range) - 1)
        row = -1
        for i, future in enumerate(build_range.future_build_infos):
            column = i % self.COLUMNS
            if column == 0:
                row += 1
            item = BuildItem(
                future,
                column * BuildItem.WIDTH + self.SPACE * column,
                row * BuildItem.WIDTH + self.SPACE * row,
                selectable=i not in bounds,
            )
            if i == mid:
                item.setBrush(QBrush(Qt.blue))
                self.mid_build = item
            elif i in bounds:
                item.setBrush(QBrush(Qt.lightGray))

            pen = QPen(self.palette().windowText().color())
            item.setPen(pen)
            self.addItem(item)

    def _update_palette(self):
        for item in self.items():
            pen = QPen(self.palette().windowText().color())
            item.setPen(pen)

    def event(self, event: QEvent) -> bool:
        if event.type() == QEvent.PaletteChange:
            self._update_palette()
            return True
        return super().event(event)


class SkipChooserView(QGraphicsView):
    build_choosen = Signal()

    def __init__(self, parent=None):
        QGraphicsView.__init__(self, parent)
        self.setMouseTracking(True)

    def setScene(self, scene):
        QGraphicsView.setScene(self, scene)
        self.centerOn(scene.mid_build)
        scene.mid_build.setSelected(True)

    def mousePressEvent(self, evt):
        item = self.itemAt(evt.pos())
        # do nothing if we don't click on an item
        if not item:
            return
        # do nothing if we click on a bound
        if not (item.flags() & QGraphicsRectItem.ItemIsSelectable):
            return
        # only one item can be selected at a time, so deselect if any
        for item in self.scene().selectedItems():
            item.setSelected(False)
        QGraphicsView.mousePressEvent(self, evt)

    def mouseMoveEvent(self, evt):
        QGraphicsView.mouseMoveEvent(self, evt)
        # implement a real time tooltip
        item = self.itemAt(evt.pos())
        if item:
            QToolTip.showText(evt.globalPos(), str(item))

    def mouseDoubleClickEvent(self, evt):
        item = self.itemAt(evt.pos())
        # do nothing if we click on a bound
        if item and item.flags() & QGraphicsRectItem.ItemIsSelectable:
            self.build_choosen.emit()


class SkipDialog(QDialog):
    def __init__(self, build_range, parent=None):
        QDialog.__init__(self, parent)
        assert len(build_range) > 3
        from mozregui.ui.skip_dialog import Ui_SkipDialog

        self.ui = Ui_SkipDialog()
        self.ui.setupUi(self)
        self.scene = SkipChooserScene(build_range)
        self.scene.selectionChanged.connect(self.on_selection_changed)
        self.ui.gview.setScene(self.scene)
        self.ui.gview.build_choosen.connect(self.accept)

    def on_selection_changed(self):
        items = self.scene.selectedItems()
        if items:
            self.ui.lbl_status.setText("Selected build to test, %s" % items[0])

    def build_index(self, item):
        return self.scene.build_range.future_build_infos.index(item.future_build_info)

    def choose_next_build(self):
        if self.exec() == SkipDialog.Accepted:
            items = self.scene.selectedItems()
            assert len(items) == 1
            return self.build_index(items[0])

    def closeEvent(self, evt):
        if (
            QMessageBox.warning(
                self,
                "Stop the bisection ?",
                "Closing this dialog will end the bisection. Are you sure"
                " you want to end the bisection now ?",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No,
            )
            == QMessageBox.Yes
        ):
            evt.accept()
        else:
            evt.ignore()


if __name__ == "__main__":
    from mozregression.build_range import BuildRange, FutureBuildInfo

    class FInfo(FutureBuildInfo):
        def _fetch(self):
            return self.data

    build_range = BuildRange(None, [FInfo(None, i) for i in range(420)])

    from PySide6.QtWidgets import QApplication, QMainWindow

    app = QApplication([])
    win = QMainWindow()

    dlg = SkipDialog(build_range)
