#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright (C) 2010-2011 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
# Copyright (C) 2010-2011 by Dick Kniep <dick.kniep@lindix.nl>
#
# PyHoca GUI 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.
#
# PyHoca GUI 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, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

modules ={}

import gevent
import gevent.monkey
gevent.monkey.patch_all()

import subprocess

try:
    import wxversion
    wxversion.select('2.9')
except: pass
try:
    import wxversion
    wxversion.select('2.8')
except: pass

import argparse
import os
import sys
import exceptions
import locale
import gettext
import wx

PROG_NAME = os.path.basename(sys.argv[0])
PROG_PID  = os.getpid()

from x2go import X2GOCLIENT_OS as _X2GOCLIENT_OS

if _X2GOCLIENT_OS in ('Linux', 'Mac'):
    import setproctitle
    setproctitle.setproctitle(PROG_NAME)

app = sys.argv[0]
if app.startswith('./') or os.path.dirname(PROG_NAME).endswith('trunk'):
    sys.path.insert(0, os.path.join(os.path.dirname(PROG_NAME)))
    os.environ['PYHOCAGUI_DEVELOPMENT'] = '1'
    print '### PyHoca-GUI running in development mode ###'
else:
    if _X2GOCLIENT_OS == 'Windows':
        sys.stdout = open(os.path.join(os.environ['TEMP'], '%s_stdout.log' % PROG_NAME), 'w')
        sys.stderr = open(os.path.join(os.environ['TEMP'], '%s_stderr.log' % PROG_NAME), 'w')

from pyhoca.wxgui.basepath import locale_basepath

# Python X2go modules
from x2go import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
if _X2GOCLIENT_OS == 'Windows':
    from x2go import X2goClientXConfig as _X2goClientXConfig
from x2go import X2goLogger as _X2goLogger
from x2go import x2go_cleanup as _x2go_cleanup

# X2go backends
from x2go.defaults import BACKENDS_CONTROLSESSION, BACKEND_CONTROLSESSION_DEFAULT
from x2go.defaults import BACKENDS_TERMINALSESSION, BACKEND_TERMINALSESSION_DEFAULT
from x2go.defaults import BACKENDS_SERVERSESSIONINFO, BACKEND_SERVERSESSIONINFO_DEFAULT
from x2go.defaults import BACKENDS_SERVERSESSIONLIST, BACKEND_SERVERSESSIONLIST_DEFAULT
from x2go.defaults import BACKENDS_PROXY, BACKEND_PROXY_DEFAULT
from x2go.defaults import BACKENDS_SESSIONPROFILES, BACKEND_SESSIONPROFILES_DEFAULT
from x2go.defaults import BACKENDS_CLIENTSETTINGS, BACKEND_CLIENTSETTINGS_DEFAULT
from x2go.defaults import BACKENDS_CLIENTPRINTING, BACKEND_CLIENTPRINTING_DEFAULT

from pyhoca.wxgui import __VERSION__ as _version
from pyhoca.wxgui import messages
from pyhoca.wxgui import PyHocaGUI

if _X2GOCLIENT_OS == 'Windows':
    from pyhoca.wxgui.basepath import nxproxy_binary
    os.environ.update({'NXPROXY_BINARY': nxproxy_binary, })

__author__ = "Mike Gabriel, Dick Kniep"
__version__ = _version

# version information
VERSION=_version
VERSION_TEXT="""
%s[%s] - an X2go GUI client written in Python
----------------------------------------------------------------------
developed by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
sponsored by Dick Kniep <dick.kniep@lindix.nl> (2010-2011)

VERSION: %s

""" % (PROG_NAME, PROG_PID, VERSION)

def check_running():
    if _X2GOCLIENT_OS  in ('Linux', 'Mac'):
        p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
        psA_out = p.communicate()
        return psA_out[0].count(PROG_NAME) > 1
    elif _X2GOCLIENT_OS == 'Windows':
        import wmi
        w = wmi.WMI()
        _p_names = []
        for process in w.Win32_Process():
            _p_names.append(process.Name)
        return len([ _p_name for _p_name in _p_names if _p_name == PROG_NAME]) > 1


def version():
    # print version text and exit
    sys.stderr.write ("%s\n" % VERSION_TEXT)
    sys.exit(0)


# sometimes we have to fail...
def runtime_error(m, parser=None, exitcode=-1):
    """\
    STILL UNDOCUMENTED
    """
    if parser is not None:
        parser.print_usage()
    sys.stderr.write ("%s: error: %s\n" % (PROG_NAME, m))
    sys.exit(exitcode)


if _X2GOCLIENT_OS == 'Windows':
    _x = _X2goClientXConfig()
    _known_xservers = _x.known_xservers
    _installed_xservers = _x.installed_xservers


if _X2GOCLIENT_OS == 'Windows':
    _config_backends = ('FILE', 'WINREG')
elif _X2GOCLIENT_OS == 'Linux':
    _config_backends = ('FILE', 'GCONF')
else:
    _config_backends = ('FILE')

for _profiles_backend_default in _config_backends:
    if BACKENDS_SESSIONPROFILES[_profiles_backend_default] == BACKEND_SESSIONPROFILES_DEFAULT:
        break
for _settings_backend_default in _config_backends:
    if BACKENDS_CLIENTSETTINGS[_settings_backend_default] == BACKEND_CLIENTSETTINGS_DEFAULT:
        break
for _printing_backend_default in _config_backends:
    if BACKENDS_CLIENTPRINTING[_printing_backend_default] == BACKEND_CLIENTPRINTING_DEFAULT:
        break

# debug options...
debug_options =  [
                   {'args':['-d','--debug'], 'default': False, 'action': 'store_true', 'help': 'enable application debugging code', },
                   {'args':['--quiet'], 'default': False, 'action': 'store_true', 'help': 'disable any kind of log output', },
                   {'args':['--libdebug'], 'default': False, 'action': 'store_true', 'help': 'enable debugging code of the underlying Python X2go module', },
                   {'args':['--libdebug-sftpxfer'], 'default': False, 'action': 'store_true', 'help': 'enable debugging code of Python X2go\'s sFTP server code (very verbose, and even promiscuous)', },
                   {'args':['-V', '--version'], 'default': False, 'action': 'store_true', 'help': 'print version number and exit', },
                 ]
x2go_gui_options = [
                   {'args':['-u','--username'], 'default': None, 'metavar': '<username>', 'help': 'username for the session (default: current user)', },
                   {'args':['-P','--session-profile'], 'default': None, 'metavar': '<profile-name>', 'help': 'directly connect to a session profile', },
                   {'args':['--non-interactive'], 'default': False, 'action': 'store_true', 'help': 'run the session manager in non-interactive mode, this option sets the following options to true: --restricted-trayicon, --start-on-connect, --resume-all-on-connect, --exit-on-disconnect, --disconnect-on-suspend and --disconnect-on-terminate', },
                   {'args':['--auto-connect'], 'default': False, 'action': 'store_true', 'help': 'connect sessions via SSH pubkey authentication if possible', },
                   {'args':['--show-profile-metatypes'], 'default': False, 'action': 'store_true', 'help': 'show descriptive meta information on session profiles in menus (NOTE: this makes menus appear a bit more sluggish, use it mostly for debugging)', },
                   {'args':['--restricted-trayicon'], 'default': False, 'action': 'store_true', 'help': 'restricts session manager\'s main icon functionality to information window and application exit', },
                   {'args':['--start-on-connect'], 'default': False, 'action': 'store_true', 'help': 'start a session directly after authentication if no session is currently running/suspended', },
                   {'args':['--exit-on-disconnect'], 'default': False, 'action': 'store_true', 'help': 'exit the session manager after a server connection has died', },
                   {'args':['--resume-newest-on-connect', '--resume-on-connect'], 'default': False, 'action': 'store_true', 'help': 'on connect auto-resume the newest suspended session', },
                   {'args':['--resume-oldest-on-connect'], 'default': False, 'action': 'store_true', 'help': 'on connect auto-resume the oldest suspended session', },
                   {'args':['--resume-all-on-connect'], 'default': False, 'action': 'store_true', 'help': 'auto-resume all suspended sessions on connect', },
                   {'args':['--disconnect-on-suspend'], 'default': False, 'action': 'store_true', 'help': 'disconnect a server if a session has been suspended', },
                   {'args':['--disconnect-on-terminate'], 'default': False, 'action': 'store_true', 'help': 'disconnect a server if a session has been terminated', },
                   {'args':['--disable-splash'], 'default': False, 'action': 'store_true', 'help': 'disable the applications splash screen', },
                   {'args':['--disable-options'], 'default': False, 'action': 'store_true', 'help': 'disable the client options configuration window', },
                   {'args':['--disable-printingprefs'], 'default': False, 'action': 'store_true', 'help': 'disable the client\'s printing preferences window', },
                   {'args':['--disable-profilemanager'], 'default': False, 'action': 'store_true', 'help': 'disable the session profile manager window', },
                   {'args':['--display'], 'default': None, 'metavar': '<hostname>:<screennumber>', 'help': 'set the DISPLAY environment variable to <hostname>:<screennumber>', },
                   {'args':['--logon-window-position'], 'default': None, 'metavar': '<x-pos>x<y-pos>', 'help': 'give a custom position for the logon window, use negative values to position relative to right/bottom border', },
                 ]
if _X2GOCLIENT_OS == 'Windows':
    x2go_gui_options.append(
                   {'args':['--lang'], 'default': None, 'metavar': 'LANGUAGE', 'help': 'set the GUI language (currently available: en, de, nl, es)', },
        )

backend_options = [
                   {'args':['--backend-controlsession'], 'default': None, 'metavar': '<CONTROLSESSION_BACKEND>', 'choices': BACKENDS_CONTROLSESSION.keys(), 'help': 'force usage of a certain CONTROLSESSION_BACKEND (do not use this unless you know exactly what you are doing)', },
                   {'args':['--backend-terminalsession'], 'default': None, 'metavar': '<TERMINALSESSION_BACKEND>', 'choices': BACKENDS_TERMINALSESSION.keys(), 'help': 'force usage of a certain TERMINALSESSION_BACKEND (do not use this unless you know exactly what you are doing)', },
                   {'args':['--backend-serversessioninfo'], 'default': None, 'metavar': '<SERVERSESSIONINFO_BACKEND>', 'choices': BACKENDS_TERMINALSESSION.keys(), 'help': 'force usage of a certain SERVERSESSIONINFO_BACKEND (do not use this unless you know exactly what you are doing)', },
                   {'args':['--backend-serversessionlist'], 'default': None, 'metavar': '<SERVERSESSIONLIST_BACKEND>', 'choices': BACKENDS_TERMINALSESSION.keys(), 'help': 'force usage of a certain SERVERSESSIONLIST_BACKEND (do not use this unless you know exactly what you are doing)', },
                   {'args':['--backend-proxy'], 'default': None, 'metavar': '<PROXY_BACKEND>', 'choices': BACKENDS_PROXY.keys(), 'help': 'force usage of a certain PROXY_BACKEND (do not use this unless you know exactly what you are doing)', },
                   {'args':['--backend-sessionprofiles'], 'default': None, 'metavar': '<SESSIONPROFILES_BACKEND>', 'choices': _config_backends, 'help': 'use given backend for accessing session profiles, available backends on your system: %s (default: %s)' % (', '.join(_config_backends), _profiles_backend_default), },
                   {'args':['--backend-clientsettings'], 'default': None, 'metavar': '<CLIENTSETTINGS_BACKEND>', 'choices': _config_backends, 'help': 'use given backend for accessing the client settings configuration, available backends on your system: %s (default: %s)' % (', '.join(_config_backends), _settings_backend_default), },
                   {'args':['--backend-clientprinting'], 'default': None, 'metavar': '<CLIENTPRINTING_BACKEND>', 'choices': _config_backends, 'help': 'use given backend for accessing the client printing configuration, available backends on your system: %s (default: %s)' % (', '.join(_config_backends), _printing_backend_default), },
                  ]

if _X2GOCLIENT_OS == 'Windows':
    contrib_options = [
                       {'args':['--start-xserver'], 'default': False, 'action': 'store_true', 'help': 'start the XServer before starting the session manager application, detect best XServer automatically, if more than one XServer is installed on your system', },
                       {'args':['-X', '--preferred-xserver'], 'default': None, 'metavar': '<XSERVER>', 'choices': _known_xservers, 'help': 'start either of the currently supported XServers: %s -- make sure your preferred XServer is installed on your system' % _known_xservers, },
                       {'args':['--start-pulseaudio'], 'default': False, 'action': 'store_true', 'help': 'start the PulseAudio server before starting the session manager application', },
                      ]

portable_options = [
                   {'args':['--client-rootdir'], 'default': None, 'metavar': '</path/to/.x2goclient/dir>', 'help': 'define an alternative location where to find plain text config files (default: <HOME>/.x2goclient). This option will set ,,--backend-profiles FILE\'\', ,,--backend-clientsettings FILE\'\' and ,,--backend-clientprinting FILE\'\'', },
                   {'args':['--sessions-rootdir'], 'default': None, 'metavar': '</path/to/.x2go/dir>', 'help': 'define an alternative location for session runtime files'},
                   {'args':['--ssh-rootdir'], 'default': None, 'metavar': '</path/to/.ssh/dir>', 'help': 'define an alternative location for SSH files', },
                  ]


def parseargs():

    global DEBUG
    global print_action_args

    p = argparse.ArgumentParser(description='Graphical X2go client implemented in (wx)Python.',\
                                formatter_class=argparse.RawDescriptionHelpFormatter, \
                                add_help=True, argument_default=None)
    p_debugopts = p.add_argument_group('Debug options')
    p_guiopts = p.add_argument_group('PyHoca-GUI options')
    p_portableopts = p.add_argument_group('Portable application support')
    p_backendopts = p.add_argument_group('Python X2go backend options (for experts only)')

    if _X2GOCLIENT_OS == 'Windows':
        p_contribopts = p.add_argument_group('XServer options (MS Windows only)')
        p_portableopts = p.add_argument_group('File locations for portable setups (MS Windows only)')
        _option_groups = ((p_guiopts, x2go_gui_options), (p_debugopts, debug_options), (p_contribopts, contrib_options), (p_portableopts, portable_options), (p_backendopts, backend_options), )
    else:
        _option_groups = ((p_guiopts, x2go_gui_options), (p_debugopts, debug_options),  (p_portableopts, portable_options), (p_backendopts, backend_options), )
    for (p_group, opts) in _option_groups:
        required = False
        for opt in opts:

            args = opt['args']
            del opt['args']
            p_group.add_argument(*args, **opt)

    a = p.parse_args()

    logger = _X2goLogger(tag='PyHoca-GUI')
    liblogger = _X2goLogger()

    if a.debug:
        logger.set_loglevel_debug()

    if a.libdebug:
        liblogger.set_loglevel_debug()

    if a.quiet:
        logger.set_loglevel_quiet()
        liblogger.set_loglevel_quiet()

    if a.libdebug_sftpxfer:
        liblogger.enable_debug_sftpxfer()

    if a.version:
        version()

    if a.username is None:
        a.username = _CURRENT_LOCAL_USER

    if a.non_interactive:
        if a.session_profile is None:
            runtime_error('In non-interactive mode you have to use the --session-profile option (or -P) to specify a certain session profile name!', parser=p)
        a.restricted_trayicon = True
        a.start_on_connect = True
        a.resume_all_on_connect = True
        a.exit_on_disconnect = True
        a.disconnect_on_suspend = True
        a.disconnect_on_terminate = True

    if a.non_interactive and (a.resume_newest_on_connect or a.resume_oldest_on_connect):
        # allow override...
        a.resume_all_on_connect = False

    if _X2GOCLIENT_OS == 'Windows' and a.preferred_xserver:
        if a.preferred_xserver not in _installed_xservers:
            runtime_error('Xserver ,,%s\'\' is not installed on your Windows system' % a.preferred_xserver, parser=p)
        a.start_xserver = a.preferred_xserver

    if _X2GOCLIENT_OS == 'Windows' and a.start_xserver and a.display:
        runtime_error('You can tell PyHoca-GUI to handle XServer startup and then specify a DISPLAY environment variable!', parser=p)

    if a.display:
        os.environ.update({'DISPLAY': a.display})
    else:
        if _X2GOCLIENT_OS == 'Windows' and not a.start_xserver:
            os.environ.update({'DISPLAY': 'localhost:0'})

    if a.client_rootdir:
        a.backend_sessionprofiles='FILE'
        a.backend_clientsettings='FILE'
        a.backend_clientprinting='FILE'

    return a, logger, liblogger

def main():
    args, logger, liblogger = parseargs()
    if _X2GOCLIENT_OS == 'Windows':
        if args.lang:
            lang = gettext.translation('pyhoca-gui', localedir=locale_basepath, languages=[args.lang], )
        else:
            lang = gettext.translation('pyhoca-gui', localedir=locale_basepath, languages=['en'], )
        lang.install(unicode=True)
    else:
        gettext.install('pyhoca-gui', localedir=locale_basepath, unicode=True)

    if check_running(): 
        sys.stderr.write("\n###############################\n### %s: already running for user %s\n###############################\n" % (PROG_NAME, _CURRENT_LOCAL_USER))
        m = messages.PyHoca_MessageWindow_Ok(wx.App(), shortmsg='ALREADY_RUNNING', title=u'PyHoca-GUI (%s)...' % VERSION, icon='pyhoca-trayicon')
        m.ShowModal()
        version()

    try:
        thisPyHocaGUI = PyHocaGUI(args, logger, liblogger, version=VERSION)
        thisPyHocaGUI.MainLoop()
    except KeyboardInterrupt:
        thisPyHocaGUI.ExitMainLoop()
    except SystemExit:
        thisPyHocaGUI.ExitMainLoop()

if __name__ == '__main__':
    main()
