#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 29 21:07:55 2020

@author: carl
"""

import os
import sys
import traceback
import multiprocessing
import signal

from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog
from PyQt5.QtWidgets import QErrorMessage, QMessageBox, QStyleFactory
from PyQt5.QtCore import Qt, QSettings, QLocale
from PyQt5.QtGui import QFontDatabase, QFont, QFontInfo
from PyQt5.Qt import qApp

from .exceptiondialog import ExceptionDialog
from .copyfigure import add_clipboard_to_figures
from .. import octavvs_version



class OctavvsMainWindow(QMainWindow):

    fileOptions = QFileDialog.Options() | QFileDialog.DontUseNativeDialog

    def __init__(self, parent=None):
        super().__init__(parent)
        self.settings = QSettings('MICCS', 'OCTAVVS ' + self.program_name())
        qApp.installEventFilter(self)
        self.setupUi(self)

        self.errorMsg = QErrorMessage(self)
        self.errorMsg.setWindowModality(Qt.WindowModal)

        self.units = 'cm-1'
        self.plot_ltr = True

    def post_setup(self):
        "Called after setting up UI but before loading data etc"
        self.setWindowTitle('OCTAVVS %s %s' % (self.program_name(), octavvs_version))
        ExceptionDialog.install(self)

    @classmethod
    def program_name(cls):
        "Return the name of the program that this main window represents"
        return cls.__name__

    def showDetailedErrorMessage(self, err, details):
        "Show an error dialog with details"
        q = QMessageBox(self)
        q.setIcon(QMessageBox.Critical)
        q.setWindowTitle("Error")
        q.setText(err)
        q.setTextFormat(Qt.PlainText)
        q.setDetailedText(details)
        q.addButton('OK', QMessageBox.AcceptRole)
        return q.exec()

    def loadErrorBox(self, file, err, warning=False):
        "Prepare an error dialog for failure to load a file"
        q = QMessageBox(self)
        if warning:
            q.setIcon(QMessageBox.Information)
            q.setWindowTitle("Warning loading file")
            q.setText("Warning from loading '"+file+"':\n"+err[0])
        else:
            q.setIcon(QMessageBox.Warning)
            q.setWindowTitle("Error loading file")
            q.setText("Failed to load '"+file+"':\n"+err[0])
        q.setTextFormat(Qt.PlainText)
        q.setDetailedText(err[1])
        return q

    def getLoadSaveFileName(self, title, filter=None, settingname=None,
                            savesuffix=None, multiple=False,
                            settingdefault=None,
                            directory=None, defaultfilename=None):
        "Show a file dialog and select one or more files"
        setting = self.settings.value(settingname, settingdefault
                                      ) if settingname is not None else None
        if directory is None:
            directory = setting if type(setting) is str else None
        dialog = QFileDialog(parent=self, caption=title,
                             directory=directory, filter=filter)
        if defaultfilename is not None:
            dialog.selectFile(defaultfilename)
#        if setting and type(setting) is not str:
#            dialog.restoreState(setting)
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        if savesuffix is not None:
            dialog.setAcceptMode(QFileDialog.AcceptSave)
            dialog.setDefaultSuffix(savesuffix)
        elif multiple:
            dialog.setFileMode(QFileDialog.ExistingFiles)
        else:
            dialog.setFileMode(QFileDialog.ExistingFile)
        dialog.exec()
        files = dialog.selectedFiles()
        if not dialog.result() or not files:
            return None
        if settingname is not None:
            self.settings.setValue(settingname, os.path.dirname(files[0]))
        return files if multiple else files[0]

    def getSaveFileName(self, title, **kwargs):
        "Show a file dialog and select an output file"
        return self.getLoadSaveFileName(title=title, **kwargs)

    def getLoadFileName(self, title, **kwargs):
        "Show a file dialog and select an input file"
        return self.getLoadSaveFileName(title=title, **kwargs)

    def getLoadFileNames(self, title, **kwargs):
        "Show a file dialog and select an input file"
        return self.getLoadSaveFileName(title=title, multiple=True, **kwargs)

    def getImageFileName(self, title, **kwargs):
        "Show a file dialog and select a single image file"
        return self.getLoadSaveFileName(title=title,
                filter="Image files (*.jpg *.jpeg *.png *.tif "
                    "*.tiff *.bmp *.gif);;All files (*)",
                **kwargs)

    def getDirectoryName(self, title, settingname=None, savesetting=True,
                         default=None):
        "Show a file dialog and select a directory"
        directory = self.settings.value(settingname, default) if \
            settingname is not None else None
        dialog = QFileDialog(parent=self, caption=title,
                             directory=directory)

        dialog.setAcceptMode(QFileDialog.AcceptSave)
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        dialog.setOption(QFileDialog.ShowDirsOnly, True)
        dialog.exec()
        dirs = dialog.selectedFiles()
        if not dialog.result() or not dirs:
            return None
        if savesetting:
            self.settings.setValue(settingname, dirs[0])
        return dirs[0]

    def updateWavenumberRange(self):
        "For derived classes, when the wn range is updated"
        pass

    def updateDimensions(self, wh):
        "For derived classes, when image width/height is updated"
        pass

    def updateUnitsAndOrder(self, units, ltr):
        "Update the units (cm^-1 or nm) and direction of plots"
        self.units = units
        self.plot_ltr = ltr

    def unitsRichText(self):
        if self.units == 'cm-1':
            return 'cm<sup>-1</sup>'
        return self.units


    @staticmethod
    def choose_font(families):
        db = QFontDatabase()
        for family in families:
            if family in db.families():
                font = QFont(family)
                font.setStyleHint(QFont.SansSerif, QFont.PreferDefault)
                font.setStyleStrategy(
                    QFont.PreferAntialias | QFont.NoFontMerging)
                return font
        return QFont()  # system default

    @classmethod
    def run_octavvs_application(windowclass, parser=None, parameters=[],
                                isChild=False):
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        res = 1
        try:
            progver = f"OCTAVVS {windowclass.program_name()} {octavvs_version}"
            windowparams = {}
            fontsize = 11
            native_style = True
            if parser is not None:
                parser.add_argument('--version', action='version',
                                    version=progver)
                parser.add_argument('--font-size', metavar="pts",
                                    help="font size in points",
                                    default=fontsize,
                                    type=int)
                parser.add_argument('--native-style',
                                    help="don't switch to Fusion QStyle",
                                    action='store_true')
                selmp = multiprocessing.get_start_method(
                    allow_none=True) is None
                if selmp:
                    parser.add_argument(
                        '--mpmethod', help="method used to start child"
                        " processes: 'fork', 'spawn' or 'forkserver'")
                args = parser.parse_args()
                fontsize = args.font_size
                native_style = args.native_style
                windowparams = { k: args.__dict__[k] for k in parameters }
                if selmp and args.mpmethod:
                    multiprocessing.set_start_method(args.mpmethod)

            app = QApplication.instance()
            if not app:
                app = QApplication(sys.argv)
                if not native_style:
                    QLocale.setDefault(QLocale.c())
                    app.setStyle(QStyleFactory.create("Fusion"))
                font = OctavvsMainWindow.choose_font(
                    ["Segoe UI", "Noto Sans", "Arial"])
                font.setPointSize(fontsize)
                app.setFont(font)
                i = QFontInfo(font)
                print(f"OCTAVVS GUI font set to {i.family()}, {i.pointSize()}"
                      f" ({i.pixelSize()} px)")

            add_clipboard_to_figures()
            window = windowclass()
            window.show()
            window.post_setup(**windowparams)
            if not isChild:
                qApp.lastWindowClosed.connect(qApp.quit);
                res = app.exec_()

        except Exception:
            traceback.print_exc()
            print('Press some key to quit')
            input()
        if not isChild :
            sys.exit(res)

