#!/usr/bin/python

#
# Mnemosyne <Peter.Bienstman@gmail.com>
#

import os
import sys
import time

# Workaround for this bug:
#    https://github.com/pyinstaller/pyinstaller/issues/1113
import encodings.idna

# Custom error handling on Windows.

if sys.platform == "win32":

    import ctypes

    class Stderr(object):

        # Note: it would be nice to queue the messages and display them
        # using atexit, but but atexit does not seem to run.

        def write(self, text):
            if text.strip():
                MessageBox = ctypes.windll.user32.MessageBoxW
                MessageBox(None, text, "Mnemosyne", 0)

        def flush(self):
            pass

    sys.stderr = Stderr()

from optparse import OptionParser

from mnemosyne.libmnemosyne import Mnemosyne
from mnemosyne.libmnemosyne.utils import MnemosyneError

# Parse options.
parser = OptionParser()
parser.usage = "%prog [<database_file>]"
parser.add_option("-d", "--datadir", dest="data_dir", type="string",
                  help="data directory", default=None)
parser.add_option("--no-upgrades", dest="automatic_upgrades",
    action="store_false", help="do not upgrade automatically", default=True)
parser.add_option("--debug", dest="debug_file",
    help="log debug information to FILE", metavar="FILE", default=None)
parser.add_option("--web-server", dest="web_server",
    action="store_true", help="run web server without GUI", default=False)
parser.add_option("--sync-server", dest="sync_server",
    action="store_true", help="run sync server without GUI", default=False)
parser.add_option("--qt-scale-factor", dest="qt_scale_factor", type="string",
    action="store", default=None)
parser.add_option("--qt-opengl", dest="qt_opengl", type="string", action="store",
    help="OpenGL implementation to be used by Qt ('desktop', 'angle', 'software')",
    default=None)
parser.add_option("--version", dest="version",
    action="store_true", help="show version information", default=False)
(options, args) = parser.parse_args()

if options.version == True:
    from mnemosyne.version import version
    print(("Mnemosyne " + version).rstrip())
    sys.exit(0)

# Check if we have to override the data_dir determined in libmnemosyne,
# either because we explicitly specified a data_dir on the command line,
# or because there is a mnemosyne directory present in the current directory.
# The latter is handy when Mnemosyne is run from a USB key, so that there
# is no need to refer to a drive letter which can change from computer to
# computer.
data_dir = None
if options.data_dir != None:
    data_dir = os.path.abspath(options.data_dir)
elif os.path.exists(os.path.join(os.getcwd(), "mnemosyne")) and \
    os.path.isdir(os.path.join(os.getcwd(), "mnemosyne")):
    data_dir = os.path.abspath(os.path.join(os.getcwd(), "mnemosyne"))

# Filename argument.
if len(args) > 0:
    filename = os.path.abspath(args[0])
else:
    filename = None

# Load the Mnemosyne library.
mnemosyne = Mnemosyne(upload_science_logs=None, interested_in_old_reps=True)

# Run as server.
if options.web_server == True or options.sync_server == True:
    # Initialise Mnemosyne.
    mnemosyne.components.insert(0,
        ("mnemosyne.libmnemosyne.gui_translators.no_gui_translator",
         "NoGuiTranslator"))
    mnemosyne.components.append(\
        ("mnemosyne.libmnemosyne.ui_components.main_widget",
         "MainWidget"))
    mnemosyne.initialise(data_dir=data_dir, filename=filename,
        automatic_upgrades=options.automatic_upgrades,
        debug_file=options.debug_file, server_only=True)
    mnemosyne.config().server_only = True
    # Sync server.
    if options.sync_server == True:
        from mnemosyne.libmnemosyne.sync_server import SyncServerThread
        sync_server_thread = SyncServerThread(mnemosyne.component_manager)
        sync_server_thread.daemon = True
        sync_server_thread.start()
    # Web server.
    if options.web_server == True:
        from mnemosyne.web_server.web_server import WebServerThread
        web_server_thread = WebServerThread(mnemosyne.component_manager)
        web_server_thread.daemon = True
        web_server_thread.start()
    # Heartbeat.
    from threading import Timer
    def heartbeat():
        # If a sync request comes in while this is running, it will
        # fail because of threading issues.
        try:
            mnemosyne.database().load(mnemosyne.config()["last_database"])
            mnemosyne.controller().heartbeat()
            mnemosyne.database().unload()
        except MnemosyneError:
            pass
        heartbeat_thread = Timer(60 * 60, heartbeat)
        heartbeat_thread.daemon = True
        heartbeat_thread.start()
    Timer(1, heartbeat).start()
    # Hack to allow killing the servers quickly during development.
    try:
        while True:
            time.sleep(0.1)
    except KeyboardInterrupt:
        pass
    sys.exit(0)

# Initialise GUI toolkit.

os.environ["QT_SCALE_FACTOR_ROUNDING_POLICY"] = "PassThrough"
if options.qt_scale_factor:
    os.environ["QT_SCALE_FACTOR"] = options.qt_scale_factor

from PyQt6 import QtWebEngineWidgets # Needs to happen first.
from PyQt6 import QtCore, QtGui
from PyQt6.QtWidgets import QApplication, QStyleFactory

if options.qt_opengl:
    if options.qt_opengl not in ["desktop", "angle", "software"]:
        print(\
"Invalid argument for qt-opengl. Should be 'desktop', 'angle' or 'software'")
        sys.exit(-1)
    if options.qt_opengl == "desktop":
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseDesktopOpenGL)
    elif options.qt_opengl == "angle":
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseOpenGLES)
    elif options.qt_opengl == "software":
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseSoftwareOpenGL)

a = QApplication(sys.argv)
a.setApplicationName("Mnemosyne")
a.setDesktopFileName("mnemosyne.desktop")
#a.setStyle(QStyleFactory.create("fusion"))

# Get rid of the white menubar on Windows and do some further tweaks.
color = QtGui.QPalette().window().color().name()
stylesheet = """
* {
font-size: 12px;
}

    QMenuBar {
        background-color: {color}; 
        border-bottom-color: #CCCCCC; 
        border-style: solid; 
        border-width: 1px; 
        padding: 0px;
    }

    QFrame[frameShape="5"] {
        border: none;
        background: #CCCCCC;
        max-width: 1px;
    }

    QMenu {
        background-color: {color}; 
        border-color: {color}; 
        border-style: solid; 
        border-width: 1px; 
        margin: 5px;
    }

    QMainWindow::separator, QMenu::separator { 
        height: 1px;
        background: #CCCCCC;
        margin-top: 0px;
        margin-bottom: 0px;
    }

    QMenu::item {
        background-color: transparent;
    }
        
    QMenu::item:selected {
        background: #CCCCCC;
    }"""
a.setStyleSheet(stylesheet)

# For system install:
QtCore.QDir.addSearchPath("icons", 
    os.path.abspath(__file__ + "/../../../pixmaps"))
# For Windows installer:
QtCore.QDir.addSearchPath("icons", 
    "pixmaps")

# Add other components we need. The translator should come first.
# The UI components should come in the order they should be instantiated,
# but apart from that, the order does not matter.
mnemosyne.components.insert(0,
                            ("mnemosyne.pyqt_ui.qt_gui_translator",
                             "QtGuiTranslator"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.main_wdgt",
                             "MainWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.configuration",
                             "PyQtConfiguration"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.pyqt_render_chain",
                             "PyQtRenderChain"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.compact_database_dlg",
                             "CompactDatabaseDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.compact_database_dlg",
                             "PyQtDatabaseMaintenance"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.add_cards_dlg",
                             "AddCardsDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.edit_card_dlg",
                             "EditCardDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.browse_cards_dlg",
                             "BrowseCardsDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.activate_cards_dlg",
                             "ActivateCardsDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.manage_card_types_dlg",
                             "ManageCardTypesDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.manage_plugins_dlg",
                             "ManagePluginsDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_dlg",
                             "StatisticsDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.card_type_wdgt_generic",
                             "GenericCardTypeWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_wdgts_plotting",
                             "ScheduleWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_wdgts_plotting",
                             "RetentionScoreWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_wdgts_plotting",
                             "GradesWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_wdgts_plotting",
                             "EasinessWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_wdgts_plotting",
                             "CardsAddedWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_wdgts_plotting",
                             "CardsLearnedWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.statistics_wdgt_html",
                             "HtmlStatisticsWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.criterion_wdgt_default",
                             "DefaultCriterionWdgt"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.configuration_dlg",
                             "ConfigurationDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.sync_dlg",
                             "SyncDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.qt_sync_server",
                             "QtSyncServer"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.qt_web_server",
                             "QtWebServer"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.configuration_wdgt_main",
                             "ConfigurationWdgtMain"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.configuration_wdgt_study",
                             "ConfigurationWdgtStudy"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.configuration_wdgt_card_appearance",
                             "ConfigurationWdgtCardAppearance"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.configuration_wdgt_servers",
                             "ConfigurationWdgtServers"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.getting_started_dlg",
                             "GettingStartedDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.tip_dlg",
                             "TipDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.about_dlg",
                             "AboutDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.import_dlg",
                             "ImportDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.export_dlg",
                             "ExportDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.export_metadata_dlg",
                             "ExportMetadataDlg"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.prefill_tag_behaviour_plugin",
                             "PrefillTagBehaviourPlugin"))
mnemosyne.components.append(("mnemosyne.pyqt_ui.pronouncer_dlg",
                             "PronouncerDlg"))
mnemosyne.gui_for_component["ScheduledForgottenNew"] = [\
                            ("mnemosyne.pyqt_ui.review_wdgt",
                             "ReviewWdgt")]
mnemosyne.gui_for_component["NewOnly"] = [\
                            ("mnemosyne.pyqt_ui.review_wdgt",
                             "ReviewWdgt")]
mnemosyne.gui_for_component["CramAll"] = [\
                            ("mnemosyne.pyqt_ui.review_wdgt_cramming",
                             "ReviewWdgtCramming")]
mnemosyne.gui_for_component["CramRecent"] = [\
                           ("mnemosyne.pyqt_ui.review_wdgt_cramming",
                            "ReviewWdgtCramming")]
mnemosyne.gui_for_component["GooglePronouncer"] = [\
                           ("mnemosyne.pyqt_ui.pronouncer_dlg",
                            "PronouncerDlg")]
mnemosyne.gui_for_component["GoogleTranslator"] = [\
                           ("mnemosyne.pyqt_ui.translator_dlg",
                            "TranslatorDlg")]

# Run Mnemosyne.
mnemosyne.initialise(data_dir=data_dir, filename=filename,
		     automatic_upgrades=options.automatic_upgrades,
		     debug_file=options.debug_file)
mnemosyne.main_widget().show()
mnemosyne.main_widget().raise_()  # Needed for OSX.
if mnemosyne.config()["first_run"] == True:
    mnemosyne.controller().show_getting_started_dialog()
    mnemosyne.config()["first_run"] = False
elif mnemosyne.config()["show_daily_tips"] == True:
    mnemosyne.controller().show_tip_dialog()
a.exec()
mnemosyne.finalise()
