# 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

import qt

import player
import playermodel
import downloader

palmtop = True

builtinConfig = """
[General]
mediaDir=~/.autoplayer
userConfigPath=~/.autoplayrc
feedUrl=http://10.10.0.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)
		self.sources.load()
		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**16
		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.rename(target, os.path.join(self.mediaDir, targetName))
		finally:
			tryUnlink(target)

	def _getFeedIndex(self, feedUrl):
		self.feedIndex = downloader._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 autoplayer 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 = self.feedIndex.pop(0)
					self._downloadMedium(url)
					self.sources.save()
					if downloader.getFreeBytes(self.mediaDir)<self.minFree:
						self._sendUi("error", "Disk full")
						break
					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(qt.QMainWindow):
	def __init__(self, config):
		self.config = config
		self.dlThread = None
		qt.QMainWindow.__init__(self)
		self.mainFrame = qt.QFrame(self)
		self.setCentralWidget(self.mainFrame)
		self.layout = qt.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 = qt.QLabel("No feed info yet", self.mainFrame)
		self.statusLabel.setAlignment(qt.Qt.SingleLine | qt.Qt.AlignLeft)
		self.layout.addWidget(self.statusLabel, 0, 0)
		self.messageLabel = qt.QLabel("", self.mainFrame)
		self.messageLabel.setTextFormat(qt.Qt.PlainText)
		self.messageLabel.setAlignment(qt.Qt.AlignLeft | qt.Qt.WordBreak)
		self.messageLabel.setMaximumWidth(400)
		self.layout.addWidget(self.messageLabel, 1, 0)
		self.userButton = qt.QPushButton("Start", self.mainFrame)
		self.userButton.setDisabled(True)
		self.connect(self.userButton, qt.SIGNAL("clicked()"), self.run)
		self.layout.addWidget(self.userButton, 2, 0)
	
	def _makeMenubar(self):
		fileMenu = qt.QPopupMenu(self)
		fileMenu.insertItem("&Quit", self.prepareQuit)
		self.menu = qt.QMenuBar(self)
		self.menu.insertItem("File", fileMenu)

	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.setText("Start")
		self.userButton.setDisabled(False)

	def _startDownloader(self):
		self.dlThread = threading.Thread(
			target=self.downloader.downloadFeed)
		self.dlThread.setDaemon(True)
		self.dlThread.start()
		self.userButton.setText("Stop")

	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 _downloaderAvailable(self, downloader):
		self.downloader = downloader
		self.userButton.setDisabled(False)
		self.dlThread = None

	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":
				qt.QMessageBox.critical(self, "pull", content)
			elif kind=="count":
				self.statusLabel.setText("%d items left"%
					content)
		elif kind==None:
			if content=="finished":
				self._handleStoppedDownloader()
			elif isinstance(content, Downloader):
				self._downloaderAvailable(content)

	def prepareQuit(self):
		if self.dlThread:
			self._stopDownloader()
		qt.qApp.quit()
		
	def run(self):
		if self.dlThread:
			self._stopDownloader()
		else:
			self._startDownloader()


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 = qt.QApplication(sys.argv)
			app.connect(app, qt.SIGNAL("lastWindowClosed()"),
				app, qt.SLOT("quit()"))
		ui = QtUi(config)
		if palmtop:
			app.showMainWidget(ui)
		else:
			ui.show()
		app.exec_loop()
		urllib.urlcleanup()

if __name__=="__main__":
	main()

