# -*- coding: iso-8859-1 -*-
import xml.sax
from Tools.Directories import crawlDirectory, resolveFilename, SCOPE_CONFIG, SCOPE_SKIN, SCOPE_DEFAULTDIR, copyfile, copytree, fileExists
from Components.NimManager import nimmanager
from Components.Ipkg import IpkgComponent
from Components.config import config, configfile
from Tools.HardwareInfo import HardwareInfo
from enigma import eConsoleAppContainer, eDVBDB
import os

class InfoHandlerParseError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)

class InfoHandler(xml.sax.ContentHandler):
	def __init__(self, prerequisiteMet, directory):
		self.attributes = {}
		self.directory = directory
		self.list = []
		self.globalprerequisites = {}
		self.prerequisites = {}
		self.elements = []
		self.validFileTypes = ["skin", "config", "services", "favourites", "package"]
		self.prerequisitesMet = prerequisiteMet
		self.data = ""

	def printError(self, error):
		print "Error in defaults xml files:", error
		raise InfoHandlerParseError, error

	def startElement(self, name, attrs):
		#print name, ":", attrs.items()
		self.elements.append(name)

		if name in ("hardware", "bcastsystem", "satellite", "tag", "flag"):
			if not attrs.has_key("type"):
					self.printError(str(name) + " tag with no type attribute")
			if self.elements[-3] in ("default", "package"):
				prerequisites = self.globalprerequisites
			else:
				prerequisites = self.prerequisites
			if not prerequisites.has_key(name):
				prerequisites[name] = []
			prerequisites[name].append(str(attrs["type"]))

		if name == "info":
			self.foundTranslation = None
			self.data = ""

		if name == "files":
			if attrs.has_key("type"):
				if attrs["type"] == "directories":
					self.attributes["filestype"] = "directories"
				elif attrs["type"] == "package":
					self.attributes["filestype"] = "package"
				# TODO add a compressed archive type

		if name == "file":
			self.prerequisites = {}
			if not attrs.has_key("type"):
				self.printError("file tag with no type attribute")
			else:
				if not attrs.has_key("name"):
					self.printError("file tag with no name attribute")
				else:	
					if not attrs.has_key("directory"):
						directory = self.directory
					type = attrs["type"]
					if not type in self.validFileTypes:
						self.printError("file tag with invalid type attribute")
					else:
						self.filetype = type
						self.fileattrs = attrs

		if name == "package":
			if attrs.has_key("details"):
				self.attributes["details"] = str(attrs["details"])
			if attrs.has_key("name"):
				self.attributes["name"] = str(attrs["name"])
			if attrs.has_key("packagename"):
				self.attributes["packagename"] = str(attrs["packagename"])
			if attrs.has_key("packagetype"):
				self.attributes["packagetype"] = str(attrs["packagetype"])
			if attrs.has_key("needsRestart"):
				self.attributes["needsRestart"] = str(attrs["needsRestart"])
			if attrs.has_key("shortdescription"):
				self.attributes["shortdescription"] = str(attrs["shortdescription"])

		if name == "screenshot":
			if attrs.has_key("src"):
				self.attributes["screenshot"] = str(attrs["src"])

	def endElement(self, name):
		#print "endElement", name
		#print "self.elements:", self.elements
		self.elements.pop()
		if name == "file":
			#print "prerequisites:", self.prerequisites
			if len(self.prerequisites) == 0 or self.prerequisitesMet(self.prerequisites):
				if not self.attributes.has_key(self.filetype):
					self.attributes[self.filetype] = []
				if self.fileattrs.has_key("directory"):
					directory = str(self.fileattrs["directory"])
					if len(directory) < 1 or directory[0] != "/":
						directory = self.directory + directory
				else:
					directory = self.directory
				self.attributes[self.filetype].append({ "name": str(self.fileattrs["name"]), "directory": directory })

		if name in ( "default", "package" ):
			self.list.append({"attributes": self.attributes, 'prerequisites': self.globalprerequisites})
			self.attributes = {}
			self.globalprerequisites = {}

	def characters(self, data):
		if self.elements[-1] == "author":
			self.attributes["author"] = str(data)
		if self.elements[-1] == "name":
			self.attributes["name"] = str(data)
		if self.elements[-1] == "packagename":
			self.attributes["packagename"] = str(data)
		if self.elements[-1] == "needsRestart":
			self.attributes["needsRestart"] = str(data)
		if self.elements[-1] == "shortdescription":
			self.attributes["shortdescription"] = str(data)
		if self.elements[-1] == "description":
			self.data += data.strip()
			self.attributes["description"] = str(self.data)
		#print "characters", data


class DreamInfoHandler:
	STATUS_WORKING = 0
	STATUS_DONE = 1
	STATUS_ERROR = 2
	STATUS_INIT = 4

	def __init__(self, statusCallback, blocking = False, neededTag = None, neededFlag = None):
		self.hardware_info = HardwareInfo()
		self.directory = "/"

		self.neededTag = neededTag
		self.neededFlag = neededFlag

		# caution: blocking should only be used, if further execution in enigma2 depends on the outcome of
		# the installer!
		self.blocking = blocking

		self.currentlyInstallingMetaIndex = None

		self.console = eConsoleAppContainer()
		self.appClosed_conn = self.console.appClosed.connect(self.installNext)
		self.reloadFavourites = False

		self.statusCallback = statusCallback
		self.setStatus(self.STATUS_INIT)

		self.packageslist = []
		self.packagesIndexlist = []
		self.packageDetails = []

	def readInfo(self, directory, file):
		print "Reading .info file", file
		handler = InfoHandler(self.prerequisiteMet, directory)
		try:
			xml.sax.parse(file, handler)
			for entry in handler.list:
				self.packageslist.append((entry,file)) 
		except InfoHandlerParseError:
			print "file", file, "ignored due to errors in the file"
		#print handler.list

	def readIndex(self, directory, file):
		print "Reading .xml meta index file", directory, file
		handler = InfoHandler(self.prerequisiteMet, directory)
		try:
			xml.sax.parse(file, handler)
			for entry in handler.list:
				self.packagesIndexlist.append((entry,file))
		except InfoHandlerParseError:
			print "file", file, "ignored due to errors in the file"
		#print handler.list

	def readDetails(self, directory, file):
		self.packageDetails = []
		print "Reading .xml meta details file", file
		handler = InfoHandler(self.prerequisiteMet, directory)
		try:
			xml.sax.parse(file, handler)
			for entry in handler.list:
				self.packageDetails.append((entry,file))
		except InfoHandlerParseError:
			print "file", file, "ignored due to errors in the file"
		#print handler.list

	# prerequisites = True: give only packages matching the prerequisites
	def fillPackagesList(self, prerequisites = True):
		self.packageslist = []
		packages = []
		if not isinstance(self.directory, list):
			self.directory = [self.directory]

		for directory in self.directory:
			packages += crawlDirectory(directory, ".*\.info$")

		for package in packages:
			self.readInfo(package[0] + "/", package[0] + "/" + package[1])

		if prerequisites:
			for package in self.packageslist[:]:
				if not self.prerequisiteMet(package[0]["prerequisites"]):
					self.packageslist.remove(package)
		return self.packageslist

	# prerequisites = True: give only packages matching the prerequisites
	def fillPackagesIndexList(self, prerequisites = True):
		self.packagesIndexlist = []
		indexfileList = []

		if not isinstance(self.directory, list):
			self.directory = [self.directory]

		for indexfile in os.listdir(self.directory[0]):
			if indexfile.startswith("index-"):
				if indexfile.endswith(".xml"):
					if indexfile[-7:-6] == "_":
						continue
					indexfileList.append(indexfile)
		if len(indexfileList):
			for file in indexfileList:
				neededFile = self.directory[0] + "/" + file
				if os.path.isfile(neededFile):
					self.readIndex(self.directory[0] + "/" , neededFile)

		if prerequisites:
			for package in self.packagesIndexlist[:]:
				if not self.prerequisiteMet(package[0]["prerequisites"]):
					self.packagesIndexlist.remove(package)
		return self.packagesIndexlist

	# prerequisites = True: give only packages matching the prerequisites
	def fillPackageDetails(self, details = None):
		self.packageDetails = []
		detailsfile = details
		if not isinstance(self.directory, list):
			self.directory = [self.directory]
		self.readDetails(self.directory[0] + "/", self.directory[0] + "/" + detailsfile)
		return self.packageDetails
			
	def prerequisiteMet(self, prerequisites):
		# TODO: we need to implement a hardware detection here...
		print "prerequisites:", prerequisites
		met = True
		if self.neededTag is None:
			if prerequisites.has_key("tag"):
				return False
		elif self.neededTag == 'ALL_TAGS':
				return True
		else:
			if prerequisites.has_key("tag"):
				if not self.neededTag in prerequisites["tag"]:
					return False
			else:
				return False

		if self.neededFlag is None:
			if prerequisites.has_key("flag"):
				return False
		else:
			if prerequisites.has_key("flag"):
				if not self.neededFlag in prerequisites["flag"]:
					return False
			else:
				return True # No flag found, assuming all flags valid
				
		if prerequisites.has_key("satellite"):
			for sat in prerequisites["satellite"]:
				if int(sat) not in nimmanager.getConfiguredSats():
					return False			
		if prerequisites.has_key("bcastsystem"):
			has_system = False
			for bcastsystem in prerequisites["bcastsystem"]:
				if nimmanager.hasNimType(bcastsystem):
					has_system = True
			if not has_system:
				return False
		if prerequisites.has_key("hardware"):
			hardware_found = False
			for hardware in prerequisites["hardware"]:
				if hardware == self.hardware_info.device_name:
					hardware_found = True
			if not hardware_found:
				return False
		return True
	
	def installPackages(self, indexes):
		print "installing packages", indexes
		if len(indexes) == 0:
			self.setStatus(self.STATUS_DONE)
			return
		self.installIndexes = indexes
		print "+++++++++++++++++++++++bla"
		self.currentlyInstallingMetaIndex = 0
		self.installPackage(self.installIndexes[self.currentlyInstallingMetaIndex])

	def installPackage(self, index):
		print "self.packageslist:", self.packageslist
		if len(self.packageslist) <= index:
			print "no package with index", index, "found... installing nothing"
			return
		print "installing package with index", index, "and name", self.packageslist[index][0]["attributes"]["name"]
		
		attributes = self.packageslist[index][0]["attributes"]
		self.installingAttributes = attributes
		self.attributeNames = ["skin", "config", "favourites", "package", "services"]
		self.currentAttributeIndex = 0
		self.currentIndex = -1
		self.installNext()
		
	def setStatus(self, status):
		self.status = status
		self.statusCallback(self.status, None)
						
	def installNext(self, *args, **kwargs):
		if self.reloadFavourites:
			self.reloadFavourites = False
			db = eDVBDB.getInstance().reloadBouquets()

		self.currentIndex += 1
		attributes = self.installingAttributes
		#print "attributes:", attributes
		
		if self.currentAttributeIndex >= len(self.attributeNames): # end of package reached
			print "end of package reached"
			if self.currentlyInstallingMetaIndex is None or self.currentlyInstallingMetaIndex >= len(self.installIndexes) - 1:
				print "set status to DONE"
				self.setStatus(self.STATUS_DONE)
				return
			else:
				print "increment meta index to install next package"
				self.currentlyInstallingMetaIndex += 1
				self.currentAttributeIndex = 0
				self.installPackage(self.installIndexes[self.currentlyInstallingMetaIndex])
				return
		
		self.setStatus(self.STATUS_WORKING)		
		
		print "currentAttributeIndex:", self.currentAttributeIndex
		currentAttribute = self.attributeNames[self.currentAttributeIndex]
		
		print "installing", currentAttribute, "with index", self.currentIndex
		
		if attributes.has_key(currentAttribute):
			if self.currentIndex >= len(attributes[currentAttribute]): # all jobs done for current attribute
				self.currentIndex = -1
				self.currentAttributeIndex += 1
				self.installNext()
				return
		else: # nothing to install here
			self.currentIndex = -1
			self.currentAttributeIndex += 1
			self.installNext()
			return
			
		if currentAttribute == "skin":
			skin = attributes["skin"][self.currentIndex]
			self.installSkin(skin["directory"], skin["name"])
		elif currentAttribute == "config":
			if self.currentIndex == 0:
				from Components.config import configfile
				configfile.save()
			configAttributes = attributes["config"][self.currentIndex]
			self.mergeConfig(configAttributes["directory"], configAttributes["name"])
		elif currentAttribute == "favourites":
			favourite = attributes["favourites"][self.currentIndex]
			self.installFavourites(favourite["directory"], favourite["name"])
		elif currentAttribute == "package":
			package = attributes["package"][self.currentIndex]
			self.installIPK(package["directory"], package["name"])
		elif currentAttribute == "services":
			service = attributes["services"][self.currentIndex]
			self.mergeServices(service["directory"], service["name"])
				
	def readfile(self, filename):
		if not os.path.isfile(filename):
			return []
		fd = open(filename)
		lines = fd.readlines()
		fd.close()
		return lines
			
	def mergeConfig(self, directory, name, merge = True):
		print "merging config:", directory, " - ", name
		if os.path.isfile(directory + name):
			config.loadFromFile(directory + name, append = merge)
			configfile.save()
		self.installNext()
		
	def installIPK(self, directory, name):
		if self.blocking:
			os.system("opkg install " + directory + name)
			self.installNext()
		else:
			self.ipkg = IpkgComponent()
			self.ipkg.addCallback(self.ipkgCallback)
			self.ipkg.startCmd(IpkgComponent.CMD_INSTALL, {'package': directory + name})
		
	def ipkgCallback(self, event, param):
		print "ipkgCallback"
		if event == IpkgComponent.EVENT_DONE:
			self.installNext()
		elif event == IpkgComponent.EVENT_ERROR:
			self.installNext()
	
	def installSkin(self, directory, name):
		print "installing skin:", directory, " - ", name
		print "cp -a %s %s" % (directory, resolveFilename(SCOPE_SKIN))
		if self.blocking:
			copytree(directory, resolveFilename(SCOPE_SKIN))
			self.installNext()
		else:
			if self.console.execute("cp -a %s %s" % (directory, resolveFilename(SCOPE_SKIN))):
				print "execute failed"
				self.installNext()

	def mergeServices(self, directory, name, merge = False):
		print "merging services:", directory, " - ", name
		if os.path.isfile(directory + name):
			db = eDVBDB.getInstance()
			db.reloadServicelist()
			db.loadServicelist(directory + name)
			db.saveServicelist()
		self.installNext()

	def installFavourites(self, directory, name):
		print "installing favourites:", directory, " - ", name
		self.reloadFavourites = True

		if self.blocking:
			copyfile(directory + name, resolveFilename(SCOPE_CONFIG))
			self.installNext()
		else:
			if self.console.execute("cp %s %s" % ((directory + name), resolveFilename(SCOPE_CONFIG))):
				print "execute failed"
				self.installNext()


class ImageDefaultInstaller(DreamInfoHandler):
	def __init__(self):
		DreamInfoHandler.__init__(self, self.statusCallback, blocking = True)
		self.directory = resolveFilename(SCOPE_DEFAULTDIR)
		#config is not available yet!
		if not fileExists("/etc/enigma2/settings"):
			print "Installing image defaults."
			self.fillPackagesList()
			self.installPackage(0)
			print "Installing image defaults done!"
		
	def statusCallback(self, status, progress):
		pass
