#!/usr/bin/env python

# Copyright (C) 2011, Aleksey Lim
#
# 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 os
import sys
import time
import logging
import datetime
from optparse import OptionParser
from os.path import basename
from gettext import gettext as _

import dbus
import gobject
import dbus.mainloop.glib

gobject.threads_init()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

from sugar_client import env, printf, util, direct
from sugar_client.util import enforce


_SERVER_SERVICE = (
        dbus.SessionBus,
        'org.sugarlabs.client.Server',
        'org.sugarlabs.client.Server',
        '/org/sugarlabs/client/Server',
        )

_BACKUP_SERVICE = (
        dbus.SessionBus,
        'org.sugarlabs.client.Backup',
        'org.sugarlabs.client.Backup',
        '/org/sugarlabs/client/Backup',
        )

_SYSTEM_SERVICE = (
        dbus.SystemBus,
        'org.sugarlabs.client.System',
        'org.sugarlabs.client.System',
        '/org/sugarlabs/client/System',
        )


def cmd_register():
    url = args.pop(0) if args else ''

    if options.direct:
        return direct.register(url)
    else:
        return _call(_SERVER_SERVICE, 'Register', url)


def cmd_backup():
    url = args.pop(0) if args else ''

    if options.direct:
        return direct.backup_journal(url)
    else:
        return _call(_BACKUP_SERVICE, 'Backup', url)


def cmd_restore():
    url = ''
    date = 0
    if args:
        arg = args.pop(0)
        try:
            date = datetime.datetime.strptime(arg, '%Y-%m-%d')
            date = int(time.mktime(date.timetuple()))
        except ValueError:
            url = arg

    if options.direct:
        return direct.restore_journal(url, date)
    else:
        return _call(_BACKUP_SERVICE, 'Restore', url, date)


def cmd_update():
    if options.direct:
        return direct.update_system()
    else:
        return _call(_SYSTEM_SERVICE, 'Update')


def cmd_monitor():
    if options.direct:
        return direct.monitor()
    else:
        printf.info(_('Command makes sense only with --direct option passed'))


def cmd_session():
    enforce(not options.direct,
            _('This command cannot be called with --direct'))
    enforce(os.geteuid() != 0,
            _('This command can be called only by non-root user'))

    # Startup all services to let them do background work they designed for
    for bus_class, iface, name, path in \
            [_SERVER_SERVICE, _SYSTEM_SERVICE]:
        printf.info(_('Bootstraping %s'), iface)
        obj = bus_class().get_object(name, path)
        dbus.Interface(obj, iface)


def _call(service, method, *call_args):
    bus_class, iface, name, path = service

    obj = bus_class().get_object(name, path)
    service = dbus.Interface(obj, iface)
    props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
    mainloop = gobject.MainLoop()
    state = []

    def progress_cb(iface, changed_props, invalidated_props):
        if 'Info' in changed_props:
            printf.info(changed_props['Info'])
        if 'Progress' in changed_props:
            printf.progress('%d%%' % changed_props['Progress'])
        if 'State' in changed_props and changed_props['State'] < 10:
            state.append(int(changed_props['State']))
            mainloop.quit()

    props.connect_to_signal('PropertiesChanged', progress_cb)

    getattr(service, method)(*call_args)
    try:
        mainloop.run()
        return 0 if state[0] == 0 else 1
    except KeyboardInterrupt:
        if hasattr(service, 'Cancel'):
            service.Cancel()
        printf.info(_('Aborted'))
        return 1


HELP = """
Commands:
  session               start all DBus services to let them do background work;
                        needs to be called by Sugar user after getting X11 and
                        DBus sessions
  register [URL]        process registration on sugar-server using URL or
                        default server location
  backup [URL]          rsync Journal content to URL or to sugar-server after
                        getting registration there
  restore [URL|YYYY-MM-DD]
                        rsync Journal content from URL or from sugar-server
                        after getting registration there;
                        if YYYY-MM-DD was specified, makes sence only for
                        sugar-server case, this date backup will be restored
  update                process unattended packages update
  monitor               process stats gathering; --stats-url should be passed

See http://wiki.sugarlabs.org/go/Sugar_Server_Kit/sugar-client for details."""


parser = OptionParser(
        usage='%prog [OPTIONS] [COMMAND]',
        description=_('Client side tool to interact with a school server.'),
        add_help_option=False)
parser.print_version = \
        lambda: sys.stdout.write('%s\n' % env.VERSION)
parser.add_option('-d', '--direct',
        help=_('directly calling functionality avoiding D-Bus services, ' \
                'needs to be used only for testing purposes and ' \
                'in extreme cases.'),
        action='store_true')
parser.add_option('-q', '--quiet',
        help=_('suppress any output while working'),
        action='store_true')
parser.add_option('-h', '--help',
        help=_('show this help message and exit'),
        action='store_true')
parser.add_option('-V', '--version',
        help=_('show version number and exit'),
        action='version')

util.Option.bind(parser, [
        '/etc/sugar-client.conf',
        '~/.config/sugar-client/config',
        ],
        notice=_('make sense only with --direct'))

options, args = parser.parse_args()

# Handle backwards compatibility basenames
if basename(sys.argv[0]) in ['journal-backup', 'ds-backup.sh', 'ds-backup.py']:
    command = 'backup'
elif basename(sys.argv[0]) == 'journal-restore':
    command = 'restore'
elif basename(sys.argv[0]) == 'dextrose-updater':
    command = 'update'
elif args:
    command = args.pop(0)
else:
    command = None

if not command and not options.help:
    prog = basename(sys.argv[0])
    print 'Usage: %s [OPTIONS] [COMMAND]' % prog
    print '       %s -h|--help' % prog
    print
    print parser.description
    print HELP
    exit(0)

if options.help:
    parser.print_help()
    print HELP
    exit(0)

try:
    printf.QUIET = options.quiet

    if options.direct:
        util.Option.merge(options)
        if os.geteuid():
            util.Option.merge(options, [env.profile_path('sugar-client.conf')])
    else:
        env.debug.value = options.debug

    if not env.debug.value:
        level = logging.WARNING
    elif env.debug.value == 1:
        level = logging.INFO
    else:
        level = logging.DEBUG
    logging.basicConfig(level=level,
            format='-- %(levelname)s (%(name)s) %(message)s')

    enforce('cmd_' + command in globals(),
            _('Unknown command "%s"') % command)
    exit(globals()['cmd_' + command]() or 0)
except Exception:
    printf.exception()
    exit(1)
finally:
    printf.flush_hints()
