#!/usr/bin/env python
# -*- mode: python -*-

# Miro - an RSS based video player application
# Copyright (C) 2005-2010 Participatory Culture Foundation
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the OpenSSL
# library.
#
# You must obey the GNU General Public License in all respects for all of
# the code used other than OpenSSL. If you modify file(s) with this
# exception, you may extend this exception to your version of the file(s),
# but you are not obligated to do so. If you do not wish to do so, delete
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.


# This comes first.  Seriously.
from miro.plat import xlibhelper
if xlibhelper.XInitThreads() == 0:
    print "WARNING: XInitThreads() failed!"

import os.path
import sys
import string

import optparse

import pygtk
pygtk.require('2.0')

import glib
from miro.plat import upgrade
try:
    upgrade.upgrade()
except glib.GError, ge:
    # this handles when the user tries to run Miro but gconf isn't
    # started or something along those lines.  ge has the full
    # message, so we print that out.
    print "Gconf error."
    print str(ge)
    sys.exit(1)

usage = "usage: %prog [options] [torrent files] [video files]"
parser = optparse.OptionParser(usage=usage)
parser.add_option('--version',
                  dest='version', action='store_true',
                  help='Print version info.')
parser.set_defaults(version=False)

parser.add_option('--theme',
                  dest='theme', metavar='<THEME>',
                  help='Theme to use.')
parser.add_option('--frontend',
                  dest='frontend', metavar='<FRONTEND>',
                  help='Frontend to use (widgets, cli, shell).')
parser.set_defaults(frontend="widgets")

group = optparse.OptionGroup(parser, "Settings options")
group.add_option('--list',
                 action="store_true",
                 dest="list_options",
                 help="Lists all preferences that can be set by command line.")
parser.set_defaults(list_options=False)
group.add_option('--set',
                 action="append",
                 dest="set_prefs",
                 metavar="<PREF>",
                 nargs=1,
                 help="Sets a preference and saves the new value for future Miro runs.  Example: miro --set renderer=gstreamer")
parser.set_defaults(set_prefs=[])
parser.add_option_group(group)

group = optparse.OptionGroup(parser, "Debugging options")
group.add_option('--debug',
                  action="store_true",
                  help='Starts Miro in a gdb session for better debugging.')
group.add_option('--unittest',
                  dest='unittest', action='store_true',
                  help='Run unittests instead of launching the program.')
parser.set_defaults(unittest=False)
group.add_option('--convert',
                  dest='convert', action='store_true',
                  help='Command line conversion.')
parser.set_defaults(unittest=False)
group.add_option('-v',
                  dest='unittest_verbose', action='store_true',
                  help='Run unittests in verbose mode.')
parser.set_defaults(unittest_verbose=False)
group.add_option('--profile',
                  metavar='<FILE>',
                  help='Write profiling information to FILE.')
group.add_option('--bias',
                  metavar='<BIAS>', type=float,
                  help='Set profiling bias to BIAS.')
parser.set_defaults(bias=None)
group.add_option('--sync',
                  action="store_true",
                  help='Use X synchronously.')
parser.set_defaults(sync=False)
group.add_option('--gconf-name',
                  dest='gconf_name',
                  metavar='<NAME>',
                  help='Use a different name for gconf values than "miro"')
parser.set_defaults(gconf_name='miro')
parser.add_option_group(group)

(parsed_options, args) = parser.parse_args()

# Setup gconf_name early on so we can load config values
from miro.plat import options
options.gconf_name = parsed_options.gconf_name

from miro.plat import utils
utils.initialize_locale()

from miro import gtcache
gtcache.init()

# This fixes some/all problems with Python 2.6 support but should be
# re-addressed when we have a system with Python 2.6 to test with.
# (bug 11262)
if sys.version_info[0] == 2 and sys.version_info[1] > 5:
    import miro.feedparser
    import miro.storedatabase
    sys.modules['feedparser'] = miro.feedparser
    sys.modules['storedatabase'] = miro.storedatabase

from miro import config
from miro import prefs
from miro import commandline

if '--unittest' in sys.argv:
    # handle this outside optparse, because unittest has its own option set
    sys.argv.remove('--unittest')
    import logging
    logging.basicConfig(level=logging.CRITICAL)
    from miro import test
    test.run_tests()
    sys.exit(0)
    
if '--convert' in sys.argv:
    sys.argv.remove('--convert')
    from miro.plat import clconverter
    clconverter.convert(sys.argv[1:])
    sys.exit(0)
    
def load_frontend(globals_, locals_, frontend):
    try:
        _temp = __import__(frontend, globals_, locals_, ['application'], -1)
        return _temp.application
    except ImportError, ie:
        # this is goofy, but to quell messages that happen when
        # trying to find which package the frontend is in
        if "No module" not in str(ie):
            print "ImportError on %s: %s" % (frontend, ie)
        return None
    
def startup(props_to_set):
    frontend = parsed_options.frontend
    goodchars = string.letters + "."
    for c in frontend:
        if c not in goodchars:
            raise ValueError("Unknown frontend: %s" % parsed_options.frontend)

    application = None
    attempts = [
        "miro.plat.frontends.%s" % frontend,
        "miro.frontends.%s" % frontend,
        "%s" % frontend
        ]

    for att in attempts:
        application = load_frontend(globals(), locals(), att)
        if application is not None:
            break
    else:
        raise ValueError("Cannot load frontend: %s" % frontend)

    application.run_application(props_to_set, parsed_options.theme)

def get_keyval(keyval):
    key, val = keyval.split("=")
    if not key in options.PREFERENCES:
        print "Option '%s' does not exist." % (key)
        print "Exiting...."
        sys.exit(1)

    p = options.PREFERENCES[key]

    try:
        defaulttype = type(p.default)
        if defaulttype == unicode or defaulttype == str:
            pass
        elif defaulttype == float:
            val = float(val)
        elif defaulttype == int:
            val = int(val)
        elif defaulttype == bool:
            if val[0].lower() in ["t", "y", "1" ]:
                val = True
            else:
                val = False
    except Exception, e:
        print "Problem converting value to correct type: %s" % e
        print "Exiting...."
        sys.exit(1)

    return p, val

def startapp():
    # Create miro directories in the user's home
    support_dir = config.get(prefs.SUPPORT_DIRECTORY)
    if not os.path.exists(support_dir):
        os.makedirs(support_dir)

    props_to_set = []

    for mem in parsed_options.set_prefs:
        p, val = get_keyval(mem)
        props_to_set.append((p, val))

    options.shouldSyncX = parsed_options.sync
    options.frontend = parsed_options.frontend
    if parsed_options.theme:
        options.themeName = parsed_options.theme
        print 'Theme is %s' % parsed_options.theme

    for i in xrange(len(args)):
        if args[i].startswith('file://'):
            args[i] = args[i][len('file://'):]

    commandline.set_command_line_args(args)
    startup(props_to_set)

def print_version():
    print """
Miro (%s)

Miro comes with ABSOLUTELY NO WARRANTY.

This is free software, and you are welcome to redistribute it under certain
conditions.  See license.txt for details.
""" % config.get(prefs.APP_VERSION)

if parsed_options.version:
    print_version()

elif parsed_options.list_options:
    print_version()

    print "Options that can be set:"
    for p in options.PREFERENCES.values():
        print "   %s\n      %s\n" % (p.alias, p.helptext)

elif parsed_options.profile:
    import sys
    try:
        import cProfile as profile
        sys.modules['profile'] = profile
    except ImportError:
        import profile
    from miro import eventloop
    def main():
        startapp()
    if (parsed_options.bias):
        profile.Profile.bias = parsed_options.bias
    eventloop.profile_file = parsed_options.profile
    profile.run ('main()', parsed_options.profile)

else:
    try:
        import dbus
        from miro.plat import onetime
        try:
            onetime.OneTime()
            startapp()
        except dbus.NameExistsException:
            bus = dbus.SessionBus()
            proxy_obj = bus.get_object(
                'org.participatoryculture.dtv.onetime',
                '/org/participatoryculture/dtv/OneTime')
            iface = dbus.Interface(
                proxy_obj, 'org.participatoryculture.dtv.OneTimeIFace')
            for i, arg in enumerate(args):
                args[i] = arg.decode('latin1')
            dbusargs = dbus.Array(args, signature="s")

            try:
                iface.handle_args(dbusargs)
            except dbus.DBusException:
                startapp()
        except dbus.DBusException:
            startapp()
    except ImportError:
        startapp()
