# -*- coding: utf-8 -*-
"""
gridview.py -- A small python app to display ROIs and edit labels driven by FathomNet annotations
Copyright 2020  Monterey Bay Aquarium Research Institute
Distributed under MIT license. See license.txt for more information.

The app loads images from a directory and then sorts them by label, time, or height. Based on the max
image size and the scree width, it divides the layout into columns and rows and then
fills these with RectWidgets. Each rect widget controls an ROI and annotation and
new annotations can be applied to groups of selected widgets

The app also provides a view similar to rectlabel, where existing annotations can be dragged to adjust and
new annotations can be added.

This was inspired from a PyQt4 GraphicsGridLayout example found here:
http://synapses.awardspace.info/pages-scripts/python/pages/python-pyqt_qgraphicsview-thumbnails-grid.py.html



"""

import os
import cv2
import sys
import qdarkstyle
import pyqtgraph as pg
from style import breeze_resources
from config import settings
from libs.image_mosaic import ImageMosaic
from libs.rectlabel import RectLabel
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from libs.annotation import prettify
from libs import vars

# Define main window class from template
path = os.path.dirname(os.path.abspath(__file__))
uiFile = os.path.join(path, 'gridview.ui')
WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile)


class MainWindow(TemplateBaseClass):

    settings = QtCore.QSettings(os.path.join("config", "gui.ini"), QtCore.QSettings.IniFormat)

    def __init__(self, data_path):
        QtWidgets.QMainWindow.__init__(self)

        TemplateBaseClass.__init__(self)
        self.setWindowTitle('GridView - Python - Qt')

        self.data_path = data_path

        # Create the main window
        self.ui = WindowTemplate()
        self.ui.setupUi(self)

        # restore window size and splitters
        self.gui_restore()

        # hold labels to apply to boxes
        self.all_labels = []

        # last selected ROI
        self.last_selected_roi = None

        # signals and slots
        self.ui.needsReviewButton.clicked.connect(self.move_to_review)
        self.ui.discardButton.clicked.connect(self.move_to_discard)
        self.ui.moveBack.clicked.connect(self.move_back)
        self.ui.clearSelections.clicked.connect(self.clear_selected)
        self.ui.labelSelectedButton.clicked.connect(self.update_labels)
        self.ui.zoomSpinBox.valueChanged.connect(self.update_zoom)
        self.ui.sortMethod.currentTextChanged.connect(self.update_layout)
        self.ui.hideLabeled.stateChanged.connect(self.update_layout)
        self.ui.hideToReview.stateChanged.connect(self.update_layout)
        self.ui.hideDiscarded.stateChanged.connect(self.update_layout)
        self.ui.styleComboBox.currentTextChanged.connect(self.set_gui_style)

        # The image mosaic holds all of the thumbnails as a grid of RectWidgets
        self.image_mosaic = ImageMosaic(self.ui.roiGraphicsView, data_path, self.rect_click,
                                        self.ui.zoomSpinBox.value()/100)

        self.image_mosaic.hide_discarded = self.ui.hideDiscarded.isChecked()
        self.image_mosaic.hide_to_review = self.ui.hideToReview.isChecked()
        self.image_mosaic.hide_labeled = self.ui.hideLabeled.isChecked()

        # rendering the mosaic will load images and annotations and populate the mosaic
        self.image_mosaic.render_mosaic()

        # Sow some stats about the images and annotations
        self.statusBar().showMessage('Loaded ' + str(len(self.image_mosaic.annotations)) +
                                     ' annotations and ' + str(len(self.image_mosaic.thumbs)) + ' rois.')

        # load the label names from labels.txt
        self.get_labels()

        # create the rectlabel widget
        self.rectlabel = RectLabel(self.ui.roiDetailGraphicsView, all_labels=self.all_labels)
        self.rectlabel.set_label(self.ui.labelComboBox.currentText())

        # connect set_label slot with text changed signal from labels
        self.ui.labelComboBox.currentTextChanged.connect(self.rectlabel.set_label)

        # set the gui style
        self.set_gui_style()

    def get_labels(self):
        # setup the combobox with labels loaded from a file
        """with open('labels.txt') as f:
            for line in f.readlines():
                if len(line) > 0:
                    self.all_labels.append(line.strip('\n\t\r'))
            self.all_labels = sorted(self.all_labels)
            self.ui.labelComboBox.clear()
            self.ui.labelComboBox.addItems(self.all_labels)
        """
        self.ui.labelComboBox.clear()
        self.ui.labelComboBox.addItems(sorted(vars.pull_all_concepts()))
        self.ui.labelComboBox.completer().setCompletionMode(QtWidgets.QCompleter.PopupCompletion)

    def gui_restore(self):
        finfo = QtCore.QFileInfo(self.settings.fileName())
        if finfo.exists() and finfo.isFile():
            self.restoreGeometry(self.settings.value("geometry"))
            self.restoreState(self.settings.value("windowState"))
            self.ui.splitter1.restoreState(self.settings.value("splitter1state"))
            self.ui.splitter2.restoreState(self.settings.value("splitter2state"))

    def gui_save(self):
        self.settings.setValue('geometry', self.saveGeometry())
        self.settings.setValue('windowState', self.saveState())
        self.settings.setValue('splitter1state', self.ui.splitter1.saveState())
        self.settings.setValue('splitter2state', self.ui.splitter2.saveState())

    def update_labels(self):
        label = self.ui.labelComboBox.currentText()
        self.image_mosaic.apply_label(label)
        self.rectlabel.apply_label(label)

    def move_to_review(self):
        self.image_mosaic.move_selected(self.data_path, settings.REVIEW_SUBDIR, review=True, discard=False)

    def move_to_discard(self):
        self.image_mosaic.move_selected(self.data_path, settings.DISCARD_SUBDIR, review=False, discard=True)

    def move_back(self):
        self.image_mosaic.move_selected(os.path.join(self.data_path, settings.DISCARD_SUBDIR), self.data_path, review=False, discard=False)
        self.image_mosaic.move_selected(os.path.join(self.data_path, settings.REVIEW_SUBDIR), self.data_path, review=False, discard=False)

    def clear_selected(self):
        self.image_mosaic.clear_selected()

    def update_layout(self):
        method = self.ui.sortMethod.currentText()
        self.image_mosaic.hide_discarded = self.ui.hideDiscarded.isChecked()
        self.image_mosaic.hide_to_review = self.ui.hideToReview.isChecked()
        self.image_mosaic.hide_labeled = self.ui.hideLabeled.isChecked()
        self.image_mosaic.render_mosaic(sort_key=method)


    def update_zoom(self, zoom):
        self.image_mosaic.update_zoom(zoom/100)

    def rect_click(self, rect):

        # remove highlight from the last selected ROI
        if self.last_selected_roi is not None:
            self.last_selected_roi.isLastSelected = False
            self.last_selected_roi.update()

        # update the new selection
        rect.isLastSelected = True
        rect.update()
        self.last_selected_roi = rect


        if len(self.rectlabel.rois) > 0:
            self.rectlabel.save_all()
        self.rectlabel.clear()
        full_img = rect.getFullImage()
        if full_img is None:
            print('No Image Found')
            return
        self.rectlabel.roiDetail.setImage(cv2.cvtColor(rect.getFullImage(), cv2.COLOR_BGR2RGB))
        self.rectlabel.add_annotation(rect.annotation_object_index,  rect)
        self.ui.annotationXML.clear()
        self.ui.annotationXML.insertPlainText(prettify(rect.annotation.root))

    def closeEvent(self, event):
        self.gui_save()
        QtWidgets.QMainWindow.closeEvent(self, event)

    def set_gui_style(self):
        # setup stylesheet
        # set the environment variable to use a specific wrapper
        # it can be set to PyQt, PyQt5, PySide or PySide2 (not implemented yet)
        if self.ui.styleComboBox.currentText().lower() == 'darkstyle':
            os.environ['PYQTGRAPH_QT_LIB'] = 'PyQt5'
            app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api=os.environ['PYQTGRAPH_QT_LIB']))
        elif self.ui.styleComboBox.currentText().lower() == 'darkbreeze':
            file = QtCore.QFile(":/dark.qss")
            file.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text)
            stream = QtCore.QTextStream(file)
            app.setStyleSheet(stream.readAll())
        elif self.ui.styleComboBox.currentText().lower() == 'default':
            app.setStyleSheet("")

if __name__ == "__main__":

    # create the Qt application
    app = QtWidgets.QApplication(sys.argv)
    app.setApplicationName("GridView")

    # check for input arguments
    if len(sys.argv) < 2:
        print('Please supply a data directory as the first argument')
        print('Usage: python gridview.py [path]')
        exit()

    # create the main window
    main = MainWindow(sys.argv[1])
    main.show()

    # exit the app after the window is closed
    sys.exit(app.exec_())
