# A stand-alone simple downloader

import Queue
import locale
import os
import sys
import tempfile
import threading
import time
import traceback
import urllib
import urlparse

from PyQt4.QtCore import Qt
from PyQt4 import QtGui
from PyQt4 import QtCore

import player
import playermodel
import downloader as apdl

palmtop = False

builtinConfig = """
[General]
mediaDir=~/.autoplayer
userConfigPath=~/.autoplayrc
feedUrl=http://192.168.20.1:9091/rss
plName=.autoplay
"""


def tryUnlink(fName):
	try:
		os.unlink(fName)
	except os.error:
		pass


class Downloader:
	def __init__(self, mediaDir, feedUrl, ui=None):
		self.mediaDir, self.ui = mediaDir, ui
		self._getFeedIndex(feedUrl)
		self.sources = player.FilePlayList(mediaDir)
		try:
			self.sources.load()
		except player.DeserializationError: # no .autoplayer yet
			pass

		self.pleaseStop = False
		self.downloading = False
		self.minFree = 5000000
		self.updateCount()

	def _getTmpName(self):
		fd, name = tempfile.mkstemp(".deleteme", "download", self.mediaDir)
		os.close(fd)
		return name

	def _sendUi(self, what, msg):
		if self.ui:
			self.ui.message(what, msg)
		else:
			print "%s: %s"%(what, msg)

	def _downloadOne(self, url, targetName):
		bufsz = 2**14
		target = self._getTmpName()
		f = open(target, "w")
		try:
			u = urllib.urlopen(url)
			while True:
				data = u.read(bufsz)
				if data=="":
					break
				f.write(data)
			f.close()
			u.close()
			os.system("sync")
			os.rename(target, os.path.join(self.mediaDir, targetName))
			os.system("sync")
		finally:
			tryUnlink(target)

	def _getFeedIndex(self, feedUrl):
		self.feedIndex = apdl._parseFeedIndex(feedUrl)
		self.feedIndex.sort()
	
	def _downloadMedium(self, url):
		basename = os.path.basename(
			urlparse.urlparse(url)[2]).encode("iso-8859-1")
		self._sendUi("info", "Getting %s"%basename)
		self._downloadOne(url, basename)
		self._sendUi("info", "Got %s"%basename)
		self.sources.addSource(basename)
		# tell server to move thing to archive
		urllib.urlopen(url.replace("media", "move", 1))

	def updateCount(self):
		self._sendUi("count", len(self.feedIndex))

	def downloadFeed(self):
		try:
			self.sources.removePlayedFiles()
		except player.ComponentFailure:
			pass
		self.downloading = True
		try:
			try:
				while self.feedIndex and not self.pleaseStop:
					timestamp, url, size = self.feedIndex.pop(0)
					if size is None:
						# make random guess -- all that's bad if this is off
						# is a wasted download
						size = 3000000
					if apdl.getFreeBytes(self.mediaDir)-size<self.minFree:
						self._sendUi("error", "Disk full")
						break

					self._downloadMedium(url)
					self.sources.save()
					self.updateCount()

			except Exception, msg:
				traceback.print_exc()
				self._sendUi("error", str(msg))
		finally:
			self.downloading = False
			self.pleaseStop = False
			self._sendUi(None, "finished")

	def stop(self):
		self.pleaseStop = True


class QtUi(QtGui.QMainWindow):
	def __init__(self, config, app):
		self.config, self.app = config, app
		self.dlThread = None
		QtGui.QMainWindow.__init__(self)
		self.mainFrame = QtGui.QFrame(self)
		self.setCentralWidget(self.mainFrame)
		self.layout = QtGui.QGridLayout(self.mainFrame)
		self.eventQueue = Queue.Queue()
		self._makeMenubar()
		self._makeWidgets()
		self.dlThread = threading.Thread(target=self.getDl)
		self.dlThread.start()
		self.startTimer(100)

	def _makeWidgets(self):
		self.statusLabel = QtGui.QLabel("No feed info yet", self.mainFrame)
		self.statusLabel.setAlignment(Qt.AlignLeft)
		self.statusLabel.setMaximumWidth(400)
		self.layout.addWidget(self.statusLabel, 2, 0)
		self.messageLabel = QtGui.QLabel("", self.mainFrame)
		self.messageLabel.setTextFormat(Qt.PlainText)
		self.messageLabel.setMaximumWidth(400)
		self.layout.addWidget(self.messageLabel, 3, 0)
		self.userButton = QtGui.QPushButton("Stop", self.mainFrame)
		self.userButton.setDisabled(True)
		self.layout.addWidget(self.userButton, 4, 0)
	
	def _makeMenubar(self):
		fileMenu = self.menuBar().addMenu("File")
		fileMenu.addAction("&Quit", self.prepareQuit)

	def _stopDownloader(self):
		self.downloader.stop()
		self.message("info", "Finishing last download")
		self.userButton.setDisabled(True)
	
	def _handleStoppedDownloader(self):
		self.dlThread = None
		self.message("info", "Download finished")
		self.userButton.setDisabled(True)
		self.app.processEvents()
		QtCore.QTimer.singleShot(3000, self.app.quit)
    
	def _startDownloader(self):
		self.dlThread = threading.Thread(
			target=self.downloader.downloadFeed)
		self.dlThread.setDaemon(True)
		self.dlThread.start()
		self.userButton.connect(self.userButton,
			QtCore.SIGNAL("clicked()"), self._stopDownloader)
		self.userButton.setDisabled(False)

	def getDl(self):
		# supposed to run in dlThread
		locale.setlocale(locale.LC_ALL, "C")
		try:
			downloader = Downloader(self.config.get("mediaDir"), 
				self.config.get("feedUrl"), self)
		except Exception, msg:
			traceback.print_exc()
			self.message("error", str(msg))
		else:
			self.message(None, downloader)

	def message(self, kind, content):
		self.eventQueue.put((kind, content))
	
	def timerEvent(self, ev):
		while True:
			try:
				self._handleNextEvent()
			except Queue.Empty:
				break

	def _handleNextEvent(self):
		kind, content = self.eventQueue.get(block=False)
		if isinstance(kind, basestring):
			if kind in ["info"]:
				self.messageLabel.setText("%s: %s"%(kind.upper(), content))
			elif kind=="error":
				QtGui.QMessageBox.critical(self, "pull", content)
				self._handleStoppedDownloader()
			elif kind=="count":
				self.statusLabel.setText("%d items left"%content)
		elif kind==None:
			if content=="finished":
				self._handleStoppedDownloader()
			elif isinstance(content, Downloader):
				self.downloader = content
				self._startDownloader()

	def prepareQuit(self):
		if self.dlThread:
			self._stopDownloader()
		self.app.quit()
		

def main():
	config = playermodel.Config(builtinConfig)
	if len(sys.argv)>1:
		dl = Downloader(config.get("mediaDir"), config.get("feedUrl"))
		dl.downloadFeed()
	else:
		if palmtop:
			import qtpe
			app = qtpe.QPEApplication(sys.argv)
		else:
			app = QtGui.QApplication(sys.argv)
			app.connect(app, QtCore.SIGNAL("lastWindowClosed()"),
				app, QtCore.SLOT("quit()"))
		ui = QtUi(config, app)
		if palmtop:
			app.showMainWidget(ui)
		else:
			ui.show()
		app.exec_()
		urllib.urlcleanup()

if __name__=="__main__":
	main()
