#!/usr/bin/python
# coding=utf-8
# Copyright (C) 2008 Valmantas Paliksa <walmis at balticum-tv dot lt>
# Copyright (C) 2008 Tadas Dailyda <tadas at dailyda dot com>
#
# Licensed under the GNU General Public License Version 3
#
# 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, sys

#support running uninstalled
_dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if os.path.exists(os.path.join(_dirname,"ChangeLog")):
	sys.path.insert(0, _dirname)
	
import gtk
import gobject
import dbus.glib
from optparse import OptionParser
import gettext
import urllib
import time

from blueman.bluez.Adapter import Adapter
from blueman.main.Device import Device
from blueman.main.FakeDevice import FakeDevice
from blueman.bluez.Manager import Manager
from blueman.Functions import *
from blueman.Constants import *
from blueman.gui.DeviceSelectorDialog import DeviceSelectorDialog
from blueman.main.SpeedCalc import SpeedCalc
from blueman.main.AppletService import AppletService

from blueman.ods.OdsManager import OdsManager

enable_rgba_colormap()

class Sender(gobject.GObject):
	__gsignals__ = {
		'result' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
	}
	def __init__(self, device, adapter, files):
		gobject.GObject.__init__(self)
		self.Builder = gtk.Builder()
		self.Builder.set_translation_domain("blueman")
		self.Builder.add_from_file(UI_PATH +"/send-dialog.ui")
		self.window = self.Builder.get_object("window")
		
		self.l_dest = self.Builder.get_object("l_dest")
		self.l_file = self.Builder.get_object("l_file")
		
		self.pb = self.Builder.get_object("pb")
		
		self.b_cancel = self.Builder.get_object("b_cancel")
		self.b_cancel.connect("clicked", self.on_cancel)
		
		self.pb.props.text = _("Connecting")
		
		self.device = device
		self.adapter = Adapter(adapter)
		self.files = files
		self.session = None
		
		self.total_bytes = 0
		self.total_transferred = 0
		
		self._last_bytes = 0
		self._last_update = 0
		
		self.error_dialog = None
		self.cancelling = False
		
		#bytes transferred on a current transfer
		self.transferred = 0
		
		self.speed = SpeedCalc(6)

		for i in range(len(self.files)-1,-1,-1):
			f = self.files[i]
			match = re.match("file://(.*)", f)
			if match:
				f = self.files[i] = urllib.unquote(match.groups(1)[0])
				
			if os.path.exists(f) and not os.path.isdir(f):
				f =  os.path.abspath(f)
				self.total_bytes += os.path.getsize(f)
			else:
				self.files.remove(f)

		
		self.num_files = len(self.files)
		try:
			self.manager = OdsManager()
		except:
			d = gtk.MessageDialog(  self.window,
			type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK)
			d.props.text = _("obex-data-server not available")
			d.props.secondary_text = _("obex-data-server is probably not installed")
			d.run()
			d.destroy()
			exit(1)
		
		if self.num_files == 0:
			exit(1)
		
		self.l_file.props.label = os.path.basename(self.files[-1])
		
		self.manager.GHandle("session-created", self.on_session_created)
		self.manager.GHandle("session-destroyed", self.on_session_destroyed)
		
		print "Sending to", device.Address
		self.l_dest.props.label = device.Alias
		
		self.create_session()
		
		self.window.show()

		
		
	def create_session(self):
		dprint("Creating session")
		def on_error(msg):
			dprint("Failed to create session")
			d = gtk.MessageDialog(  self.window,
			type=gtk.MESSAGE_ERROR, buttons=(gtk.BUTTONS_CLOSE))
			d.props.text = _("Error occurred")
			d.props.icon_name = "blueman"
			d.props.secondary_text = str(msg).split(":")[1].strip(" ")
		
			resp = d.run()
			d.destroy()
			exit(1)
		
		props = self.adapter.GetProperties()
		self.manager.create_session(self.device.Address, props["Address"], error_handler=on_error)
		
	def on_cancel(self, button):
		def reply(*args):
			self.session.Disconnect()
			self.emit("result", False)
		
		self.pb.props.text = _("Cancelling")
		if button:
			button.props.sensitive = False
		
		if self.session:
			if self.session.Connected:
				self.session.Cancel(reply_handler=reply, error_handler=reply)
			else:
				print self.session.object_path
				self.manager.CancelSessionConnect(self.session.object_path)
				self.emit("result", False)
		else:
			self.emit("result", False)
			
		
		
	def on_transfer_started(self, session, filename, path, size):
		dprint("transfer started")
		
		#first transfer
		if self.total_transferred == 0:
			self.pb.props.text = _("Sending File") + (" %(0)s/%(1)s (%(2).2f %(3)s/s) " + _("ETA:") + " %(4)s") % {"1": self.num_files, 
										"0": (self.num_files - len(self.files)+1),
										"2": 0.0,
										"3": "B/s",
										"4": "∞" }
		
		self.l_file.props.label = filename
		self._last_bytes = 0
		self.transferred = 0
		
		
	def on_transfer_progress(self, session, progress):
		self.transferred = progress
		if self._last_bytes == 0:
			self.total_transferred += progress
		else:
			self.total_transferred += (progress - self._last_bytes) 
		
		self._last_bytes = progress
		
		tm = time.time()
		if tm - self._last_update > 0.5:
			spd = self.speed.calc(self.total_transferred)
			(size, units) = format_bytes(spd)
			try:
				x = ((self.total_bytes-self.total_transferred) / spd)+1
				if x > 60:
					x = x / 60
					eta = ngettext("%.0f Minute", "%.0f Minutes", round(x)) % x
				else:
					eta = ngettext("%.0f Second", "%.0f Seconds", round(x)) % x
			except ZeroDivisionError:
				eta = "∞"

			self.pb.props.text = _("Sending File") + (" %(0)s/%(1)s (%(2).2f %(3)s/s) " + _("ETA:") + " %(4)s") % {"1": self.num_files, 
									"0": (self.num_files - len(self.files)+1),
									"2": size,
									"3": units,
									"4": eta}
			self._last_update = tm
		
		self.pb.props.fraction = float(self.total_transferred) / self.total_bytes

		
	def on_transfer_completed(self, session):
		del self.files[-1]
		
		self.process_queue()
		
	def process_queue(self):
		if len(self.files) > 0:
			self.send_file(self.files[-1])
		else:
			self.emit("result", True)
		
		
	def send_file(self, file_path):
		dprint(file_path)
		if self.session and self.session.Connected:
			self.session.SendFile(file_path)
		
		
	def on_session_disconnected(self, session):
		if self.session:
			try:
				self.session.Close()
			except:
				dprint("Warning: Session already closed")
		
	def on_session_destroyed(self, manager, path):
		if self.session.object_path == path:
			self.session = None
		
	def on_session_connected(self, session):
		dprint("commence transfer")
		self.sesion = session
		self.process_queue()
		
	def on_session_error(self, session, name, msg):
		dprint("session err", name, msg)
		if not self.error_dialog:	
			self.speed.reset()
			d = gtk.MessageDialog(  self.window,
						type=gtk.MESSAGE_ERROR)
			d.props.text = msg
			d.props.modal = True
			d.props.secondary_text = _("Error occurred while sending file %s") % os.path.basename(self.files[-1])
			d.props.icon_name = "blueman"
		
			if len(self.files) > 1:
				d.add_button(_("Skip"), gtk.RESPONSE_NO)
			d.add_button(_("Retry"), gtk.RESPONSE_YES)
			d.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
		
		
			def on_response(dialog, resp):
				dialog.destroy()
				self.error_dialog = None
				
				if resp == gtk.STOCK_CANCEL:
					self.on_cancel(None)
				elif resp == gtk.RESPONSE_NO:
					self.total_bytes -= os.path.getsize(self.files[-1])
					self.total_transferred -= self.transferred
					self.transferred = 0
					del self.files[-1]
					if not self.session or not self.session.Connected:
						self.create_session()
					self.process_queue()
				elif resp == gtk.RESPONSE_YES:
					self.total_transferred -= self.transferred
					self.transferred = 0
					if not self.session or not self.session.Connected:
						self.create_session()

					self.process_queue()
				else:
					self.on_cancel(None)		
		
			d.connect("response", on_response)
			d.show()
			self.error_dialog = d
		
		
	def on_session_created(self, manager, session):
		dprint()
		self.session = session
		session.GHandle("connected", self.on_session_connected)
		session.GHandle("disconnected", self.on_session_disconnected)
		session.GHandle("error-occurred", self.on_session_error)
		session.GHandle("transfer-started", self.on_transfer_started)
		session.GHandle("transfer-progress", self.on_transfer_progress)
		session.GHandle("transfer-completed", self.on_transfer_completed)
	
class SendTo:
	def __init__(self):
		setup_icon_path()
	
		usage = "Usage: %prog [options] file1 file2 ... fileN"
		parser = OptionParser(usage)
		parser.add_option("-d", "--device", dest="device",
				action="store", help=_("Send files to this device"), metavar="ADDRESS")
		
		parser.add_option("", "--dest", dest="device",
				action="store", help="Same as --device", metavar="ADDRESS")
				
		parser.add_option("-s", "--source", dest="source",
				action="store", help=_("Source adapter. Takes address or adapter's name eg. hci0"), metavar="PATTERN")
		
		(options, args) = parser.parse_args()
		
		check_bluetooth_status(_("Bluetooth needs to be turned on for file sending to work"), lambda: exit())
		
		self.options = options
		self.args = args
		
		self.device = None
		self.adapter = None
		self.files = []
		
		if options.device == None:
			if not self.select_device():
				exit()

			self.do_send()
			
		else:
			m = Manager("gobject")
			try:
				if options.source != None:
					try:
						adapter = m.GetAdapter(options.source)
					except:
						adapter = m.GetAdapter()
				else:
					adapter = m.GetAdapter()
			except:
				print "Error: No Adapters present"
				exit()
			try:
				d = adapter.FindDevice(options.device)
			except:
				info = {}
				info["Address"] = options.device
				info["Name"] = info["Address"].replace(":", "-")
				info["Alias"] = info["Name"]
				info["Fake"] = True
				
				d = FakeDevice(info)

			self.device = Device(d)
			self.adapter = adapter.GetObjectPath()
			self.do_send()
		
		gtk.main()
		
	def do_send(self):
		if len(self.args) == 0:
			if not self.select_files():
				exit()
			else:
				sender = Sender(self.device, self.adapter, self.files)
		else:
			sender = Sender(self.device, self.adapter, self.args)
			
		def on_result(sender, res):
			gtk.main_quit()
		sender.connect("result", on_result)
		
		
	def select_files(self):
		d = gtk.FileChooserDialog(_("Select files to send"), buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
											gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
		d.props.icon_name = "blueman-send-file"									
		d.set_select_multiple(True)
		resp = d.run()
		
		if resp == gtk.RESPONSE_ACCEPT:
			self.files = d.get_filenames()
			d.destroy()
			return True
		else:
			d.destroy()
			return False

	
	def select_device(self):
		d = DeviceSelectorDialog()
		resp = d.run()
		d.destroy()
		if resp == gtk.RESPONSE_ACCEPT:
			sel = d.GetSelection()
			if sel:
				self.device = sel[1]
				self.adapter = sel[0]
				return True
			else:
				return False
		else:
			return False
	

	
SendTo()
