#!/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
ngettext = gettext.ngettext
_ = gettext.gettext

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
		
		#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):
		if self.session:
			if self.session.Connected:
				self.session.Cancel()
				self.session.Disconnect()
			else:
				print self.session.object_path
				self.manager.CancelSessionConnect(self.session.object_path)
			
		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.Connected:
			self.session.SendFile(file_path)
		
		
	def on_session_disconnected(self, session):
		if self.session:
			try:
				self.session.Close()
			except:
				print "Warning: Session already closed"
		
	def on_session_destroyed(self, manager, path):
		dprint("session destroyed")
		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)
		self.speed.reset()
		d = gtk.MessageDialog(  self.window,
					type=gtk.MESSAGE_ERROR)
		d.props.text = msg
		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)
		
		resp = d.run()
		d.destroy()
		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)

		
		
	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")	
		
		(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:
				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()
