#  ============================================================================
#
#  Copyright (C) 2007-2011 Conceptive Engineering bvba. All rights reserved.
#  www.conceptive.be / project-camelot@conceptive.be
#
#  This file is part of the Camelot Library.
#
#  This file may be used under the terms of the GNU General Public
#  License version 2.0 as published by the Free Software Foundation
#  and appearing in the file license.txt included in the packaging of
#  this file.  Please review this information to ensure GNU
#  General Public Licensing requirements will be met.
#
#  If you are unsure which license is appropriate for your use, please
#  visit www.python-camelot.com or contact project-camelot@conceptive.be
#
#  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
#  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
#  For use of this library in commercial applications, please contact
#  project-camelot@conceptive.be
#
#  ============================================================================

"""Main function, to be called to start the GUI interface"""
from PyQt4 import QtCore
from camelot.core.utils import ugettext as _

from camelot.art import resources # Required for tooltip visualization
resources.__name__ # Dodge PyFlakes' attack

class Application(QtCore.QObject):
    """The camelot application.  This class will take care of the order of
    initialization of various stuff needed to get the application up and
    running, each of its methods will be called in subsequent order,
    overwrite any of them to customize its behaviour.

    This class will create the QApplication and call its processEvents
    method regulary while starting up the application.
    """

    def __init__(self, application_admin):
        """:param application_admin: a subclass of camelot.admin.application_admin.ApplicationAdmin
        customized to your app"""
        super(Application, self).__init__()
        self.application_admin = application_admin
        self._splash_window = None

    def show_splashscreen(self):
        """:return: the splash window"""
        from PyQt4 import QtGui
        pixmap = self.application_admin.get_splashscreen()
        # don't let splash screen stay on top, this might hinder
        # registration wizards or others that wait for user input
        # while camelot is starting up
        # flag = QtCore.Qt.WindowStaysOnTopHint
        splash_window = QtGui.QSplashScreen(pixmap) #, flag)
        splash_window.show()
        self._splash_window = splash_window
        return splash_window

    def show_splash_message(self, splash_window, message):
        """:param message: displays a message on the splash screen, informing
        the user of the status of the application"""
        from PyQt4 import QtCore
        msgalign = QtCore.Qt.AlignBottom #| QtCore.Qt.AlignRight
        msgcolor = QtCore.Qt.white
        splash_window.showMessage(message, msgalign, msgcolor)

    def set_application_attributes(self, application):
        """Sets the attributes of the QApplication object
        :param application: the QApplication object"""
        application.setOrganizationName(self.application_admin.get_organization_name())
        application.setOrganizationDomain(self.application_admin.get_organization_domain())
        application.setApplicationName(self.application_admin.get_name())
        application.setWindowIcon(self.application_admin.get_icon())

    def pre_initialization(self):
        """Method that is called before the model thread is started, while the app is still
        running single threaded.

        The default implementation verifies if the select_database attribute is set to
        True on the ApplicationAdmin, and if this is the case, present the user with a
        database selection wizard.
        """
        if self.application_admin.database_selection:
            #
            # in case of profile selection, load the system locale translations for
            # the profile dialog.  These might be different from the final translations
            # that are specified in the profile
            #
            locale_name = QtCore.QLocale().name()
            language_name = locale_name.split('_')[0]
            camelot_translator = self.application_admin._load_translator_from_file( 'camelot', 
                                                                                    'camelot',
                                                                                    'art/translations/%s/LC_MESSAGES/'%language_name )
            if camelot_translator:
                QtCore.QCoreApplication.instance().installTranslator( camelot_translator )
            from camelot.view.database_selection import select_database
            select_database(self.application_admin)

    def start_model_thread(self):
        """Launch the second thread where the model lives"""
        from camelot.view.model_thread import get_model_thread, construct_model_thread
        from camelot.view.remote_signals import construct_signal_handler
        construct_model_thread()
        construct_signal_handler()
        mt = get_model_thread()
        mt.setup_exception_signal.connect( self.initialization_exception )
        mt.start()

    def load_translations(self, application):
        """Fill the QApplication with the needed translations
        :param application: the QApplication on which to install the translator
        """
        from camelot.core.utils import load_translations
        from camelot.view.model_thread import get_model_thread
        get_model_thread().post(load_translations)
        for translator in self.application_admin.get_translator():
            application.installTranslator(translator)

    def initialization(self):
        """Method that is called afther the model has been set up, before the main
        window is constructed"""
        pass

    def create_main_window(self):
        """:return: a QWidget representing the main window, upon its appearance, the splash
        screen will be closed"""
        return self.application_admin.create_main_window()

    def close_splashscreen(self, splash_window, main_window):
        """closes the splashcreen on appearance of the main window
        :param splash_window: the splash screen window, as generated by the
        show splashcreen function
        """
        main_window.show()
        splash_window.finish(main_window)

    def start_event_loop(self, application):
        """Starts the application's main event loop, wait until it is finished, then
        exit
        :param application: the QApplication to run"""
        import sys
        sys.exit( application.exec_() )

    @QtCore.pyqtSlot(object)
    def initialization_exception(self, exception_info):
        """This method is called whenever an exception occurs before the event
        loop has been started, or if the setup of the model thread failed.  By
        default this pops up a dialog.
        
        :param exception_info: a serialized form of the exception
        """
        from camelot.view.controls import exception
        if self._splash_window:
            self._splash_window.hide()
        exception.model_thread_exception_message_box(exception_info)
        
    def main(self):
        """the main function of the application, this will call all other
        functions before starting the event loop"""
        import logging
        logger = logging.getLogger('camelot.view.main')

        try:
            #
            # before anything else happens or is imported, the splash screen should be there
            #
            import sys
            from PyQt4 import QtGui, QtCore
            app = QtGui.QApplication([a for a in sys.argv if a])
            splash_window = self.show_splashscreen()

            self.show_splash_message(splash_window, _('Initialize application'))
            # regularly call processEvents to keep the splash alive
            app.processEvents()
            #  font = app.font()
            #  font.setStyleStrategy(QtGui.QFont.PreferAntialias)
            #  font.setPointSize(font.pointSize()+1)
            #  app.setFont(font)
            if hasattr( QtCore, 'QT_MAJOR_VERSION'):
                QT_MAJOR_VERSION = float('.'.join(str(QtCore.QT_VERSION_STR).split('.')[0:2]))
                logger.debug('qt version %s, pyqt version %s' %
                             (QtCore.QT_VERSION_STR, QtCore.PYQT_VERSION_STR))
                logger.debug('qt major version %f' % QT_MAJOR_VERSION)
            app.processEvents()
            import sqlalchemy, elixir
            logger.debug('sqlalchemy version %s'%sqlalchemy.__version__)
            logger.debug('elixir version %s'%elixir.__version__)
            app.processEvents()
            self.set_application_attributes(app)
            self.pre_initialization()
            app.processEvents()
            # regularly call processEvents to keep the splash alive
            self.show_splash_message(splash_window, _('Setup database'))
            app.processEvents()
            self.start_model_thread()
            app.processEvents()
            #
            # WEIRD, if we put this code in a method, the translations
            # don't work
            #
            from camelot.core.utils import load_translations
            from camelot.view.model_thread import get_model_thread
            self.show_splash_message(splash_window, _('Load translations'))
            app.processEvents()
            get_model_thread().post(load_translations)
            self.show_splash_message(splash_window, _('Create translator'))
            app.processEvents()
            translator = self.application_admin.get_translator()
            self.show_splash_message(splash_window, _('Install translator'))
            if isinstance(translator, list):
                for t in translator:
                    app.installTranslator( t )
            else:
                app.installTranslator( translator )
            app.processEvents()
            # Set the style sheet
            self.show_splash_message(splash_window, _('Create main window'))
            app.processEvents()
            stylesheet = self.application_admin.get_stylesheet()
            if stylesheet:
                app.setStyleSheet(stylesheet)
            app.processEvents()
            self.initialization()
            app.processEvents()
            main_window = self.create_main_window()
            self.close_splashscreen(splash_window, main_window)
            return self.start_event_loop(app)
        except Exception, e:
            from camelot.view.controls import exception
            exc_info = exception.register_exception( logger, 'exception in initialization', e )
            self.initialization_exception( exc_info )

def main(application_admin):
    """shortcut main function, call this function to start the GUI interface with minimal hassle
    and without the need to construct an Application object.  If you need to customize the initialization
    process, construct an Application subclass and use it's main method.

    :param application_admin: object of type ApplicationAdmin (as defined in application_admin.py)
    that specifies the look of the GUI interface
    """
    app = Application(application_admin)
    app.main()

if __name__ == '__main__':
    from application_admin import ApplicationAdmin
    main(ApplicationAdmin())


