#!/usr/bin/env python

# Miro - an RSS based video player application
# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
# 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 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: miro [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('--window',
                  dest='window',
                  metavar='<WINDOW>',
                  help='<width>x<height> dimensions for the main window.')
parser.add_option_group(group)

group = optparse.OptionGroup(parser, "Tools")
group.add_option('--convert',
                 dest='convert', action='store_true',
                 help='Command line conversion.')
parser.set_defaults(convert=False)
parser.add_option_group(group)

group = optparse.OptionGroup(parser, "Debugging options")
group.add_option('--gdb',
                 action='store_true',
                 help='Runs Miro under gdb.')
group.add_option('--debug',
                 dest='debugmode',
                 metavar='<YESNO>',
                 help='Puts Miro in debug mode for easier debugging.')
parser.set_defaults(debugmode="")
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('--g-fatal-warnings',
                 action="store_true")
# NOTE: we don't actually do anything with g-fatal-warnings, but it gets
# picked up by the GTK code.
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')
group.add_option('--home',
                 dest='user_home',
                 metavar='<DIR>',
                 help='Like setting $HOME env var, but only in Miro')
parser.set_defaults(user_home='~/')
group.add_option('--ignore-onetime',
                 dest='ignore_onetime',
                 action='store_true',
                 help="Don't check if another Miro is already running.")
parser.set_defaults(ignore_onetime=False)
parser.add_option_group(group)

group = optparse.OptionGroup(parser, "Unittest options")
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('-v',
                 dest='unittest_verbose', action='store_true',
                 help='Run unittests in verbose mode.')
parser.set_defaults(unittest_verbose=False)
group.add_option('--failfast',
                  dest='unittest_failfast', action='store_true',
                  help='Run unittests and stop on first failure.')
parser.set_defaults(unittest_failfast=False)
parser.add_option_group(group)

(parsed_options, args) = parser.parse_args()

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

from miro.plat import utils
utils.initialize_locale()

utils.register_exec_prefix()

from miro import bootstrap
bootstrap.bootstrap()

# 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 app
from miro import prefs
from miro import commandline
from miro import startfrontend

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 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 check_existing_instance_running(args):
    """Check if there is an existing instance of Miro running.

    If so, args will be passed to that instance to open and we will return
    True.  The caller should then quit
    """
    try:
        import dbus
    except ImportError:
        return False

    from miro.plat import onetime
    try:
        onetime.OneTime()
    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")
        return True
    except dbus.DBusException:
        # Should we log an error here?  Probably yes, but the logging code
        # hasn't been set up, let's skip it for now.
        pass
    return False

def startapp():
    # set debugmode
    if parsed_options.debugmode:
        if parsed_options.debugmode.lower() in ("y", "yes"):
            app.debugmode = True
        else:
            app.debugmode = False

    # create miro directories in the user's home
    support_dir = app.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

    if parsed_options.window:
        try:
            width, height = [int(mem) for mem in parsed_options.window.split("x")]
            width = max(800, width)
            height = max(600, height)
            options.override_dimensions = (width, height)
        except ValueError:
            print "'%s' is not a valid value." % parsed_options.window
            print "Usage: --window=<width>x<height>"
            sys.exit(1)

    commandline.set_command_line_args(args)
    startfrontend.run_application(parsed_options.frontend, props_to_set,
            parsed_options.theme)


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.
""" % app.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:
    should_start = True
    if not parsed_options.ignore_onetime:
        if check_existing_instance_running(args):
            should_start = False
    if should_start:
        startapp()
