#!/usr/bin/python
# -*- coding: utf-8 -*-
### BEGIN LICENSE
# Copyright (C) 2010 Benjamin Elbers <elbersb@gmail.com>
#This program is free software: you can redistribute it and/or modify it 
#under the terms of the GNU General Public License version 3, as published 
#by the Free Software Foundation.
#
#This program is distributed in the hope that it will be useful, but 
#WITHOUT ANY WARRANTY; without even the implied warranties of 
#MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.
### END LICENSE

import sys
import os
from os.path import join, isdir, exists
import re
import subprocess
import time
import hashlib
import optparse
import logging
import dbus, dbus.service, dbus.glib
import simplejson as json

try:
    from gtk import events_pending, main_iteration
    import gobject
    gobject.threads_init()
except:
    print "PyGTK/GTK is missing."
    sys.exit(-1)

# Options and configuration
def parse_opts():
    usage = "usage: %prog [options]"

    oparse = optparse.OptionParser(usage=usage)

    oparse.add_option("-v", "--verbose", action="store_true",
                      help="Verbose messages", default=False)
    oparse.add_option("-d", "--debug", action="store_true",
                      help="Debug messages", default=False)
    oparse.add_option("-l", "--link", help="Add a link")

    opts, rest = oparse.parse_args()
    return opts
    
opts = parse_opts()

if opts.debug:
    logging.root.setLevel(logging.DEBUG)
elif opts.verbose:
    logging.root.setLevel(logging.INFO)
link = opts.link

# Setup path

launch_dir = os.path.abspath(sys.path[0])
source_tree = os.path.join(launch_dir, "..", "otrverwaltung")

# If we were invoked from source directory add that as the
# preferred module path ...
if os.path.exists(os.path.join(source_tree, "path.py")):        
    local = True
    logging.info("Running from source tree; adjusting path")
    sys.path.insert(0, os.path.realpath(os.path.dirname(source_tree)))
    locale_dir = os.path.join(launch_dir, "..", "locale")
else:
    local = False
    logging.debug("Assuming path is correct")
    locale_dir = "/usr/share/locale"
    
    try:
        import otrverwaltung
    except ImportError: # possible on systems where pycentral is not installed
        sys.path.insert(0, "/usr/share/pyshared")           
    
import gettext, locale
import gtk.glade
gettext.install('otrverwaltung', locale_dir, True)
    
locale.setlocale(locale.LC_ALL, '')
for module in gettext, gtk.glade:
     module.bindtextdomain('otrverwaltung', locale_dir)
     module.textdomain('otrverwaltung')    
    
# intern
from otrverwaltung.gui import gui
from otrverwaltung.pluginsystem import PluginSystem
from otrverwaltung.actions import actions
from otrverwaltung.config import Config
from otrverwaltung import fileoperations
import otrverwaltung.cutlists as cutlists_management
from otrverwaltung.constants import Section, Action, Cut_action, DownloadStatus
from otrverwaltung.planning import Planning
from otrverwaltung.downloader import Download
from otrverwaltung.conclusions import ConclusionsManager
from otrverwaltung import path

class App:
    """ Hauptklasse des Programms. """
    
    section = Section.OTRKEY
    """ Die aktuell angezeigt `~constants.Section`. Zum Ändern sollte die Funktion `~otr.App.show_section` verwendet werden. """
    
    def __init__(self):        
   
        configuration = {
            'general': {
                'folder_new_otrkeys': '',
                'folder_uncut_avis': '',
                'folder_cut_avis': '',
                'folder_trash_otrkeys': '',
                'folder_trash_avis': '',
                'folder_archive': '',
                'otrkey_search': 'http://otrkeyfinder.com/?search=',
                'decoder': '',
                'save_email_password': False,
                'email': '',
                'password': '',
                'verify_decoded': False,
                'cut_avis_by': 'avidemux',
                'cut_hqs_by':           '',
                'cut_mp4s_by': 'avidemux',
                'cut_avis_man_by': 'avidemux',
                'cut_hqs_man_by': '',
                'cut_mp4s_man_by': 'avidemux',
                'server': 'http://cutlist.at/',
                'cut_action': Cut_action.ASK,
                'delete_cutlists': True,
                'smart': True,
                'choose_cutlists_by': 0, # 0 = size, 1=name
                'cutlist_username': '',
                'mplayer': 'mplayer',
                'planned_items': '',
                'rename_cut': True,
                'rename_schema': '{titel} vom {tag}. {MONAT} {jahr}, {stunde}-{minute} ({sender})',
                'cutlist_mp4_as_hq': False, # for mp4s, when searching cutlist by name, add an HQ --> Name.HQ.mp4
                'show_bottom': False,                
                'cutlist_hash': hashlib.md5(str(time.time())).hexdigest(),
                'window_settings': '',
                'pre_cut_show': 11,
                'after_cut_show': 5
            },
            'downloader': {
                'resume_on_startup': True,
                'preferred_downloader' : '',
                'aria2c': ['aria2c', "--allow-overwrite=true", "--continue"],
                'wget': ['wget'],
                'aria2c_torrent': ["aria2c", "--check-integrity=true", "--continue" ]
            },
            'plugins': {
                'enabled': 'Play',
                'config': {} # used as plugin config storage
            }
                         
        }
        
        if local:
            self.config = Config(os.path.join(launch_dir, "..", "conf"), configuration)    
        else:
            # TODO: Remove this line after one release
            configuration = self.import_old_config(configuration)
                    
            self.config = Config(path.get_config_dir("conf"), configuration)

        self.__search_text = ""
        self.locked = False
            
        # regex
        self.uncut_video = re.compile('.*_([0-9]{2}\.){2}([0-9]){2}_([0-9]){2}-([0-9]){2}_.*_([0-9])*_TVOON_DE.mpg\.(avi|HQ\.avi|HD\.avi|mp4)$')
        self.cut_video = re.compile('.*(avi|mp4|mkv|wmv)$')
       
        # load gui
        self.__gui = gui.Gui(self)  # for backwards compability
        self.gui = self.__gui

        self.config.load()
    
        if self.config.get('general', 'window_settings'):
            maximize, width, height = self.config.get('general', 'window_settings').split(',')
            if int(maximize):
                self.__gui.main_window.maximize()            
            self.__gui.main_window.resize(int(width), int(height))
    
        # load downloads
        downloads = []
        try:
            file = open(path.get_storage_dir("data"), 'r')
            print file
            downloads = json.loads(file.read())['downloads']
        except (IOError, ValueError), message:
            print "Can't read downloads: %s" % message

        for json_download in downloads:
            download = Download(self, self.config)
            download.from_json(json_download)
            self.gui.main_window.treeview_download.add_objects(download)

            if self.config.get('downloader', 'resume_on_startup') and download.information['status'] in [DownloadStatus.RUNNING, DownloadStatus.SEEDING]:
                download.start(force=True)

        # conclusions_manager
        self.conclusions_manager = ConclusionsManager(self)

        # load plugins
        if local:
            plugin_paths = [path.getdatapath('plugins')]
        else:
            plugin_paths = path.get_plugin_paths()
    
        self.plugin_system = PluginSystem(self, self.__gui, plugin_paths, self.config.get('plugins', 'enabled'), self.config.get('plugins', 'config'))    
        
        self.show_section(Section.DOWNLOAD)
        
        self.planned_broadcasts = Planning()
        self.planned_broadcasts.read_config(self.config.get('general', 'planned_items'))
        for broadcast in self.planned_broadcasts:
            self.__gui.main_window.append_row_planning(broadcast)
        
        self.__gui.main_window.broadcasts_badge()                           
       
    def import_old_config(self, configuration):
        # TODO: Remove after one release          
        
        # try to move plugins
        import shutil
        src = join(os.environ.get('HOME'), '.otr-verwaltung', 'plugins')
        dst = path.get_storage_dir("plugins")
        try:
            shutil.copytree(src, dst)                    
        except:
            pass
        
        try:
            config = join(os.environ.get('HOME'), '.otr-verwaltung', 'conf')            
            config = open(config, 'r')
        except:
            return configuration
                       
        # read file
        for line in config:
            if not '=' in line:
                continue
            key, value = line.split('=', 1)
            key, value = key.strip(), value.strip()
            
            if key in configuration['general'].keys():
                datatype = type(configuration['general'][key])
                
                #print "%5s: %15s=%15s" % (datatype, key.strip(), value)
                if datatype == bool:
                    value = bool(int(value))
                else:
                    value = datatype(value)
                
                print "IMPORT: %s of %s" % (key, datatype)
                configuration['general'][key] = value

            elif key=="enabled_plugins":
                configuration['plugins']['enabled'] = value      
                
        os.rename(join(os.environ.get('HOME'), '.otr-verwaltung'), join(os.environ.get('HOME'), '.otr-verwaltung_ALT'))
           
        return configuration 
             
    ### 
    ### Show sections
    ###
    
    def show_section(self, section):
        """ Zeigt eine der verschiedenen `Sections <constants.Section>` an. 
                        
            * aktualisiert einen Treeview und zeigt den korrekten an
            * setzt die aktuelle `~otr.App.section`
            * zeigt die korrekten Toolbuttons an """

        # set current section
        self.section = section
        
        # set toolbar
        self.__gui.main_window.set_toolbar(section)
        
        self.section_info = {        
            Section.PLANNING: 'scrolledwindow_planning',
            Section.DOWNLOAD: 'scrolledwindow_download',
            Section.OTRKEY: 'scrolledwindow_files',
            Section.VIDEO_UNCUT: 'scrolledwindow_files',
            Section.VIDEO_CUT: 'scrolledwindow_files',
            Section.ARCHIVE: 'scrolledwindow_files',
            Section.TRASH: 'scrolledwindow_files'
        }
                
        self.__gui.main_window.clear_files()
        files = []
        
        treeview = self.section_info[section]
        self.__gui.main_window.show_treeview(treeview)
                
        if section == Section.OTRKEY:
            files = self.__section_otrkey()
        
        elif section == Section.VIDEO_UNCUT:
            files = self.__section_video_uncut()   

        elif section == Section.VIDEO_CUT:
            files = self.__section_video_cut()   

        elif section == Section.TRASH:
            files = self.__section_trash()   

        elif section == Section.ARCHIVE: 
            # returns NO files       
            self.__section_archive()

        if len(files) > 0: # this is not executed when the section is "Archive"
            files.sort() 
            
            # put filenames into treestore
            for f in files:
                # TODO: don't show files if in use
                self.__append_row_treeview_files(None, f)

    # helper for different sections   
    def __section_otrkey(self):
        path = self.config.get('general', 'folder_new_otrkeys')
        
        if path == "":
            return []
        
        files = [join(path, f) for f in os.listdir(path) if f.endswith(".otrkey") and self.search(f)]                           
            
        return files
         
    def __section_video_uncut(self):
        path = self.config.get('general', 'folder_uncut_avis')
        
        files = [join(path, f) for f in os.listdir(path) if self.uncut_video.match(f) and self.search(f)]
            
        return files
        
    def __section_video_cut(self):    
        path = self.config.get('general', 'folder_cut_avis')
        
        files = []                
        for f in os.listdir(path):
            if not self.uncut_video.match(f):
                if self.cut_video.match(f):
                    if self.search(f):
                        files += [join(path, f)]
        
        return files
        
    def __section_trash(self):
        path_otrkeys = self.config.get('general', 'folder_trash_otrkeys')
        path_avis = self.config.get('general', 'folder_trash_avis')
                    
        files = [join(path_otrkeys, f) for f in os.listdir(path_otrkeys) if f.endswith('.otrkey') and self.search(f)]
        files += [join(path_avis, f) for f in os.listdir(path_avis) if self.cut_video.match(f) and self.search(f)]
                
        return files

    def __section_archive(self):
        path = self.config.get('general', 'folder_archive')
        
        self.__tree(None, path)
                 
    # recursive function for archive to add folders and files with a tree structure
    def __tree(self, parent=None, path=None):              
        if parent != None:            
            dir = self.__gui.main_window.builder.get_object('treeview_files').get_model().get_value(parent, 0)
        else:  # base path (archive directory)
            dir = path

        files = []
        files = os.listdir(dir)            

        for file in files:
            full_path = join(dir, file)
            
            if isdir(full_path):                
                iter = self.__append_row_treeview_files(parent, full_path)
                self.__tree(iter)
            else:
                if self.cut_video.match(file):
                    if self.search(file):
                        self.__append_row_treeview_files(parent, full_path)

    ###
    ### Helpers
    ###
    
    def rename_by_schema(self, filename, schema=""):
        """ Gibt den nach dem angegebenen Schema umbenannten Dateinamen zurück. Wird `schema` leer gelassen, so wird das eingestellte Schema verwendet. """
    
        if schema == "":
            schema = self.config.get('general', 'rename_schema')        
        
        if self.uncut_video.match(filename):           
            
            parts = filename.split('_')
            parts.reverse()

            titel_list = parts[6:len(parts)]
            titel_list.reverse()
            titel = " ".join(titel_list)
            titel_mit = "_".join(titel_list)
            titel_dot = ".".join(titel_list)

            stunde, minute = parts[4].split('-')
            jahr, monat, tag = parts[5].split('.')           
            monatsname = time.strptime(monat, '%m')
            monatsname = time.strftime('%B', monatsname)

            sender_gross = parts[3].capitalize()

            format = parts[0]
            
            if 'mp4' in format:
                format = 'mp4'
            elif 'HQ' in format:
                format = 'HQ'
            elif 'HD' in format:
                format = 'HD'
            else:
                format = 'avi'

            values = {
                'titel' : titel,
                'titel_' : titel_mit,
                'titel.' : titel_dot,
                'sender' : parts[3],
                'SENDER': sender_gross,
                'tag': tag,
                'monat': monat,
                'MONAT': monatsname,
                'jahr': jahr,
                'stunde': stunde,
                'minute': minute,
                'dauer' : parts[2],
                'format' : format
            }
             
            for token, value in values.iteritems():
                schema = schema.replace('{%s}' % token, value)                            
                
            return schema
        else:         
            return filename
     
    def __append_row_treeview_files(self, parent, filename):        
        iter = self.__gui.main_window.append_row_files(parent, filename, fileoperations.get_size(filename), fileoperations.get_date(filename), isdir(filename))
        return iter
     
    
    ### 
    ### Search
    ### 
                      
    def start_search(self, search):
        """ #FIXME """

        self.__search_text = search.lower()
   
        # create dict of counts
        counts = {}

        for method, section in [(self.__section_otrkey, Section.OTRKEY),
                                (self.__section_video_uncut, Section.VIDEO_UNCUT),
                                (self.__section_video_cut, Section.VIDEO_CUT),
                                (self.__section_trash, Section.TRASH)]:
            items = method()   
            count = len(items)
            counts[section] = count

        # archive 
        files = []       
        for root, dirs, wfiles in os.walk(self.config.get('general', 'folder_archive')):
            for f in wfiles:                
                if self.cut_video.match(f) and self.search(f):
                    files += [join(root, f)]

        count = len(files)
        counts[Section.ARCHIVE] = count

        self.show_section(self.section)                   
        
        return counts
    
    def stop_search(self):
        """ #FIXME """
        
        self.__search_text = ""
        self.show_section(self.section)
        
    def search(self, f):
        """ #FIXME """
        
        if self.__search_text == "":
            return True
        else:    
            if self.__search_text in f.lower():
                return True
            else:
                return False
        
    ###
    ### Actions
    ###        
    
    def perform_action(self, chosen_action, cut_action=None):
        
        action = actions.get_action(chosen_action, self, self.__gui)        
        
        if chosen_action in [Action.PLAN_EDIT, Action.PLAN_SEARCH, Action.PLAN_REMOVE]:
            model, selected_rows = self.__gui.main_window.builder.get_object('treeview_planning').get_selection().get_selected_rows()
            
            if len(model) == 0: return            
            
            if len(selected_rows) == 0:            
                if chosen_action != Action.PLAN_EDIT and self.__gui.question_box("Es ist keine Sendung markiert! Sollen alle Sendungen verarbeitet werden?"):
                    selected_rows = [row.path for row in selected_rows]
                    for row in model:
                        selected_rows.append(row.path)
                else:
                    return
           
            selected_rows = [model.get_iter(path) for path in selected_rows]
            self.locked = True
            action.do(selected_rows)
                     
        elif chosen_action in [Action.DECODE, Action.CUT, Action.DECODEANDCUT, Action.RENAME, Action.REAL_DELETE, Action.RESTORE, Action.DELETE, Action.ARCHIVE]:
            filenames = self.__gui.main_window.get_selected_filenames()

            if len(filenames) == 0:
                model = self.__gui.main_window.builder.get_object('treeview_files').get_model()
    
                if len(model) != 0 and self.__gui.question_box("Es ist keine Datei markiert! Sollen alle Dateien verarbeitet werden?"):
                    filenames = [row[0] for row in model]
                else:
                    return

            self.locked = True
            if chosen_action in [Action.DECODE, Action.CUT, Action.DECODEANDCUT]:
                action.do(chosen_action, filenames, cut_action)
            else:
                action.do(filenames)

        elif chosen_action in [Action.DOWNLOAD_START, Action.DOWNLOAD_STOP, Action.DOWNLOAD_REMOVE]:
            model, selected_rows = self.__gui.main_window.treeview_download.get_selection().get_selected_rows()
                      
            if len(selected_rows) == 0:
                return
           
            downloads = [model.get_value(model.get_iter(path), 0) for path in selected_rows]
            action.do(downloads)

        elif chosen_action == Action.NEW_FOLDER:
            filenames = self.__gui.main_window.get_selected_filenames()
            self.locked = True
            if len(filenames) == 0:
                action.do(self.config.get('general', 'folder_archive'))
            else:
                action.do(filenames[0])

        else:
            self.locked = True
            action.do()

        self.locked = False
        
        # update view?
        if action.update_list:            
            self.__gui.main_window.do_search(self.__search_text)
            self.show_section(self.section)            
    
    def play_file(self, filename):
        """ #FIXME """        
        
        subprocess.Popen(['xdg-open', filename])
    
    def __show(self, cuts, video_filename, edl_subtitles_cb):        
        f_edl = open(join(self.config.get('general', 'folder_new_otrkeys'), ".tmp.edl"), "w")
        f_sub = open(join(self.config.get('general', 'folder_new_otrkeys'), ".tmp.sub"), "w")    
        
        # get cuts in seconds
        
        edl_subtitles_cb(f_edl, f_sub, cuts)        
        
        f_edl.close()
        f_sub.close()
        
        f_edl = join(self.config.get('general', 'folder_new_otrkeys'), ".tmp.edl")
        f_sub = join(self.config.get('general', 'folder_new_otrkeys'), ".tmp.sub")
            
        p = subprocess.Popen([self.config.get('general', 'mplayer'), "-osdlevel", "3", "-edl", f_edl, "-sub", f_sub, video_filename])
       
        # wait
        while p.poll() == None:
            time.sleep(1)
            while events_pending():
                main_iteration(False)
                
        fileoperations.remove_file(f_edl)
        fileoperations.remove_file(f_sub)
        
    def show_cuts(self, video_filename, cutlist):
        """ #FIXME """
        
        def edl_subtitles_cb(f_edl, f_sub, cuts):            
            diff = self.config.get('general', 'pre_cut_show')
            pre_diff = self.config.get('general', 'after_cut_show')
            
            sub_count = 0

            f_edl.write("0 ")      

            for count, (start, duration) in enumerate(cuts):
                end = start + duration

                f_edl.write("%s 0\n" % (start - diff))
                f_edl.write("%s %s 0\n" % (start + pre_diff, end - diff))
                f_edl.write("%s " % (end + pre_diff))

                if count == 0:
                    for second in range(diff):
                        sub_count += 1
                        f_sub.write("%s\n" % sub_count)
                        f_sub.write("%s --> %s\n" % (self.format_seconds(start-diff+second), self.format_seconds(start-diff+second+1)))
                        f_sub.write("Sendung beginnt in %s Sekunden...\n\n" % str(diff - second))
                else:
                    for second in range(diff):
                        sub_count += 1
                        f_sub.write("%s\n" % sub_count)
                        f_sub.write("%s --> %s\n" % (self.format_seconds(start-diff+second), self.format_seconds(start-diff+second+1)))
                        f_sub.write("Werbung #%i endet in %s Sekunden...\n\n" % (count, str(diff - second)))

                if count == (len(cuts) - 1):
                    for second in range(diff):
                        sub_count += 1
                        f_sub.write("%s\n" % sub_count)
                        f_sub.write("%s --> %s\n" % (self.format_seconds(end-diff+second), self.format_seconds(end-diff+second+1)))
                        f_sub.write("Sendung endet in %s Sekunden...\n\n" % str(diff - second))
                else:
                    for second in range(diff):
                        sub_count += 1
                        f_sub.write("%s\n" % sub_count)
                        f_sub.write("%s --> %s\n" % (self.format_seconds(end-diff+second), self.format_seconds(end-diff+second+1)))
                        f_sub.write("Werbung #%i beginnt in %s Sekunden...\n\n" % (count + 1, str(diff - second)))

            f_edl.write("50000 0")
         
        error = cutlist.read_cuts()
                
        if error:
            self.__gui.message_error_box(error)
            return           

        self.__show(cutlist.cuts_seconds, video_filename, edl_subtitles_cb)            
    
    def show_cuts_after_cut(self, video_filename, cutlist):
        """ #FIXME """
        
        def edl_subtitles_cb(f_edl, f_sub, cuts):                    
            diff = self.config.get('general', 'pre_cut_show')
            pre_diff = self.config.get('general', 'after_cut_show')
                        
            length = 0
            sub_count = 0
                            
            for count, (start, duration) in enumerate(cuts):
                f_edl.write("%s %s 0\n" % (pre_diff + length, length + duration - diff))
                length += duration
                
                # vor dem schnitt:
                for second in range(diff):
                    sub_count += 1
                    f_sub.write("%s\n" % sub_count)
                    f_sub.write("%s --> %s\n" % (self.format_seconds(length - diff + second), self.format_seconds(length - diff + second + 1)))
                    if count == len(cuts) - 1:
                        f_sub.write("Sendung endet in %s Sekunden...\n\n" % str(diff - second))
                    else:
                        f_sub.write("Schnitt in %s Sekunden...\n\n" % str(diff - second))


        error = cutlist.read_cuts()
                
        if error:
            self.__gui.message_error_box(error)
            return           

        self.__show(cutlist.cuts_seconds, video_filename, edl_subtitles_cb)
    
    def format_seconds(self, seconds):
        """ #FIXME """
        
        hrs = seconds / 3600       
        leftover = seconds % 3600
        mins = leftover / 60
        secs = leftover % 60
        ms = int(seconds) - seconds
        
        return "%02d:%02d:%02d,%03d" % (hrs, mins, secs, ms)
                      
    def run(self, link=None):
        """ #FIXME """
        
        self.__gui.main_window.show()        

        if self.config.get('general', 'folder_new_otrkeys') == "":      
            self.__gui.message_info_box("Dies ist offenbar das erste Mal, dass OTR-Verwaltung gestartet wird.\n\nEs müssen zunächst einige wichtige Einstellungen vorgenommen werden. Klicken Sie dazu auf OK.")
            self.__gui.preferences_window.show()
               
        if link:
            self.add_download_link(link)
               
        self.__gui.run()
        
        # save downloads
        downloads = []
        for row in self.gui.main_window.treeview_download.liststore:
            downloads.append(row[0].to_json())

        try:
            file = open(path.get_storage_dir("data"), 'w')
            file.write(json.dumps({'downloads': downloads}))
            file.close()
        except IOError, message:
            print _("Can't write downloads: %s") % message

        # kill downloads
        for row in self.gui.main_window.treeview_download.liststore:
            row[0].stop()

        # write to config
        self.config.set('general', 'planned_items', self.planned_broadcasts.get_config())            
        enabled, config = self.plugin_system.get_config()
        self.config.set('plugins', 'enabled', enabled)
        self.config.set('plugins', 'config', config)

        maximized = str(int(self.__gui.main_window.maximized))
        width = str(self.__gui.main_window.size[0])
        height = str(self.__gui.main_window.size[1])
        
        settings = ','.join([maximized, width, height])
        self.config.set('general', 'window_settings', settings)
    
    def add_download_link(self, link):
        action = actions.get_action(Action.DOWNLOAD_ADD_LINK, self, self.__gui)
        action.do(link)        
 
class OTRVerwaltungService(dbus.service.Object):
    def __init__(self, app):
        self.app = app
        bus_name = dbus.service.BusName('elbersb.otrverwaltung', bus = dbus.SessionBus())
        dbus.service.Object.__init__(self, bus_name, '/elbersb/otrverwaltung')

    @dbus.service.method(dbus_interface='elbersb.otrverwaltung')
    def show_window(self):
        self.app.gui.main_window.present()
        
    @dbus.service.method(dbus_interface='elbersb.otrverwaltung')        
    def add_link(self, link):        
        self.app.add_download_link(link)        
        
if __name__ == "__main__":   
    if dbus.SessionBus().request_name("elbersb.otrverwaltung") != dbus.bus.REQUEST_NAME_REPLY_PRIMARY_OWNER:
        print "OTR-Verwaltung läuft bereits. Zeige Fenster."
        dbus.SessionBus().get_object("elbersb.otrverwaltung", "/elbersb/otrverwaltung").get_dbus_method("show_window")()
        if link:            
            dbus.SessionBus().get_object("elbersb.otrverwaltung", "/elbersb/otrverwaltung").get_dbus_method("add_link")(link)            
    else:      
        app = App()
        service = OTRVerwaltungService(app)
        app.run(link)
        app.config.save()
