# -*- coding: utf-8 -*-
# Dbus Ipc for experimental web interface
#
# dbus_interface.py
#
# Copyright (C) Martijn Voncken 2007 <mvoncken@gmail.com>
# Contains copy and pasted code from other parts of deluge,see deluge AUTHORS
#
# 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, 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 Street, 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.

import os
import gtk
import dbus
import deluge.common as common
from lib.pythonize import pythonize
import base64
import random
random.seed()

dbus_interface="org.deluge_torrent.dbusplugin"
dbus_service="/org/deluge_torrent/DelugeDbusPlugin"

dbus_manager = None
def get_dbus_manager(*args):
    #another way to make a singleton.
    global dbus_manager
    if not dbus_manager:
        dbus_manager = DbusManager(*args)
    return dbus_manager

class DbusManager(dbus.service.Object):
    def __init__(self, core, interface,config,config_file):
        self.core = core
        self.interface = interface
        self.config = config
        self.config_file = config_file
        self.bus = dbus.SessionBus()
        bus_name = dbus.service.BusName(dbus_interface,bus=self.bus)
        dbus.service.Object.__init__(self, bus_name,dbus_service)

    #
    #todo : add:  get_interface_version in=i,get_config_value in=s out=s
    #

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="",out_signature="as")
    def get_session_state(self):
        """Returns a list of torrent_ids in the session.
        same as 0.6, but returns type "as" instead of a pickle
        """
        torrent_list =  [str(key) for key in self.core.unique_IDs]
        return torrent_list

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="sas",out_signature="a{sv}")
    def get_torrent_status(self, torrent_id, keys):
        """return torrent metadata of a single torrent as a dict
        0.6  returns a pickle, this returns a dbus-type.
        +added some more values to the dict
        """

        torrent_id = int(torrent_id)
        # Convert the array of strings to a python list of strings
        nkeys = [str(key) for key in keys]

        state = self.core.get_torrent_state(torrent_id)
        torrent = self.core.unique_IDs[torrent_id]

        status = {
            "name": state["name"].decode('utf-8','ignore'),
            "total_size": state["total_size"],
            "num_pieces": state["num_pieces"],
            "state": state['state'],
            "user_paused": self.core.is_user_paused(torrent_id),
            "paused":state['is_paused'],
            "progress": int(state["progress"] * 100),
            "next_announce": state["next_announce"],
            "total_payload_download":state["total_payload_download"],
            "total_payload_upload": state["total_payload_upload"],
            "download_payload_rate": state["download_rate"],
            "upload_payload_rate": state["upload_rate"],
            "num_peers": state["num_peers"],
            "num_seeds": state["num_seeds"],
            "total_wanted": state["total_wanted"],
            "eta": common.estimate_eta(state),
            "ratio": self.interface.manager.calc_ratio(torrent_id,state),
            #non 0.6 values follow here:
            "tracker_status": state.get("tracker_status","?").decode('utf-8','ignore'),
            "uploaded_memory": torrent.uploaded_memory,
            "tracker":state["tracker"].decode('utf-8','ignore')
        }
        #more non 0.6 values
        for key in ["total_seeds", "total_peers","is_seed", "total_done",
                "total_download", "total_upload"
                #, "download_rate","upload_rate"
                , "num_files", "piece_length", "distributed_copies"
                ,"next_announce","queue_pos"]:
            status[key] = state[key]

        #print 'all_keys:',sorted(status.keys())

        status_subset = {}
        for key in keys:
            if key in status:
                status_subset[key] = status[key]
            else:
                print 'mbus error,no key named:', key
        return status_subset

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="as",out_signature="")
    def pause_torrent(self, torrents):
        """same as 0.6 interface"""
        for torrent_id in torrents:
            torrent_id = int(torrent_id)
            self.core.set_user_pause(torrent_id, True)

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="as", out_signature="")
    def resume_torrent(self, torrents):
        """same as 0.6 interface"""
        for torrent_id in torrents:
            torrent_id = int(torrent_id)
            self.core.set_user_pause(torrent_id, False)

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="as", out_signature="")
    def force_reannounce(self, torrents):
        """same as 0.6 interface"""
        for torrent_id in torrents:
            torrent_id = int(torrent_id)
            self.core.update_tracker(torrent_id)

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="asbb", out_signature="")
    def remove_torrent(self, torrent_ids, data_also, torrent_also):
        """remove a torrent,and optionally data and torrent
        additions compared to 0.6 interface: (data_also, torrent_also)
        """
        for torrent_id in torrent_ids:
            torrent_id = int(torrent_id)
            self.core.remove_torrent(torrent_id, bool(data_also)
                ,bool( torrent_also))

            #this should not be needed:
            gtk.gdk.threads_enter()
            try:
                self.interface.torrent_model_remove(torrent_id)
            except:
                pass

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="s", out_signature="b")
    def add_torrent_url(self, url):
        filename = fetch_url(url)
        self._add_torrent(filename)
        return True

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="s", out_signature="b")
    def queue_up(self, torrent_id):
        self.core.queue_up(int(torrent_id))
        return True

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="s", out_signature="b")
    def queue_down(self, torrent_id):
        self.core.queue_down(int(torrent_id))
        return True

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="ss", out_signature="b")
    def add_torrent_filecontent(self, name, filecontent_b64):
        """not available in deluge 0.6 interface"""
        #name = fillename without directory
        name =  name.replace('\\','/')
        name = 'deluge_' + str(random.random()) + '_'  + name.split('/')[-1]
        filename = os.path.join(self.core.config.get("default_download_path"), name)

        filecontent = base64.b64decode(filecontent_b64)
        f = open(filename,"wb") #no with statement, that's py 2.5+
        f.write(filecontent)
        f.close()
        print 'write:',filename
        self._add_torrent(filename)
        return True


    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="", out_signature="a{sv}")
    def get_config(self):
        return  self.core.config.mapping

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="s", out_signature="v")
    def get_config_value(self,key):
        return  self.core.config.mapping[pythonize(key)] #ugly!

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="a{sv}", out_signature="")
    def set_config(self, config):
        """Set the config with values from dictionary"""
        config = deluge.common.pythonize(config)
        # Load all the values into the configuration
        for key in self.core.config.keys():
            self.core.config[key] = config[key]
        self.core.apply_prefs()

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="", out_signature="v")
    def get_download_rate(self):
        return self.core.get_state()['download_rate']

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="", out_signature="v")
    def get_upload_rate(self):
        return self.core.get_state()['upload_rate']

    @dbus.service.method(dbus_interface=dbus_interface,
        in_signature="", out_signature="v")
    def get_num_connections(self):
        core_state = self.core.get_state()
        return core_state['num_connections']

    #internal
    def _add_torrent(self, filename):
        filename = unicode(filename)
        target = self.core.config.get("default_download_path")

        torrent_id = self.core.add_torrent(filename, target,
            self.interface.config.get("use_compact_storage"))

        #update gtk-ui This should not be needed!!
        gtk.gdk.threads_enter()
        try:
            self.interface.torrent_model_append(torrent_id)
        except:
            pass
        #finally is 2.5 only!
        gtk.gdk.threads_leave()

        return True

def fetch_url(url):
    import urllib

    try:
        filename, headers = urllib.urlretrieve(url)
    except IOError:
        raise Exception( "Network error while trying to fetch torrent from %s"
            % url)
    else:
        if (filename.endswith(".torrent") or
           headers["content-type"]=="application/x-bittorrent"):
            return filename
        else:
            raise Exception("URL doesn't appear to be a valid torrent file:%s"
                %  url)

    return None
