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

#  Copyright © 2009, 2011-2015  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/>.


import sys, os

from . import debug, config

N_ = lambda s: s


class Tee2(object):
    class Tee(object):
        def __init__(self, file1, file2):
            self.file1 = file1
            self.file2 = file2
        def write(self, data):
            self.file1.write(data)
            self.file2.write(data)
        def flush(self):
            self.file1.flush()
            self.file2.flush()
            
    def __init__(self, name='pybik.log', mode='w'):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        self.stderr = sys.stderr
        sys.stdout = self.Tee(self.file, self.stdout)
        sys.stderr = self.Tee(self.file, self.stderr)
        
    def __del__(self):
        sys.stdout = self.stdout
        sys.stderr = self.stderr
        self.file.close()
        
        
class Options:
    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-widgets', 'notime']
    arg_info = [
        (None, None, '\n'.join(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'], lambda self, value: self.print_usage(),
                            'Show help message and exit'),
        (['--version'], lambda self, value: self.print_version(),
                            'Show version number and exit'),
        (['--config-file='], lambda self, value: self.__dict__.update(config_file=value),
                            'Specify the configuration filename'),
        (['--defaultconfig'], lambda self, value: self.__dict__.update(defaultconfig=True),
                            'Print default settings to stdout and exit'),
        (['--games-file='], lambda self, value: self.__dict__.update(games_file=value),
                            'Specify the filename for saved games'),
        (['--log'], lambda self, value: self.tee(),
                            'Write logfile'),
        (['--list-tests'], lambda self, value: self.print_tests(),
                            'List tests'),
        (['--test'], lambda self, value: self.select_all_tests(),
                            'Run all tests'),
        (['--test='], lambda self, value: self.__dict__.setdefault('test', []).append(value),
                            'Run test T, can be used multiple times, '
                            'use T=n where n is a number to run test T multiple times'),
        (['--test-args='], lambda self, value: self.__dict__['test_args'].extend(a for a in value.split(',') if a),
                            'T is a comma-separated list of test arguments:\n{}'
                            .format(' '.join(test_arg_names))),
        (['--debug='], lambda self, value: self.set_debug_flags(value),
                            '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:'),
        (['-qwindowgeometry G', '-geometry G'], None,
                            'Specifies window geometry for the main window using the X11-syntax.'
                            ' For example: 100x100+50+50'),
        (['-reverse'], None,
                            'Sets the application layout direction to left-to-right'),
        (['-widgetcount'], None,
                            'Prints debug message at the end about number of widgets left'
                            ' undestroyed and maximum number of widgets existed at the same time'),
        ]
        
    def __init__(self):
        self.config_file = config.USER_SETTINGS_FILE
        self.defaultconfig = False
        self.games_file = config.USER_GAMES_FILE
        self.pure_python = False
        self.test = []
        self.test_args = []
        self.ui_args = []
        self.systemdenable = False
        
    @staticmethod
    def format_opts(args):
        args = ((a + a.strip('--')[0].upper() if a.endswith('=') else a) for a in args)
        return '  ' + ', '.join(args)
        
    @staticmethod
    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))
        
    @classmethod
    def print_usage(cls):
        print('Usage:', sys.argv[0], '[options]')
        for args, unused_func, text in cls.arg_info:
            if args is None:
                print()
                print(cls.format_help(text))
            else:
                opts = cls.format_opts(args)
                if len(opts) > 18:
                    text = '\n' + text
                else:
                    opts = opts.ljust(20)
                text = cls.format_help(text, 20)
                print(opts + text)
        sys.exit(0)
        
    @staticmethod
    def print_version():
        from textwrap import fill
        print(config.APPNAME, config.VERSION)
        print()
        print(config.COPYRIGHT.replace('©', '(C)')) #XXX: help2man cannot handle unicode
        print()
        print('\n\n'.join(fill(text, width=78) for text in config.LICENSE_INFO.split('\n\n')))
        print()
        print(fill(config.LICENSE_NOT_FOUND, width=78))
        sys.exit(0)
        
    def tee(self):
        self._tee = Tee2()
        
    @staticmethod
    def print_tests():
        for filename in config.get_testdatafiles():
            print(filename)
        sys.exit(0)
        
    def select_all_tests(self):
        self.__dict__.setdefault('test', []).extend(config.get_testdatafiles())
        
    def set_debug_flags(self, value):
        if not debug.DEBUG:
            print(config.APPNAME, config.VERSION)
        debug_flags = [f for f in value.split(',') if f]
        for d in debug_flags:
            if d not in self.debug_level_names:
                print('unknown debug option:', d)
                sys.exit(1)
        debug.set_flags(debug_flags)
        print('debug flags:', *debug_flags)
        
    def parse(self, args):
        arg_functs = {o: f for opts, f, h in self.arg_info for o in opts or [] if f is not None}
        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:
                self.ui_args.append(arg)
            else:
                func(self, value)
                
        for a in self.test_args:
            if a not in self.test_arg_names:
                print('unknown test argument:', a)
                sys.exit(1)
                
def create_app(root_dir, args):
    import gettext
    from PyQt5.QtCore import QLocale, QTranslator, Qt
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtGui import 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):
    # 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 = Options()
    opts.parse(sys.argv)
    
    debug.debug('Qt args:', opts.ui_args)
    
    if opts.systemdenable:
        print('systemde: nable')
        
    try:
        import PyQt5.QtCore
        import PyQt5.QtGui
        import PyQt5.QtWidgets
    except ImportError as e:
        print('This program needs PyQt5:', 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.runner import TestRunner
        for test in TestRunner.wrap(config.TESTDATA_DIR, opts.test, opts.test_args):
            opts.config_file = test.settings_file
            opts.games_file = test.games_file
            window = create_window(opts)
            test.run(window)
            app.exec()
    else:
        window = create_window(opts)
        app.exec()
        
    if opts.systemdenable:
        print('systemdi: sable')
    

