#!/usr/bin/python
# -*- coding: utf-8 -*-

import urllib
import zipfile
import threading
import time
import os
import os.path
import sys
import pygtk
if not sys.platform == 'win32':
	pygtk.require('2.0')
import gtk
import gobject
import stat
import traceback

class modifs():
	def __init__(self, config):
		self.config = config		
		self.init_modifs()
		
	def add_install(self, app):
		change = True
		for element in self.infos["uninstall"]:
			if element == app:
				self.infos["uninstall"].remove(element)
				change = False
				break
		if change == True:
			self.infos["install"].append(app)
		
	def add_uninstall(self, app):
		change = True
		for element in self.infos["install"]:
			if element == app:
				self.infos["install"].remove(element)
				change = False
				break
		if change == True:
			self.infos["uninstall"].append(app)
		
	def desinstaller(self):
		for app in self.infos["uninstall"]:
			self.text = "Désinstallation (" + app.get("name") + " - "
			try:	dir = app.get("dir")
			except:	dir = ""
			if dir == "" or dir == None:
				dir = app.get("name")
			self.TreeStoreModifs[app.get("IterDesinstaller")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - En cours\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
			try:
				self.rmtree(self.config.get('SynApps', 'AppsPath') + "/" + app.get("installdir") + "/" + dir)
			except WindowsError:
				self.TreeStoreModifs[app.get("IterDesinstaller")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Erreur: Impossible de supprimer l'application.\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				traceback.print_exc()
				self.error = True
			except:
				self.TreeStoreModifs[app.get("IterDesinstaller")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Erreur\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				traceback.print_exc()
				self.error = True
			else:
				self.TreeStoreModifs[app.get("IterDesinstaller")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Terminé\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				
		
	def dezipper(self):
		for app in self.infos["install"]:
			self.text = "Extraction (" + app.get("name") + " - "
			self.TreeStoreModifs[app.get("IterDezipper")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - En cours\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
			try:
				self.zipextractall("cache/apps/" + app.get("name") + ".zip", self.config.get('SynApps', 'AppsPath') + "/" + app.get("installdir") + "/")
			except WindowsError:
				self.TreeStoreModifs[app.get("IterDezipper")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Erreur: Impossible de dézipper le fichier.\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				traceback.print_exc()
				self.error = True
			except WindowsError:
				self.TreeStoreModifs[app.get("IterDezipper")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Erreur\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				traceback.print_exc()
				self.error = True
			else:
				self.TreeStoreModifs[app.get("IterDezipper")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Terminé\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
			os.remove("cache/apps/" + app.get("name") + ".zip")
		
	def execute(self):
		self.fenetre = gtk.Window()
		self.fenetre.set_title("SynApps")
		self.fenetre.set_icon(gtk.gdk.pixbuf_new_from_file("img/synapps/SynApps.ico"))
		self.fenetre.set_default_size(325, 350)
		self.fenetre.set_border_width(10)
		
		self.ProgressBar = gtk.ProgressBar()
		self.ProgressBar.set_text("")
		
		self.BordListeModifs = gtk.Frame()
		self.DefilListeModifs = gtk.ScrolledWindow()
		self.DefilListeModifs.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		
		self.TreeStoreModifs = gtk.TreeStore(gtk.gdk.Pixbuf, str)
		
		if len(self.get("uninstall")) > 0:
			IterDesinstaller = self.TreeStoreModifs.append(None, [gtk.gdk.pixbuf_new_from_file('img/synapps/Desinstaller.png'), "Désinstallation"])
			for app in self.get("uninstall"):
				iter = self.TreeStoreModifs.append(IterDesinstaller, [gtk.gdk.pixbuf_new_from_file(app.get("icon")).scale_simple(32,32,gtk.gdk.INTERP_BILINEAR), "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - En attente\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"])
				app.set("IterDesinstaller", iter)
		
		if len(self.get("install")) > 0:
			IterTelecharger = self.TreeStoreModifs.append(None, [gtk.gdk.pixbuf_new_from_file('img/synapps/Installer.png'), "Téléchargement"])
			IterDezipper = self.TreeStoreModifs.append(None, [gtk.gdk.pixbuf_new_from_file('img/synapps/Dezipper.png'), "Extraction"])
			
			for app in self.get("install"):
				iter = self.TreeStoreModifs.append(IterTelecharger, [gtk.gdk.pixbuf_new_from_file(app.get("icon")).scale_simple(32,32,gtk.gdk.INTERP_BILINEAR), "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - En attente\n<small>" + app.get("desc") + " (" + app.get("strzipsize") + ")</small>"])
				app.set("IterTelecharger", iter)
			
			for app in self.get("install"):
				iter = self.TreeStoreModifs.append(IterDezipper, [gtk.gdk.pixbuf_new_from_file(app.get("icon")).scale_simple(32,32,gtk.gdk.INTERP_BILINEAR), "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - En attente\n<small>" + app.get("desc") + " (" + app.get("strzipsize") + ")</small>"])
				app.set("IterDezipper", iter)
				
		self.Modifs = gtk.TreeView(self.TreeStoreModifs)
		self.Modifs.set_headers_visible(False)
		
		self.CellIcone = gtk.CellRendererPixbuf()
		self.CellApp = gtk.CellRendererText()
		
		self.ColonneIcone = gtk.TreeViewColumn('Icone')
		self.ColonneApp = gtk.TreeViewColumn('App', self.CellApp, markup=1)
		
		self.Modifs.append_column(self.ColonneIcone)
		self.Modifs.append_column(self.ColonneApp)
		
		self.ColonneIcone.pack_start(self.CellIcone, True)
		self.ColonneIcone.add_attribute(self.CellIcone, 'pixbuf', 0)
		
		self.Modifs.expand_all()
		
		self.DefilListeModifs.add(self.Modifs)
		self.BordListeModifs.add(self.DefilListeModifs)
		
		self.VBox = gtk.VBox()
		
		self.VBox.pack_start(self.ProgressBar, False, False)
		self.VBox.pack_start(self.BordListeModifs, True, True)
		
		self.fenetre.add(self.VBox)
		self.fenetre.show_all()
		
		self.error = False
		self.desinstaller()
		self.telecharger()
		self.dezipper()
		self.init_modifs()
		if self.error == False:
			self.fenetre.destroy()
	
	def get(self, info = None):
		"""Si info = None: Renvoie la liste des applications à installer et à désinstaller.
		   Sinon: Renvoie l'information passée en paramètres."""
		if info is None:
			return self.infos
		else:
			return self.infos[info]

	def init_modifs(self):
		"""Remet les modifications à 0."""
		self.infos = {"install": [], "uninstall": []}
		
	def on_fenetre_destroy(self, widget):
		self.init_modifs()
		
	def progress(self, count, blockSize, totalSize):
		pourcentage = count*blockSize*100/totalSize
		if pourcentage >= 100:
			pourcentage = 100.0
		if self.ProgressBar.get_fraction() != (pourcentage/100.0):
			self.ProgressBar.set_fraction(pourcentage/100.0)
			self.ProgressBar.set_text(self.text + str(pourcentage) + "%)")
			self.fenetre.set_title(self.text + str(pourcentage) + "%)")
		while gtk.events_pending():
			gtk.main_iteration_do(False)
		
	def rmtree(self, path, ignore_errors=False, onerror=None, foldersize=0, delsize=0):
		"""Recursively delete a directory tree.

		If ignore_errors is set, errors are ignored; otherwise, if onerror
		is set, it is called to handle the error with arguments (func,
		path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
		path is the argument to that function that caused it to fail; and
		exc_info is a tuple returned by sys.exc_info().  If ignore_errors
		is false and onerror is None, an exception is raised.

		"""
		if foldersize == 0 and os.path.isdir(path):
			for (folder, dirs, files) in os.walk(path):
				for file in files:
					foldersize += os.path.getsize(os.path.join(folder, file))
		
		if ignore_errors:
			def onerror(*args):
				pass
		elif onerror is None:
			def onerror(*args):
				raise
		try:
			if os.path.islink(path):
				# symlinks to directories are forbidden, see bug #1669
				raise OSError("Cannot call rmtree on a symbolic link")
		except OSError:
			onerror(os.path.islink, path, sys.exc_info())
			# can't continue even if onerror hook returns
			return
		names = []
		try:
			names = os.listdir(path)
		except os.error, err:
			onerror(os.listdir, path, sys.exc_info())
		for name in names:
			fullname = os.path.join(path, name)
			try:
				mode = os.lstat(fullname).st_mode
			except os.error:
				mode = 0
			if stat.S_ISDIR(mode):
				delsize = self.rmtree(fullname, ignore_errors, onerror, foldersize, delsize)
			else:
				try:
					delsize += os.path.getsize(fullname)
					os.remove(fullname)
					self.progress(1, delsize, foldersize)
				except os.error, err:
					onerror(os.remove, fullname, sys.exc_info())
		try:
			os.rmdir(path)
		except os.error:
			onerror(os.rmdir, path, sys.exc_info())
			
		return delsize

		
	def telecharger(self):
		for app in self.infos["install"]:
			try:	url = app.get("download")
			except:	url = ""
			
			self.text = "Téléchargement (" + app.get("name") + " - "
			self.TreeStoreModifs[app.get("IterTelecharger")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - En cours\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
			try:
				urllib.urlretrieve(url, "cache/apps/" + app.get("name") + ".zip", self.progress)
			except IOError:
				self.TreeStoreModifs[app.get("IterTelecharger")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Erreur: Impossible de télécharger le fichier\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				traceback.print_exc()
				self.error = True
			except:
				self.TreeStoreModifs[app.get("IterTelecharger")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Erreur\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				traceback.print_exc()
				self.error = True
			else:
				self.TreeStoreModifs[app.get("IterTelecharger")][1] = "<big>" + app.get("parentname") + "</big> <small>" + app.get("version") + "</small> - Terminé\n<small>" + app.get("desc") + " (" + app.get("strsize") + ")</small>"
				
	def zipextractall(self, zip, path=None, members=None, pwd=None):
		"""Extract all members from the archive to the current working
		   directory. `path' specifies a different directory to extract to.
		   `members' is optional and must be a subset of the list returned
		   by namelist().
		"""
		zip = zipfile.ZipFile(zip, 'r')
		
		if members is None:
			members = zip.namelist()

		zipsize = 0
		for infos in zip.infolist():
			zipsize += infos.file_size
		dirsize = 0
		
		for zipinfo in members:
			zip.extract(zipinfo, path, pwd)
			dirsize += zip.getinfo(zipinfo).file_size
			self.progress(1, dirsize, zipsize)

		zip.close()