#-*- coding:utf-8 -*-

#  Pybik -- A 3 dimensional magic cube game.
#  Copyright © 2009, 2011-2013  B. Clausius <barcc@gmx.de>
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Ported from GNUbik
# Original filename: main.c
# Original copyright and license: 1998, 2003--2004  John Darrington, GPL3+



import sys, os
from collections import namedtuple

from . import debug, config

N_ = lambda s: s


def format_opts(opts):
    opts = ((o + o.strip('--')[0].upper() if o.endswith('=') else o) for o in opts)
    return '  ' + ', '.join(opts)
    
def format_help(text, indent=0):
    try:
        width = int(os.environ['COLUMNS']) - 2
    except (KeyError, ValueError):
        width = 78
    width -= indent
    def split(text):
        lines = text.split('\n')
        for line in lines:
            res = None
            words = line.split(' ')
            for word in words:
                if res is None:
                    res = word
                elif len(res + word) + 1 <= width:
                    res = ' '.join((res, word))
                else:
                    yield res
                    res = word
            yield res
    return '\n'.ljust(indent+1).join(split(text))
    
def print_usage(unused_value, unused_opts):
    print('Usage:', sys.argv[0], '[options]')
    for args, unused_func, text in arg_info:
        if args is None:
            print()
            print(format_help(text))
        else:
            opts = format_opts(args)
            if len(opts) > 18:
                text = '\n' + text
            else:
                opts = opts.ljust(20)
            text = format_help(text, 20)
            print(opts + text)
    sys.exit(0)
    
def print_version(*unused_args):
    print(config.APPNAME, config.VERSION)
    print()
    print(config.COPYRIGHT.replace('©', '(C)')) #XXX: help2man cannot handle unicode
    print()
    print(config.wrap(config.LICENSE_INFO))
    print()
    print(config.wrap(config.LICENSE_NOT_FOUND))
    sys.exit(0)
    
def print_tests(*args):
    for filename in config.get_testdatafiles():
        print(filename)
    sys.exit(0)
    
def select_all_tests(value, opts):
    opts.setdefault('test', []).extend(config.get_testdatafiles())
    
debug_level_names = [__a[6:].lower() for __a in debug.__all__ if __a.startswith('DEBUG_')]
test_arg_names = ['write-y', 'write-yes', 'write-n', 'write-no', 'write-e', 'write-error',
                  'log-file', 'log-widgets']

arg_info = [
        (None, None, config.LONG_DESCRIPTION.replace('ő', 'o')), #XXX: help2man cannot handle unicode
        (None, None, 'Pybik can be fully controlled and configured via the graphical user interface.'
                     ' The options listed here are intended primarily for testing and debugging.'),
        (None, None, 'Options:'),
        (['-h', '--help'], print_usage,
                            'Show help message and exit'),
        (['--version'], print_version,
                            'Show version number and exit'),
        (['--config-file='], lambda value, opts: opts.update(config_file=value),
                            'Specify the configuration file to use instead of the default location'),
        (['--defaultconfig'], lambda value, opts: opts.update(defaultconfig=True),
                            'Print default settings to stdout and exit'),
        (['--shader='], lambda value, opts: opts.update(shader=value),
                            'Use a different shader'),
        (['--list-tests'], print_tests,
                            'List tests'),
        (['--test'], select_all_tests,
                            'Run all tests'),
        (['--test='], lambda value, opts: opts.setdefault('test', []).append(value),
                            'Run test T, can be used multiple times'),
        (['--test-args='], lambda value, opts: opts['test_args'].extend(a for a in value.split(',') if a),
                            'Comma-separated list of test arguments:\n{}'
                            .format(' '.join(test_arg_names))),
        (['--debug='], lambda value, opts: opts.setdefault('debug_flags', []).extend(
                                            f for f in value.split(',') if f),
                            'D is a comma-separated list of debug flags:\n{0}'
                            .format(' '.join(debug_level_names))),
        (None, None,  'Qt-Options, for a full list refer to the Qt-Reference,'
                ' but not all options make sense for this application:'),
        (['-style S'], None,
                            'sets the application GUI style. Possible values are'
                            ' motif, windows, and platinum.'),
        (['-widgetcount'], None,
                            'prints debug message at the end about number of widgets left'
                            ' undestroyed and maximum number of widgets existed at the same time'),
        (['-reverse'], None,
                            "sets the application's layout direction to left-to-right"),
        (['-graphicssystem G'], None,
                            'sets the backend to be used for on-screen widgets and pixmaps.'
                            ' Available options are raster and opengl.'),
    ]
    
def parse_args(args):
    arg_functs = {o: f for opts, f, h in arg_info for o in opts or [] if f is not None}
    opts = {
            'config_file': config.USER_SETTINGS_FILE,
            'defaultconfig': False,
            'shader': None,
            'pure_python': False,
            'test': [],
            'test_args': [],
            'ui_args': [],
        }
    for arg in args:
        try:
            index = arg.index('=')
        except ValueError:
            value = None
        else:
            arg, value = arg[:index+1], arg[index+1:]
        try:
            func = arg_functs[arg]
        except KeyError:
            opts['ui_args'].append(arg)
        else:
            func(value, opts)
            
    for a in opts['test_args']:
        if a not in test_arg_names:
            print('unknown test argument:', a)
            sys.exit(1)
        
    for d in opts.get('debug_flags', []):
        if d not in debug_level_names:
            print('unknown debug option:', d)
            sys.exit(1)
    opts.setdefault('debug_flags', None)
    
    class Opts: pass
    for k, v in opts.items():
        setattr(Opts, k, v)
    return Opts
    
def create_app(root_dir, args):
    import gettext
    from PyQt4.QtCore import QLocale, QTranslator, Qt
    from PyQt4.QtGui import QApplication, QPalette, QColor
    
    # initialize QApplication
    QApplication.setAttribute(Qt.AA_X11InitThreads)
    app = QApplication(args)
    args = app.arguments()[1:]
    if args:
        print('Unknown arguments:', ' '.join(args))
        sys.exit(1)
    app.setOrganizationName(config.PACKAGE)
    app.setApplicationName(config.APPNAME)
    app.setApplicationVersion(config.VERSION)
    
    # Workaround for whatsThis-Text (white text on light background)
    palette = app.palette()
    colorfg = palette.color(QPalette.Active, QPalette.ToolTipText)
    colorbg = palette.color(QPalette.Active, QPalette.ToolTipBase)
    valuefg = colorfg.value()
    valuebg = (valuefg + 255) // 2 if valuefg < 128 else valuefg // 2
    colorbg = QColor.fromHsv(colorbg.hue(), colorbg.saturation(), valuebg)
    palette.setColor(QPalette.Active, QPalette.ToolTipBase, colorbg)
    app.setPalette(palette)
    
    # initialize translation
    language = QLocale.system().name()
    # standard Qt translation, used for e.g. standard buttons and shortcut names
    translator = QTranslator()
    translator.load('qt_' + language, config.QT_LOCALE_DIR)
    app.installTranslator(translator)
    # the rest of the app use gettext for translation
    if root_dir == sys.prefix:
        # normal installation
        LOCALEDIR = None
    else:
        # different root, e.g. /usr/local, source directory
        LOCALEDIR = config.LOCALE_DIR
    t = gettext.translation(config.PACKAGE, LOCALEDIR, languages=[language], fallback=True)
    t.install(names=['ngettext'])
    return app, translator
    
def create_window(opts):
    # initialize settings
    from .settings import settings
    try:
        settings.load(opts.config_file)
    except EnvironmentError as e:
        error_message = N_('An error occurred while reading the settings:\n'
                        '{error_message}').format(error_message=e)
        debug.debug(error_message)
        settings.load('')
        from PyQt4.QtCore import QTimer
        QTimer.singleShot(0, lambda: window.error_dialog(_(error_message)))
        
    # create main window
    # The application module must be imported after a QApplication object is created
    from .application import MainWindow
    return MainWindow(opts)
    
    
def run(root_dir=None):
    opts = parse_args(sys.argv)
    
    if opts.debug_flags is not None:
        print(config.PACKAGE, config.VERSION)
        debug.set_flags(opts.debug_flags)
        print('debug flags:', *opts.debug_flags)
        debug.debug('Qt args:', opts.ui_args)
    
    try:
        import sip
        sip.setapi('QString', 2)
        sip.setapi('QVariant', 2)
        import PyQt4.QtCore
        import PyQt4.QtGui
        import PyQt4.QtOpenGL
    except ImportError as e:
        print('This program needs PyQt4:', e)
        sys.exit(1)
    
    if opts.defaultconfig:
        # settings needs Qt
        from .settings import settings
        settings.load('')
        settings.keystore.dump(sys.stdout, all=True)
        sys.exit(0)
        
    app, translator = create_app(root_dir, opts.ui_args)
    # keep the translator object for the lifetime of the app object, so that the translation works
    if opts.test:
        from pybiktest.testrunner import TestRunner
        for test in TestRunner.wrap(config.TESTDATA_DIR, opts.test, opts.test_args):
            opts.config_file = test.settings_file
            window = create_window(opts)
            test.run(window)
            app.exec()
    else:
        window = create_window(opts)
        app.exec()
    

