#
#  Picon Loader
#
#  Coded by Shaderman (c) 2011
#  Support: www.dreamboxtools.de
#
#  This plugin is licensed under the Creative Commons 
#  Attribution-NonCommercial-ShareAlike 3.0 Unported 
#  License. To view a copy of this license, visit
#  http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
#  Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
#
#  Alternatively, this plugin may be distributed and executed on hardware which
#  is licensed by Dream Multimedia GmbH.

#  This plugin is NOT free software. It is open source, you are allowed to
#  modify it (if you keep the license), but it may not be commercially 
#  distributed other than under the conditions noted above.
#
from __future__ import print_function
from __future__ import absolute_import
from Components.ActionMap import ActionMap
from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigSelection, ConfigText, ConfigYesNo
from Components.ConfigList import ConfigListScreen
from Components.Label import Label
from Components.NimManager import nimmanager
from Components.Pixmap import Pixmap
from Components.ProgressBar import ProgressBar
from Components.Sources.List import List
from Components.Sources.StaticText import StaticText
from copy import copy
from enigma import ePixmap, eServiceCenter, eServiceReference, iServiceInformation, eEnv, ePicLoad
from os import remove as os_remove, path as os_path, mkdir as os_mkdir, listdir as os_listdir, symlink as os_symlink, unlink as os_unlink
from Tools.HardwareInfo import HardwareInfo
from Screens.ChoiceBox import ChoiceBox
from Screens.MessageBox import MessageBox
from Screens.Screen import Screen
from ServiceReference import ServiceReference
from shutil import rmtree
from Tools.BoundFunction import boundFunction
from Tools.Directories import fileExists, resolveFilename, SCOPE_CONFIG, SCOPE_SKIN_IMAGE, pathExists
from Tools.LoadPixmap import LoadPixmap, pixmap_cache
from Tools.Transponder import ConvertToHumanReadable
import cPickle as pickle
import xml.etree.cElementTree as etree
#Dre
from Screens.PiconMapper import PiconMapperConfigScreen as SetSelectorScreen
from datetime import date as datetime_date
import re

PICONBASEURL	= "http://picon.dreamboxtools.de/"
USERAGENT	= "Merlin PiconLoader"
XMLFILE		= "picons_v3.xml"
INFOFILE	= "piconloader"
MAXDOWNLOADS	= 10

config.plugins.piconloader = ConfigSubsection()
config.plugins.piconloader.useFilter = ConfigYesNo(False)
config.plugins.piconloader.filterByColor = ConfigSelection(default = "all", choices = [
				("all", _("all")),
				("dark", _("dark")),
				("light", _("light"))
				])
config.plugins.piconloader.cabProvider = ConfigSelection(choices = [])
config.plugins.piconloader.selectedCabProvider = ConfigText("n/a")
config.plugins.piconloader.extDisplay = ConfigSelection(choices = [])
config.plugins.piconloader.selectedExtDisplay = ConfigText("n/a")
config.plugins.piconloader.creator = ConfigSelection(choices = [])

# base class to download a list of files
class ItemDownloader:
	def __init__(self):
		pass

	def downloadItem(self, item):
		from twisted.web.client import downloadPage
		return downloadPage(item.url, file(item.filename, 'w'), agent = USERAGENT)
		
	def startDownload(self, downloadList):
		from twisted.internet import defer
		if len(downloadList) > 0:
			ds = defer.DeferredSemaphore(tokens = MAXDOWNLOADS)
			downloads = [ds.run(self.downloadItem, item).addErrback(self.cbDownloadError, item).addCallback(self.cbDownloadFinished, item) for item in downloadList]
			defer.DeferredList(downloads).addCallback(self.cbAllDownloadsFinished)
			
	def cbDownloadError(self, error = None, item = None):
		item.error = True
		
		print("[PiconLoader] download error", error)
		
		# 0 byte files are created on download errors (wtf twisted?), delete them
		try:
			if fileExists(item.filename):
				os_remove(item.filename)
		except OSError as error:
			print('[PiconLoader] unable to delete file', item.filename)
			
	def cbDownloadFinished(self, result, item):
		pass
		
	def cbAllDownloadsFinished(self, result):
		pass
		
# storage class for picon set items
class PiconSetInfo:
	def __init__(self, url = "", filename = "", error = False, tunerType = "", description = "", name = "", author = "", size = "", date = "", provider = "", colorType = "", extDisplay = "", path = "", isInstalled = "", sref = "", channelname = ""):
		self.url = url
		self.filename = filename
		self.error = error
		self.tunerType = tunerType
		self.description = description
		self.name = name
		self.author = author
		self.size = size
		self.date = date
		self.provider = provider
		self.colorType = colorType
		self.extDisplay = extDisplay
		self.path = path
		self.isInstalled = isInstalled
		self.sref = sref
		self.channelname = channelname
		
class XmlDownloader:
	def __init__(self, session):
		self.session = session
		
		self.delList = []
		
		self.gotTunerSat = False
		self.gotTunerCab = False
		self.gotTunerTer = False
		
		self.getTunerTypes()
		
	# get the tuner types in the box
	def getTunerTypes(self):
		for slot in nimmanager.nim_slots:
			if slot.types is not None:
				if slot.isEnabled("DVB-S") or slot.isEnabled("DVB-S2"):
					self.gotTunerSat = True
				if slot.isEnabled("DVB-C"):
					self.gotTunerCab = True
				if slot.isEnabled("DVB-T") or slot.isEnabled("DVB-T2"):
					self.gotTunerTer = True
		
		print("[PiconLoader] DVB-S", self.gotTunerSat)
		print("[PiconLoader] DVB-C", self.gotTunerCab)
		print("[PiconLoader] DVB-T", self.gotTunerTer)
					
	# download the xml file
	def downloadXmlFile(self):
		print('[PiconLoader] downloading XML file', XMLFILE)
		url = PICONBASEURL + XMLFILE
		from twisted.web.client import getPage
		getPage(url, agent = USERAGENT, timeout = 5).addCallback(self.cbDownloadXml).addErrback(self.cbDownloadXmlError)
		
	# callback called when a download error occurs
	def cbDownloadXmlError(self, error):
		if error is not None:
			print('[PiconLoader] error downloading XML file:', str(error.getErrorMessage()))
			self.session.open(MessageBox, _("Unable to download XML file. Please try again later.\n%s") % str(error.getErrorMessage()), MessageBox.TYPE_ERROR)
			
	# parse the received XML file and get the picon set information
	def getItemList(self, xmlData):
		downloadList = []
		cabProviderList = [("n/a", _("n/a"))]
		creatorList = [("all", _("all"))]
		extDisplayList = [("n/a",_("n/a"))]
		
		# build our "satlist" from all satellites
		root = etree.fromstring(xmlData)

		for piconSet in root.findall("piconset"):
			tunerType	= piconSet.attrib.get("tunerType")
			if (tunerType == "DVB-C" and not self.gotTunerCab) or (tunerType == "DVB-S" and not self.gotTunerSat):
				#Dre
				#only set self.gotTunerSat to true when PiconMapper is active and PiconSet is for DVB-S
				if config.plugins.piconmapper.activate.value and tunerType == "DVB-S":
					print("[PiconLoader] set gotTunerSat to true")
					self.gotTunerSat = True
				else:
					continue
				
			name = piconSet.attrib.get("name")
			if ("oled" in name) and (not "800se" in HardwareInfo().get_device_name() and HardwareInfo().get_device_name() != "dm820"): # only offer OLED picons for DM800se / DM820HD
				continue

			description	= piconSet.attrib.get("description")
			author		= piconSet.attrib.get("author")
			size		= piconSet.attrib.get("size")
			date		= piconSet.attrib.get("date")
			colorType	= piconSet.attrib.get("colorType")
			extDisplay	= piconSet.attrib.get("extDisplay")
			
			if author not in creatorList:
				creatorList.append(author)
			
			if extDisplay is not None:
				
				if not len([x for x in extDisplayList if x[0] == extDisplay]):
					extDisplayList.append((extDisplay, extDisplay))
					print(extDisplayList)
			
			allPositions = []
			for picon in piconSet.findall("picon"):
				if tunerType == "DVB-S":
					print("[PiconLoader] PiconSet is for DVB-S")
					satPosition = picon.attrib.get("provider")
					insertPos = len(satPosition) -1
					pos = satPosition[:insertPos] + "." + satPosition[insertPos:]
					if pos.startswith("."):
						pos = "0" + pos
					allPositions.append(pos)
					
				elif tunerType == "DVB-C":
					print("[PiconLoader] PiconSet is for DVB-C")
					cabProvider = picon.attrib.get("provider")
					if not cabProvider in cabProviderList:
						cabProviderList.append(cabProvider)
				else:
					print("[PiconLoader] PiconSet is not for DVB-S nor for DVB-C")
					continue

			directory = "/tmp/" + tunerType
			if not directory in self.delList:
				self.delList.append(directory)
				
			try:
				if not os_path.exists(directory):
					os_mkdir(directory)
				directory = directory + "/" + name
				if not os_path.exists(directory):
					os_mkdir(directory)
			except OSError as error:
				print('[PiconLoader] unable to create directory', directory)
				continue
				
			if tunerType == "DVB-S" and self.gotTunerSat:
				print("[PiconLoader] PiconSet is for DVB-S and got sat tuner")
				provider = ""
				i = 0
				while i < len(allPositions) - 1:
					provider += allPositions[i] + ", "
					i += 1
				provider += allPositions[len(allPositions) - 1]
			elif tunerType == "DVB-C" and self.gotTunerCab:
				print("[PiconLoader] ]PiconSet is for DVB-C and got cable tuner")
				provider = cabProvider
							
			url = PICONBASEURL + directory[5:] + "/preview.png"
			fileName = directory + "/preview.png"
			
			path = ""
			isInstalled = ("")				

			for entry in self.installedList:
				if entry[3] == name and entry[1] == tunerType and entry[7] == provider:
					path = entry[8]
					isInstalled = _("installed")

			downloadList.append(PiconSetInfo(url = url, filename = fileName, tunerType = tunerType, description = description, name = name, author = author, size = size, date = date, provider = provider, colorType = colorType, extDisplay = extDisplay, path = path, isInstalled = isInstalled))
		
		if self.gotTunerCab:
			if config.plugins.piconloader.selectedCabProvider.value == "":
				cableDefault = "n/a"
			else:
				cableDefault = config.plugins.piconloader.selectedCabProvider.value
			config.plugins.piconloader.cabProvider.setChoices(cabProviderList, default=cableDefault)
		if config.plugins.piconloader.selectedExtDisplay.value == "":
			displayDefault = "n/a"
		else:
			displayDefault = config.plugins.piconloader.selectedExtDisplay.value
		config.plugins.piconloader.extDisplay.setChoices(extDisplayList, default = displayDefault)
		config.plugins.piconloader.creator.setChoices(creatorList)
				
		return downloadList
	
class DownloadListBuilder:
	def __init__(self):
		from Screens.InfoBar import InfoBar
		self.infoBarInstance = InfoBar.instance
		self.eServiceCenterInstance = eServiceCenter.getInstance()
		
	def getBouquetList(self, bouquetRoot):
		bouquets = []
		
		if config.usage.multibouquet.value:
			serviceList = self.eServiceCenterInstance.list(bouquetRoot)
			if serviceList:
				while True:
					service = serviceList.getNext()
					if not service.valid():
						break
					if service.flags & eServiceReference.isDirectory:
						info = self.eServiceCenterInstance.info(service)
						if info:
							bouquets.append(service)
				return bouquets
		else:
			info = self.eServiceCenterInstance.info(bouquetRoot)
			if info:
				bouquets.append(bouquetRoot)
			return bouquets
			
	def getDirectoryServices(self, directory):
		serviceList = self.eServiceCenterInstance.list(directory)
		
		if serviceList:
			while True:
				service = serviceList.getNext()
				oldPath = ""
				if service.getPath() and not service.flags & eServiceReference.isGroup and not service.flags & eServiceReference.isMarker:
					oldPath = service.getPath()
					service.setPath("")
				if not service.valid():
					break
				if service.flags & eServiceReference.isGroup:
					self.getDirectoryServices(service)
				elif service.flags & eServiceReference.isMarker:
					continue
				else:
					self.allServicesList.append(service)
				service.setPath(oldPath)
					
	def buildDownloadList(self, item):
		path = item[8]
		
		downloadList = []
		filenameCache = []
		self.allServicesList = []
		
		from Screens.ChannelSelection import service_types_tv, service_types_radio
		bouquetStringList = []
		if config.usage.multibouquet.value:
			bouquetStringList.append(service_types_tv + ' FROM BOUQUET "bouquets.tv"')
			bouquetStringList.append(service_types_radio + ' FROM BOUQUET "bouquets.radio"')
		else:
			bouquetStringList.append(service_types_tv + ' FROM BOUQUET "userbouquet.favourites.tv"')
			bouquetStringList.append(service_types_radio + ' FROM BOUQUET "userbouquet.favourites.radio"')
			
		for bouquetString in bouquetStringList:
			bouquetRoot = eServiceReference(bouquetString)
			bouquetServices = self.getBouquetList(bouquetRoot)
			if bouquetServices is None:
				continue
				
			for service in bouquetServices:
				self.getDirectoryServices(service)
			
			for service in self.allServicesList:
				sRef = buildServiceRefString(service)
				url = ""
				fileName = ""
				
				if sRef is "":
					continue

				# remove partnerbox-services paths
				oldPath = ""
				if service.getPath():
					oldPath = service.getPath()
					service.setPath("")
	
			
				info = self.eServiceCenterInstance.info(service)
				channelname = ""
				if info is not None:
					#if config.merlin2.skin_piconmode.value == "Name":
					channelname = info.getName(service) or ServiceReference(service).getServiceName() or ""
					channelname = channelname.replace('\xc2\x86', '').replace('\xc2\x87', '')
					channelname = re.sub('[^0-9a-zA-Z]+', '_', channelname)
				
					transponderInfo = info.getInfoObject(service, iServiceInformation.sTransponderData)
					if transponderInfo is not None:
						print("[PiconLoader] transponderInfo is not None", transponderInfo)
						frontendData = ConvertToHumanReadable(transponderInfo)
						tunerType = ""
						#if frontendData.has_key("tuner_type"):
						if "tuner_type" in frontendData:
							if frontendData["tuner_type"] == _("Cable"):
								tunerType  =  "DVB-C"
							elif frontendData["tuner_type"] == _("Satellite"):
								tunerType  =  "DVB-S"
							elif frontendData["tuner_type"] == _("Terrestrial"):
								tunerType = "DVB-T"	
							
						print("[PiconLoader] tuner type is ", tunerType)
						print("[PiconLoader] item[1] is ", item[1])
					
						if tunerType != item[1] and item[1] == "DVB-C":
							print("[PiconLoader] - tunerType does not match PiconSet and PiconSet is for DVB-C")
							continue

						if tunerType == "DVB-S":
							position = str(transponderInfo["orbital_position"])
							if config.plugins.piconmapper.activate.value:
								print("[PiconLoader] DVB-S - piconmapper active")
								url = PICONBASEURL + tunerType + "/" + item[3]+ "/" + position + "/" + sRef + ".png"
								fileName = path + "/" + sRef + ".png"
								if config.merlin2.skin_piconmode.value == "Name":
									fileName = path + "/" + channelname + ".png"
								if not fileName in filenameCache:
									filenameCache.append(fileName)
									downloadList.append(PiconSetInfo(url = url, path = path, filename = fileName, channelname = channelname, tunerType = tunerType, sref = sRef ))
							else:
								print("[PiconLoader] DVB-S")
								url = PICONBASEURL + tunerType + "/" + item[3] + "/" + position + "/" + sRef + ".png"
						elif tunerType == "DVB-C":
							if config.plugins.piconmapper.activate.value and tunerType != item[1]:
								print("[PiconLoader] DVB-C - piconmapper active")
								url = self.updatePiconsWithPiconMapper(sRef, item[3])
								if url != "":
									fileName = path + "/" + sRef + ".png"
									if config.merlin2.skin_piconmode.value == "Name":
										fileName = path + "/" + channelname + ".png"								
									if not fileName in filenameCache:
										filenameCache.append(fileName)
										downloadList.append(PiconSetInfo(url = url, path = path, filename = fileName, channelname = channelname, tunerType = tunerType, sref = sRef ))
							else:
								print("[PiconLoader] DVB-C")
								url = PICONBASEURL + tunerType + "/" + item[3] + "/" + item[7] + "/" + sRef + ".png"
						else: # "DVB-T":
							if config.plugins.piconmapper.activate.value:
								print("[PiconLoader] DVB-T - piconmapper active")
								url = self.updatePiconsWithPiconMapper(sRef, item[3])
								if url != "":
									fileName = path + "/" + sRef + ".png"
									if config.merlin2.skin_piconmode.value == "Name":
										fileName = path + "/" + channelname + ".png"								
									if not fileName in filenameCache:
										filenameCache.append(fileName)
										downloadList.append(PiconSetInfo(url = url, path = path, filename = fileName, channelname = channelname, tunerType = tunerType, sref = sRef ))

					#Dre - Partnerbox or IPTV
					else:
						if config.plugins.piconmapper.activate.value:
							print("[PiconLoader] Partnerbox channel - piconmapper active")
							url = self.updatePiconsWithPiconMapper(sRef, item[3])
							if url != "":
								fileName = path + "/" + sRef + ".png"
								if config.merlin2.skin_piconmode.value == "Name":
									fileName = path + "/" + channelname + ".png"							
								if not fileName in filenameCache:
									filenameCache.append(fileName)
									downloadList.append(PiconSetInfo(url = url, path = path, filename = fileName, channelname = channelname, tunerType = tunerType, sref = sRef ))
						
							service.setPath(oldPath)							
					
						if url == "":
							print("[PiconLoader] Partnerbox channel without mapping")
							#service is a partnerbox-service but piconmapper is not used - try to find a sat channel
							position = ""
							if sRef.upper().find("C00000") != -1:
								position = "192"
							elif sRef.upper().find("820000") != -1:
								position = "130"
							elif sRef.upper().find("11C000") != -1:
								position = "282"
							else:
								url = ""
								eServiceCenterInstance = eServiceCenter.getInstance()
								info = eServiceCenterInstance.info(service)
								name = info.getName(service) or ServiceReference(service).getServiceName() or ""
								print("[PiconLoader] No Mapping for channel ", name)
						
							if position != "":
								print("[PiconLoader] Partnerbox channel Astra, Hotbird or Astra2")
								url = PICONBASEURL + "DVB-S/" + item[3] + "/" + position + "/" + sRef + ".png"

				if url != "":
					print("[PiconLoader] URL set and it's not a piconmapperset")
					fileName = path + "/" + sRef + ".png"
					if config.merlin2.skin_piconmode.value == "Name":
						fileName = path + "/" + channelname + ".png"					
					if not fileName in filenameCache:
						filenameCache.append(fileName)
						downloadList.append(PiconSetInfo(url = url, path = path, filename = fileName, channelname = channelname, tunerType = tunerType, sref = sRef ))

		return downloadList
	
	#Dre
	def updatePiconsWithPiconMapper(self, sRef, setname):
		print("[PiconLoader] getting mapping from PiconMapper")
		url = ""
		if fileExists("/etc/enigma2/piconmapper.xml"):
			# replace local sRef with matching satellite sRef
			mappedsRef = self.findSatSref(sRef)
			if mappedsRef is None:
				print("[PiconLoader] no mapping found")
				return url
			else:
				print("[PiconLoader] mapping found")
				splittedsref = []
				splittedsref = mappedsRef.split("_")
				position = int(splittedsref[6][0:2], 16)
				
				url = PICONBASEURL + "DVB-S/" + setname + "/" + str(position) + "/" + mappedsRef + ".png"
				
				return url
		else:
			print("[PiconLoader] piconmapper.xml not found")
			return url
	
	#Dre
	def getSelection(self, ret):
		self.piconset = ret
		return ret
		
	#Dre
	def findSatSref(self, sRef):
		piconmapper = file("/etc/enigma2/piconmapper.xml", "r")
			
		piconmapperdom = etree.parse(piconmapper)
			
		xmldata = piconmapperdom.getroot()
			
		piconmapper.close()
			
		for x in xmldata.findall('piconmapping'):
			#if x.get('sref') == sRef:
				#return x.get('picon')
			if x.get('localsref') == sRef:
				return x.get('satsref')
		
		return None
		
def loadPiconSetsFromFile():
	fileName = resolveFilename(SCOPE_CONFIG, INFOFILE)
	print('[PiconLoader] loading installed picon set information from', fileName)
	if not fileExists(fileName):
		return
		
	pickleFile = open(fileName, "r")
	return pickle.load(pickleFile)
	
def savePiconSetsToFile(installedList):
	fileName = resolveFilename(SCOPE_CONFIG, INFOFILE)
	print('[PiconLoader] saving installed picon set information to', fileName)
	
	# remove pixmap informtation form list
	newList = []
	for listEntry in installedList:
		tmpEntry = list(listEntry)
		tmpEntry.pop(0) # pixmap can't be pickled, remove it
		newList.append(tuple(tmpEntry))
	
	pickle.dump(newList, open(fileName, "w"))
	config.merlin2.showPiconLoader.value = True
	config.merlin2.showPiconLoader.save()

def buildServiceRefString(service):
	sRef = service.toString()
	x = sRef.split(':')
	
	if len(x) < 11:
		return ""
		
	del x[x[10] and 11 or 10:] # remove name and empty path
	x[1]='0' #replace flags field
	
	if x[0] in ('4097', '8193'):
		name = '1_'+'_'.join(x[1:10])
	elif int(x[0]) == 1 and (int(x[6], 16) & 0xFFFF0000) == 0xEEEE0000:
		x[6] = '{:02X}'.format(0xEEEE0000)
		name = '1_'+'_'.join(x[1:10])
	else:
		name = '_'.join(x[:10])
	return name
	
def getServicesList(serviceRoot):
	serviceList = []
	eServiceCenterInstance = eServiceCenter.getInstance()
	services = eServiceCenterInstance.list(serviceRoot)
	
	while True:			
		service = services.getNext()
		if not service.valid(): #check if end of list
			break
		elif service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
			continue
			
		# alternative service?
		if service.flags & (eServiceReference.isGroup):
			altRoot = eServiceReference(service.toString())
			altList = eServiceCenterInstance.list(altRoot)
			if altList:
				while True:
					nextService = altList.getNext()
					if not nextService.valid():
						break
					service = nextService
					serviceList.append(service)
		else:
			serviceList.append(service)
		
	return serviceList
		
# main screen
class PiconLoader(Screen, DownloadListBuilder, XmlDownloader):

	skin = """
		<screen name="PiconLoader" position="center,center" size="660,320" title="" >
			<ePixmap pixmap="skin_default/buttons/red.png" position="50,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/green.png" position="190,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/yellow.png" position="330,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/blue.png" position="470,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<widget render="Label" source="key_red" position="50,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_green" position="190,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_yellow" position="330,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_blue" position="470,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="heading" position="5,50" zPosition="1" size="550,25" font="Regular;20" transparent="1"/>
			<ePixmap pixmap="skin_default/div-h.png" position="0,80" zPosition="1" size="660,2" />
			<widget source="list" render="Listbox" position="5,90" size="650,225">
				<convert type="TemplatedMultiContent">
					{"template": [
							MultiContentEntryPixmapAlphaBlend(pos = (5, 5), size = (100, 60), png = 0),
							MultiContentEntryText(pos = (110, 0), size = (545, 22), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_TOP, text = 2),
							MultiContentEntryText(pos = (110, 25), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Tuner:"))),
							MultiContentEntryText(pos = (180, 25), size = (75, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 1),
							MultiContentEntryText(pos = (270, 25), size = (100, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Provider:"))),
							MultiContentEntryText(pos = (360, 25), size = (295, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 7),
							MultiContentEntryText(pos = (110, 45), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Size:"))),
							MultiContentEntryText(pos = (180, 45), size = (75, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 5),
							MultiContentEntryText(pos = (270, 45), size = (100, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Author:"))),
							MultiContentEntryText(pos = (360, 45), size = (295, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 4),
							MultiContentEntryText(pos = (110, 65), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Date:"))),
							MultiContentEntryText(pos = (180, 65), size = (470, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 6),
							MultiContentEntryText(pos = (270, 65), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Path:"))),
							MultiContentEntryText(pos = (360, 65), size = (295, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 8),
						],
					"fonts": [gFont("Regular", 20), gFont("Regular", 18)],
					"itemHeight": 90
					}
				</convert>
			</widget>
		</screen>
		"""

	def __init__(self, session):
		self.session = session
		DownloadListBuilder.__init__(self)
		XmlDownloader.__init__(self, session)
		Screen.__init__(self, session)

		self["key_red"]		= StaticText(_("Exit"))
		self["key_green"]	= StaticText("")
		self["key_yellow"]	= StaticText("")
		self["key_blue"]	= StaticText(_("Picon Sets"))
		self["heading"]		= StaticText(_("Installed picon sets:"))

		self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
		{
			"cancel":	self.exit,
			"red":		self.exit,
			"blue":		self.selectPiconSet,
		}, -1)
		
		self.updateRemoveButtonsEnabled = False
		self.migrationRequired = False
		
		self.list = []
		self["list"] = List(self.list)
		
		self.onLayoutFinish.append(self.layoutFinished)
		self.onShown.append(self.startRun)

	def layoutFinished(self):
		self.setTitle(_("Picon Loader"))
		
	def exit(self):
		self.close()
		
	def startRun(self):
		self.onShown.remove(self.startRun)
		
		if not self.findRequiredTuner():
			self.hide()
			self.session.open(MessageBox, _("Sorry no DVB-S or DVB-C tuner found."), MessageBox.TYPE_ERROR)
			self.exit()
			
		self.list = loadPiconSetsFromFile()
		if self.list is not None and len(self.list):
			self.addPiconsToList()
			self.enableUpdateRemoveButtons()
		else:
			self.list = []
			config.merlin2.showPiconLoader.value = False
			config.merlin2.showPiconLoader.save()
			
		if self.migrationRequired:
			self.session.openWithCallback(self.processAnswer, MessageBox, _("You have installed unneeded picon sets for size 100x60 and/or 50x30. Do you want to remove them and create symlinks to size 220x132 instead?"), MessageBox.TYPE_YESNO, timeout=0)
			
	def processAnswer(self, answer):
		if answer:
			for entry in self.list:
				path = entry[8]
				
				if path.endswith('picon') or path.endswith('picon_50x30'):
					self.createSymLinks(path)
					self.list.remove(entry)
			
			savePiconSetsToFile(self.list)
			if not len(self.list):
				self.disableUpdateRemoveButtons()
			
			self["list"].setList(self.list)
			
	def createSymLinks(self, path):
		basepath = ""
		pos = path.rfind("/")
		if pos != 1:
			basepath = path[:pos+1]
		
		for dir in ["picon", "picon_50x30"]:
			createSymLink = False
			currentDir = "%s%s" %(basepath, dir)
			if pathExists(currentDir):
				if os_path.islink(currentDir):
					print("[PiconLoader] symlink %s already exists" %(currentDir))
					if os_path.realpath(currentDir) != "%spicon_220x132" %(basepath):
						print("[PiconLoader] symlink %s points to wrong directory - unlinking" %(currentDir))
						os_unlink(currentDir)
						createSymLink = True
					else:
						continue
				else:
					createSymLink = True
					try:
						print('[PiconLoader] deleting directory', currentDir)
						rmtree(currentDir)
					except OSError as error:
						createSymLink = False
						print('[PiconLoader] unable to delete directory', currentDir)
			else:
				createSymLink = True
				
			if createSymLink:
				print("[PiconLoader] creating symlink from %s to %spicon_220x132" %(currentDir, basepath))
				os_symlink("%spicon_220x132" %(basepath), currentDir)
				
			
	# check if at least one sat or cable tuner is installed
	def findRequiredTuner(self):
		for slot in nimmanager.nim_slots:
			#Dre: PiconMapper is valid too
			if slot.types is not None and (slot.isEnabled("DVB-S") or slot.isEnabled("DVB-S2") or slot.isEnabled("DVB-C") or config.plugins.piconmapper.activate.value):
				return True
		return False
		
	def enableUpdateRemoveButtons(self):
		self["key_green"].setText(_("Update"))
		self["key_yellow"].setText(_("Remove"))
		self["actions"].actions.update({"green":self.updateSet})
		self["actions"].actions.update({"yellow":self.removeSet})
		self.updateRemoveButtonsEnabled = True
		config.merlin2.showPiconLoader.value = True
		config.merlin2.showPiconLoader.save()

	def disableUpdateRemoveButtons(self):
		self["key_green"].setText("")
		self["key_yellow"].setText("")
		self.removeAction("green")
		self.removeAction("yellow")
		self.updateRemoveButtonsEnabled = False
		config.merlin2.showPiconLoader.value = False
		config.merlin2.showPiconLoader.save()
		
	def updateSet(self):
		if not self.updateRemoveButtonsEnabled:
			return
			
		self.installedList = self.list
		self.checkForUpdate()
		
	def removeSet(self):
		if not self.updateRemoveButtonsEnabled:
			return
			
		selectedItem = self["list"].getCurrent()
		path = selectedItem[8]
		choiceList = [(_("Yes"), "yesNoDelete"), (_("No"), "no"), (_("Yes and delete the picon directory"), "yesAndDelete")]
		self.session.openWithCallback(boundFunction(self.cbRemoveChoice, selectedItem), ChoiceBox, title = _("Do you want to remove the picon set which is installed in %s?") % path, list = choiceList)
		
	def cbRemoveChoice(self, item, result):
		if not result:
			return
			
		print('[PiconLoader] remove picon set:', result[1])
		
		if result[1] == "no":
			return
			
		path = item[8]
		if result[1] == "yesAndDelete":
			if os_path.exists(path):
				try:
					print('[PiconLoader] deleting directory', path)
					rmtree(path)
				except OSError as error:
					print('[PiconLoader] unable to delete directory', path)
				
				basepath = ""
				pos = path.rfind("/")
				if pos != 1:
					basepath = path[:pos+1]
				
				for folder in ['picon', 'picon_50x30']:
					if os_path.islink('%s%s' %(basepath, folder)) and os_path.realpath('%s%s' %(basepath, folder)) == path:
						print("[PiconLoader] Unlinking symlink %s%s" %(basepath, folder))
						os_unlink('%s%s' %(basepath, folder))
					
		self.list.remove(item)
		savePiconSetsToFile(self.list)
		
		if not len(self.list):
			self.disableUpdateRemoveButtons()
			
		self["list"].setList(self.list)
					
	def removeAction(self, descr):
		actions = self["actions"].actions
		if descr in actions:
			del actions[descr]
			
	def checkForUpdate(self):
		print('[PiconLoader] checking for updates')
		self.downloadXmlFile()
		
	# callback called when the XML download was finished
	def cbDownloadXml(self, xmlData):
		print('[PiconLoader] received XML file')
		#TODO: checken ob Fehler in getItemList ist, da ich dort einiges angepasst hab
		itemList = self.getItemList(xmlData)
		if not len(itemList):
			return

		downloadList = []
		for index, installedItem in enumerate(self.installedList):
			name = installedItem[3]
			date = installedItem[6]
			
			for item in itemList:
				if name == item.name and date < item.date:
					print('[PiconLoader] Updating picon set', name)
					downloadList.extend(self.buildDownloadList(installedItem))
					
					tmpItem = list(self.installedList[index])
					tmpItem[6] = item.date
					self.installedList[index] = tuple(tmpItem)
					
		if len(downloadList):
			self.session.openWithCallback(self.cbDownloadScreenClosed, FileDownload, downloadList)
		else:
			print('[PiconLoader] no updates available')
			self.session.open(MessageBox, _("There are no updates available."), MessageBox.TYPE_INFO)
			
		
	# picon download finished, update the preview
	def cbDownloadScreenClosed(self, result):
		savePiconSetsToFile(self.installedList)
		self["list"].setList(self.installedList)
			
	def selectPiconSet(self):
		self.session.openWithCallback(self.cbPiconSetSelectionClosed, PiconSetSelection, self.list)
		
	def cbPiconSetSelectionClosed(self, result):
		# rebuild the list
		if result:
			self.list = loadPiconSetsFromFile()
			self.addPiconsToList()
			self.enableUpdateRemoveButtons()
			
	def addPiconsToList(self):
		if not len(self.list):
			return
			
		newList = []
		for tupleEntry in self.list:
			tunerType = tupleEntry[0]
			previewSrefString = self.getPreviewService(tunerType)
			pngFile = tupleEntry[7] + "/" + previewSrefString + ".png"

			if fileExists(pngFile):
				# the preview list is designed to show HD picons (100x60), let's see if we need to scale down
				x, y = [int(i) for i in tupleEntry[4].split("x")]
				
				if (x > 100) or (y > 60): #  we need to scale down to HD picon size
					if (x / 100) >= (y / 60): # scale down to x
						newX = int(x / ( x / 100))
						newY = int(y / ( x / 100 ))
					else: # scale down to y
						newX = int(x / ( y / 60 ))
						newY = int(y / ( y / 60 ))
						
					from Components.AVSwitch import AVSwitch
					sc = AVSwitch().getFramebufferScale()
					picLoad = ePicLoad()
					picLoad.setPara((newX, newY, sc[0], sc[1], True, 1, "#00000000"))
					picLoad.startDecode(pngFile, False)
					png = picLoad.getData()
				else:
					png = LoadPixmap(cached = True, path = pngFile)
			else:
				pngFile = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/picon_default.png")
				png = LoadPixmap(cached = True, path = pngFile)
				
			listEntry = list(tupleEntry)
			if listEntry[4] in ('100x60', '50x30'):
				self.migrationRequired = True
			listEntry.insert(0, png)
			tupleEntry = tuple(listEntry)
			newList.append(tupleEntry)
			
		self.list = newList
		self["list"].setList(self.list)
		
	# get a service to show the preview picon (1st service in the 1st bouquet)
	def getPreviewService(self, tunerType):
		sRef = ""
		service = None
		if self.infoBarInstance is not None:
			servicelist = self.infoBarInstance.servicelist
			currentBouquet = servicelist.getRoot()
			bouquetList = servicelist.getBouquetList()
	
			isPartnerboxService = False
			i = 0
			stopLoop = False
			for i in range(len(bouquetList)):
				sRefForCheck = ""
				if stopLoop == True:
					break
				bouquet = bouquetList[i]
				servicelist = self.eServiceCenterInstance.list(bouquet[1])

				if not servicelist is None:
					while True:
						service = servicelist.getNext()
					
						if not service.valid(): #check if end of list
							break
						if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
							continue
						# alternative service?
						if service.flags & (eServiceReference.isGroup):
							altRoot = eServiceReference(service.toCompareString())
							altList = self.eServiceCenterInstance.list(altRoot)
							if altList:
								while True:
									nextService = altList.getNext()
									if not nextService.valid():
										break
									service = nextService
									break

						# remove partnerbox-services paths
						if service.getPath():
							isPartnerboxService = True
							service.setPath("")
					
						info = self.eServiceCenterInstance.info(service)
						if info is None:
							continue
						transponderInfo = info.getInfoObject(service, iServiceInformation.sTransponderData)
						if transponderInfo is not None:
							frontendData = ConvertToHumanReadable(transponderInfo)
							tuner_type = ""
							#if frontendData.has_key("tuner_type"):
							if "tuner_type" in frontendData:
								if frontendData["tuner_type"] == _("Cable"):
									tunerType  =  "DVB-C"
								elif frontendData["tuner_type"] == _("Satellite"):
									tunerType  =  "DVB-S"
								elif frontendData["tuner_type"] == _("Terrestrial"):
									tunerType = "DVB-T"
							if tuner_type == tunerType:
								break
						elif isPartnerboxService == True:
							isPartnerboxService = False
							if service.toString().upper().find("C00000") != -1 or service.toString().upper().find("820000") != -1 or service.toString().upper().find("11C000") != -1:
								stopLoop = True
								break
								
			sRef = service.toString()
			if sRef is not "":
				pos = sRef.rfind(':')
				if pos != -1:
					sRef = sRef[:pos].rstrip(':').replace(':','_')
		
		return sRef

class PiconSetFilter(Screen, ConfigListScreen):
	skin = """
		<screen position="center,center" size="560,320" title="" >
			<ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<widget render="Label" source="key_red" position="0,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_green" position="140,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget name="config" position="5,45" size="550,270" scrollbarMode="showOnDemand" />
		</screen>"""
		
	def __init__(self, session, gotTunerCab):
		self.session = session
		self.gotTunerCab = gotTunerCab
		Screen.__init__(self, session)
		
		self["key_red"] = StaticText(_("Cancel"))
		self["key_green"] = StaticText(_("Save"))
		
		ConfigListScreen.__init__(self, [ ])
		
		self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
		{
			"cancel":	self.cancel,
			"red":		self.cancel,
			"green":	self.save,
		}, -1)
		
		self.onLayoutFinish.append(self.layoutFinished)

	def layoutFinished(self):
		self.setTitle(_("Picon Set Filters"))
		config.plugins.piconloader.useFilter.addNotifier(self.buildConfigList, initial_call = True)

	def buildConfigList(self, configElement = None):
		cfgList = []
		cfgList.append(getConfigListEntry(_("Enable Picon filter:"), config.plugins.piconloader.useFilter))
		if config.plugins.piconloader.useFilter.value:
			cfgList.append(getConfigListEntry(_("Creator:"), config.plugins.piconloader.creator))
			cfgList.append(getConfigListEntry(_("Skin color:"), config.plugins.piconloader.filterByColor))
			cfgList.append(getConfigListEntry(_("External display:"), config.plugins.piconloader.extDisplay))
			if self.gotTunerCab:
				cfgList.append(getConfigListEntry(_("Cable provider:"), config.plugins.piconloader.cabProvider))
		self["config"].setList(cfgList)

	# save the config changes
	def save(self):
		for x in self["config"].list:
			x[1].save()
		config.plugins.piconloader.selectedCabProvider.value = config.plugins.piconloader.cabProvider.value
		config.plugins.piconloader.selectedExtDisplay.value = config.plugins.piconloader.extDisplay.value
		config.plugins.piconloader.save()
		config.plugins.piconloader.useFilter.removeNotifier(self.buildConfigList)
		
		self.close(True)

	# don't save any config changes
	def cancel(self):
		for x in self["config"].list:
			x[1].cancel()
		config.plugins.piconloader.useFilter.removeNotifier(self.buildConfigList)
		self.close(False)

# a screen to select between picon sets
class PiconSetSelection(Screen, ItemDownloader, DownloadListBuilder, XmlDownloader):

	skin = """
		<screen name="PiconSetSelection" position="center,center" size="660,320" title="">
			<ePixmap pixmap="skin_default/buttons/red.png" position="50,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/green.png" position="190,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/yellow.png" position="330,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/blue.png" position="470,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<widget render="Label" source="key_red" position="50,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_green" position="190,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_blue" position="470,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="heading" position="5,50" zPosition="1" size="550,25" font="Regular;20" transparent="1"/>
			<ePixmap pixmap="skin_default/div-h.png" position="0,80" zPosition="1" size="660,2" />
			<widget source="list" render="Listbox" position="5,90" size="650,225">
				<convert type="TemplatedMultiContent">
					{"template": [
							MultiContentEntryPixmapAlphaBlend(pos = (5, 5), size = (100, 60), png = 0),
							MultiContentEntryText(pos = (5, 70), size = (100, 20), font=1, flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER, text = 9),
							MultiContentEntryText(pos = (110, 0), size = (545, 22), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_TOP, text = 2),
							MultiContentEntryText(pos = (110, 25), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Tuner:"))),
							MultiContentEntryText(pos = (180, 25), size = (75, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 1),
							MultiContentEntryText(pos = (270, 25), size = (100, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Provider:"))),
							MultiContentEntryText(pos = (360, 25), size = (295, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 7),
							MultiContentEntryText(pos = (110, 45), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Size:"))),
							MultiContentEntryText(pos = (180, 45), size = (75, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 5),
							MultiContentEntryText(pos = (270, 45), size = (100, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Author:"))),
							MultiContentEntryText(pos = (360, 45), size = (295, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 4),
							MultiContentEntryText(pos = (110, 65), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Date:"))),
							MultiContentEntryText(pos = (180, 65), size = (470, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 6),
							MultiContentEntryText(pos = (270, 65), size = (65, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = (_("Path:"))),
							MultiContentEntryText(pos = (360, 65), size = (295, 20), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 8),
						],
					"fonts": [gFont("Regular", 20), gFont("Regular", 18)],
					"itemHeight": 90
					}
				</convert>
			</widget>
		</screen>
		"""

	def __init__(self, session, installedList):
		self.session = session
		self.installedList = installedList
		
		DownloadListBuilder.__init__(self)
		XmlDownloader.__init__(self, session)
		Screen.__init__(self, session)
		
		self["key_red"]		= StaticText(_("Cancel"))
		self["key_green"]	= StaticText("")
		self["key_blue"]	= StaticText(_("Filter"))
		self["heading"]		= StaticText(_("Available picon sets:"))
		
		# convert translated values to non-translated values
		if config.plugins.piconloader.cabProvider.value == _("n/a"):
			config.plugins.piconloader.cabProvider.value = "n/a"
		if config.plugins.piconloader.filterByColor.value == _("all"):
			config.plugins.piconloader.filterByColor.value = "all"
		if config.plugins.piconloader.creator.value == _("all"):
			config.plugins.piconloader.creator.value = "all"
		if config.plugins.piconloader.extDisplay.value == _("n/a"):
			config.plugins.piconloader.extDisplay.value = "n/a"

		self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
		{
			"cancel":	self.cancel,
			"red":		self.cancel,
			"blue":		self.editFilter,
		}, -1)
		
		self.completeItemList = []
		self.list = []
		self["list"] = List(self.list)
		self["list"].onSelectionChanged.append(self.cbSelectionChanged)
		
		self.onLayoutFinish.append(self.startRun)

	def startRun(self):
		self.setTitle(_("Picon Set Selection"))
		# download the picons.xml file
		self.downloadXmlFile()
		
	def cancel(self):
		self.cleanupTempFiles()
		self.close(False)

	def editFilter(self):
		self.session.openWithCallback(self.cbSetFilter, PiconSetFilter, self.gotTunerCab)
		
	def cbSetFilter(self, ret):
		if ret:
			self["list"].setIndex(0)
			if config.plugins.piconloader.useFilter.value:
				newList = []
				for item in self.completeItemList:
					creator = item[4]
					if creator != config.plugins.piconloader.creator.value and config.plugins.piconloader.creator.value != "all":
						continue
					colorType = item[10]
					if colorType != config.plugins.piconloader.filterByColor.value and colorType is not None and config.plugins.piconloader.filterByColor.value != "all":
						continue
					tunerType = item[1]
					if self.gotTunerCab and tunerType == "DVB-C":
						provider = item[7]
						if provider != config.plugins.piconloader.cabProvider.value and config.plugins.piconloader.cabProvider.value != "n/a":
							continue
					# don't show packages for other tuners when a cable provider filter is set
					if self.gotTunerCab and config.plugins.piconloader.cabProvider.value != "n/a" and tunerType != "DVB-C":
						continue
					extDisplay = item[11]
					if extDisplay is not None:
						if extDisplay != config.plugins.piconloader.extDisplay.value and config.plugins.piconloader.extDisplay.value != "n/a":
							continue
					newList.append(item)
				self.list = copy(newList)
				self["list"].setList(self.list)
			else:
				self["list"].setList(self.completeItemList)
		
	def installSet(self):
		selectedItem = self["list"].getCurrent()
		self.piconSetSelected(selectedItem)
		
	def piconSetSelected(self, item):
		if item:
			self.session.openWithCallback(self.cbPathSelected, PathSelection, item)
			
	def cbPathSelected(self, item):
		if item:
			path = item[8]
			choiceList = [(_("Yes"), "yesNoDelete"), (_("No"), "no"), (_("Yes, but empty the picon directory before"), "yesAndDelete")]
			self.session.openWithCallback(boundFunction(self.cbDownloadChoice, item), ChoiceBox, title = _("Do you want to install the picon set in %s?") % path, list = choiceList)
			
	def cbDownloadChoice(self, item, result):
		if not result:
			return
			
		print('[PiconLoader] install picon set:', result[1])
		
		if result[1] == "no":
			return
			
		path = item[8]
		downloadList = []
		if result[1] == "yesAndDelete":
			if os_path.exists(path):
				try:
					print('[PiconLoader] deleting directory', path)
					rmtree(path)
				except OSError as error:
					print('[PiconLoader] unable to delete directory', path)
					
			downloadList = self.buildDownloadList(item)
		elif result[1] == "yesNoDelete":
			downloadList = self.buildDownloadList(item)
			
		if len(downloadList):
			self.installedList.append(item)
			savePiconSetsToFile(self.installedList)
		
			self.session.openWithCallback(self.cbDownloadScreenClosed, FileDownload, downloadList)
		
	# callback called when the XML download was finished
	def cbDownloadXml(self, xmlData):
		print('[PiconLoader] received XML file')
		
		itemList = self.getItemList(xmlData)
		itemList.sort(reverse=True, key=self.sortFunc)
		if not len(itemList):
			return

		self.startDownload(itemList)

	def sortFunc(self, item):
		if item.size == "220x132":
			return 1000
		split = item.size.split("x")
		return int(split[0])
		
	# picon download finished, update the preview
	def cbDownloadScreenClosed(self, result):
		self.cleanupTempFiles()
		self.close(result)
		
	# remove temporary directories and files
	def cleanupTempFiles(self):
		for directory in self.delList:
			try:
				print('[PiconLoader] deleting directory', directory)
				rmtree(directory)
			except OSError as error:
				print('[PiconLoader] unable to delete directory', directory)
				
	# callback is called when a single download was finished
	def cbDownloadFinished(self, result, item):
		
		if not item.error:
			print('[PiconLoader] received picon file', item.filename)
			
			png = LoadPixmap(cached = True, path = item.filename)
			self.list.append((png, item.tunerType, item.description.encode('iso-8859-1'), item.name, item.author, item.size, item.date, item.provider, item.path, item.isInstalled, item.colorType, item.extDisplay))
			
	# callback is called when all downloads are finished
	def cbAllDownloadsFinished(self, result):
		self.completeItemList = copy(self.list)
		self.cbSetFilter(True)
		self.setInstallButton()
		
	def cbSelectionChanged(self):
		self.setInstallButton()
		
	def removeAction(self, descr):
		actions = self["actions"].actions
		if descr in actions:
			del actions[descr]
			
	def setInstallButton(self):
		if not len(self.list):
			return
			
		isInstalled = self["list"].getCurrent()[9]
		if isInstalled:
			self["key_green"].setText("")
			self.removeAction("green")
			self.removeAction("ok")
		else:
			self["key_green"].setText(_("Install"))
			self["actions"].actions.update({"green":self.installSet})
			self["actions"].actions.update({"ok":self.installSet})

# path selection screen
class PathSelection(Screen):

	skin = """
		<screen name="PiconLoaderPathSelection" position="center,center" size="760,520" title="">
			<ePixmap pixmap="skin_default/buttons/red.png" position="100,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/green.png" position="240,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/yellow.png" position="380,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/blue.png" position="520,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<widget render="Label" source="key_red" position="100,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_green" position="240,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="heading" position="5,50" zPosition="1" size="550,25" font="Regular;20" transparent="1"/>
			<ePixmap pixmap="skin_default/div-h.png" position="0,80" zPosition="1" size="760,2" />
			<widget source="list" render="Listbox" position="5,90" size="750,425">
				<convert type="TemplatedMultiContent">
					{"template": [
							MultiContentEntryText(pos = (0, 0), size = (370, 22), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 0),
							MultiContentEntryText(pos = (350, 0), size = (380, 22), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 1),
						],
					"fonts": [gFont("Regular", 20)],
					"itemHeight": 22
					}
				</convert>
			</widget>
		</screen>
		"""

	def __init__(self, session, item):
		self.session = session
		self.item = item
		Screen.__init__(self, session)
		
		self["key_red"]		= StaticText(_("Cancel"))
		self["key_green"]	= StaticText(_("Select"))
		self["heading"]		= StaticText(_("Please select a target path:"))

		self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
		{
			"cancel":	self.cancel,
			"ok":		self.select,
			"red":		self.cancel,
			"green":	self.select,
		}, -1)
		
		#if os_path.ismount("/data"): # weazle: Use data partition on 7020hd and V2 boxes
		folder = ""
		if item[5] == "96x64":
			folder = "_oled"
		elif item[5] != "100x60":
			folder = "_%s" %(item[5])
				
		self.list = [(eEnv.resolve("${datadir}/enigma2/picon%s" %(folder)), _("Flash"))]
		
		dirList = os_listdir("/media")
		blockList =  ["hdd","cf","sdcard","usb"]

		if fileExists("/proc/mounts"):
			mountsFile = open("/proc/mounts" ,"r")
			for line in mountsFile:
				entry = line.split()
				if entry[2] in ["nfs", "nfs4", "smbfs", "cifs"]:
					if entry[1].startswith("/media/"):
						print("[PiconLoader] - found network drive. Exclude ", entry[1])
						blockList.append(entry[1][7:])
			mountsFile.close()
		
		for dir in dirList:
			if dir in blockList:
				continue
			if os_path.ismount("/media/%s" %(dir)) or (os_path.islink("/media/%s" %(dir)) and os_path.ismount(os_path.realpath("/media/%s" %(dir)))):
				self.list.extend([("/media/%s/picon%s" %(dir, folder), _("generic mount"))])
			

		if os_path.ismount("/media/cf") or (os_path.islink("/media/cf") and os_path.ismount(os_path.realpath("/media/cf"))):
			self.list.extend([("/media/cf/picon%s" %(folder), _("CF-card"))])
		if os_path.ismount("/media/usb") or (os_path.islink("/media/usb") and os_path.ismount(os_path.realpath("/media/usb"))):
			self.list.extend([("/media/usb/picon%s" %(folder), _("USB-stick"))])
		if os_path.ismount("/media/sdcard") or (os_path.islink("/media/sdcard") and os_path.ismount(os_path.realpath("/media/sdcard"))):
			self.list.extend([("/media/sdcard/picon%s" %(folder), _("SD-card"))])			
		if os_path.ismount("/data"):
			self.list.extend([("/data/picon%s" %(folder), _("data partition"))])
		if os_path.ismount("/dreambox-data"):
			self.list.extend([("/dreambox-data/picon%s" %(folder), _("data partition"))])

		self["list"] = List(self.list)

		self.onLayoutFinish.append(self.layoutFinished)

	def layoutFinished(self):
		self.setTitle(_("Picon Loader Path Selection"))

	def cancel(self):
		self.close(None)
				
	def select(self):
		selectedPath = self["list"].getCurrent()[0]
		listEntry = list(self.item)
		listEntry[8] = selectedPath
		item = tuple(listEntry)
		self.close(item)
		
# a download class with progress screen
class FileDownload(Screen, ItemDownloader):

	skin = """
		<screen name="FileDownload" position="center,center" size="560,215" title="">
			<ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
			<widget render="Label" source="key_red" position="0,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="key_green" position="140,0" size="140,40" zPosition="5" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
			<widget render="Label" source="filesTotalLabel" position="5,50" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget render="Label" source="filesTotal" position="220,50" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget render="Label" source="filesCurrentLabel" position="5,75" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget render="Label" source="filesCurrent" position="220,75" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget render="Label" source="errorsCurrentLabel" position="5,100" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget render="Label" source="errorsCurrent" position="220,100" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget render="Label" source="progressBarLabel" position="5,125" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget name="progressBar" position="220,130" pixmap="skin_default/progress.png" zPosition="1" borderWidth="2" size="335,15" />
			<widget name="symLink5030Label" position="5,150" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget name="symLink5030Text" position="220,150" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget name="symLink10060Label" position="5,175" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
			<widget name="symLink10060Text" position="220,175" zPosition="1" size="200,25" font="Regular;22" transparent="1"/>
		</screen>
		"""

	def __init__(self, session, downloadList):
		self.session = session
		self.downloadList = downloadList
		
		Screen.__init__(self, session)
		
		self["key_red"]			= StaticText()
		self["key_green"]		= StaticText()
		self["filesTotalLabel"]		= StaticText(_("Files to download:"))
		self["filesCurrentLabel"]	= StaticText(_("Files received:"))
		self["errorsCurrentLabel"]	= StaticText(_("Download errors:"))
		self["filesTotal"]		= StaticText()
		self["filesCurrent"]		= StaticText("0")
		self["errorsCurrent"]		= StaticText("0")
		self["progressBarLabel"]	= StaticText(_("Download progress:"))
		self["progressBar"]		= ProgressBar()
		self["symLink5030Label"] = Label(_("Symlink to 50x30:"))
		self["symLink10060Label"] = Label(_("Symlink to 100x60:"))
		self["symLink5030Text"] = Label("")
		self["symLink10060Text"] = Label("")
		
		self["symLink5030Label"].hide()
		self["symLink10060Label"].hide()
		self["symLink5030Text"].hide()
		self["symLink10060Text"].hide()

		self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
		{
		}, -1)
		
		self.onLayoutFinish.append(self.startRun)

	def startRun(self):
		self.setTitle(_("File Download"))
		self["filesTotal"].setText(str(len(self.downloadList)))
		
		if len(self.downloadList) > 0:
			self.numDownloaded = 0
			self.numErrors = 0
			self.errorList = []
			self.progressFactor = float(100) / len(self.downloadList)
			self["progressBar"].setValue(0)
			
			self.createPaths()
			
	def createPaths(self):
		pathList = []
		for item in self.downloadList:
			if not item.path in pathList and not os_path.exists(item.path):
				print("[PiconLoader] creating missing picon directory", item.path)
				pathList.append(item.path)
				try:
					print('[PiconLoader] creating directory', item.path)
					os_mkdir(item.path)
				except OSError as error:
					print('[PiconLoader] unable to create directory', item.path)
				else:
					basepath = ""
					pos = item.path.rfind("/")
					if pos != 1:
						basepath = item.path[:pos+1]
						
						# only create symlinks when picon size is 220x132!						
						if item.path.find('picon_220x132') != -1:
							for dir in ["picon", "picon_50x30"]:
								createSymLink = False
								currentDir = "%s%s" %(basepath, dir)
								if pathExists(currentDir):
									if os_path.islink(currentDir):
										print("[PiconLoader] symlink %s already exists" %(currentDir))
										if os_path.realpath(currentDir) != "%spicon_220x132" %(basepath):
											print("[PiconLoader] symlink %s points to wrong directory - unlinking" %(currentDir))
											os_unlink(currentDir)
											createSymLink = True
										else:
											if dir == "picon_50x30":
												self["symLink5030Label"].show()
												self["symLink5030Text"].show()
												self["symLink5030Text"].setText(_("Already exists"))
											elif dir == "picon":
												self["symLink10060Label"].show()
												self["symLink10060Text"].show()
												self["symLink10060Text"].setText(_("Already exists"))
											continue
									else:
										createSymLink = True
										try:
											print('[PiconLoader] deleting directory', currentDir)
											rmtree(currentDir)
										except OSError as error:
											createSymLink = False
											print('[PiconLoader] unable to delete directory', currentDir)
								else:
									createSymLink = True
				
								if createSymLink:
									print("[PiconLoader] creating symlink from %s to %spicon_220x132" %(currentDir, basepath))
									os_symlink("%spicon_220x132" %(basepath), currentDir)
								
									if dir == "picon_50x30":
										self["symLink5030Label"].show()
										self["symLink5030Text"].show()
										self["symLink5030Text"].setText(_("Created"))
									elif dir == "picon":
										self["symLink10060Label"].show()
										self["symLink10060Text"].show()
										self["symLink10060Text"].setText(_("Created"))
			elif not item.path in pathList and os_path.exists(item.path):
				pathList.append(item.path)

				basepath = ""
				pos = item.path.rfind("/")
				if pos != 1:
					basepath = item.path[:pos+1]

					# only create symlinks when picon size is 220x132!						
					if item.path.find('picon_220x132') != -1:						
						for dir in ["picon", "picon_50x30"]:
							createSymLink = False
							currentDir = "%s%s" %(basepath, dir)
							if pathExists(currentDir):
								if os_path.islink(currentDir):
									print("[PiconLoader] symlink %s already exists" %(currentDir))
									if os_path.realpath(currentDir) != "%spicon_220x132" %(basepath):
										print("[PiconLoader] symlink %s points to wrong directory - unlinking" %(currentDir))
										os_unlink(currentDir)
										createSymLink = True
									else:
										if dir == "picon_50x30":
											self["symLink5030Label"].show()
											self["symLink5030Text"].show()
											self["symLink5030Text"].setText(_("Already exists"))
										elif dir == "picon":
											self["symLink10060Label"].show()
											self["symLink10060Text"].show()
											self["symLink10060Text"].setText(_("Already exists"))
										continue
								else:
									createSymLink = True
									try:
										print('[PiconLoader] deleting directory', currentDir)
										rmtree(currentDir)
									except OSError as error:
										createSymLink = False
										print('[PiconLoader] unable to delete directory', currentDir)
							else:
								createSymLink = True
				
							if createSymLink:
								print("[PiconLoader] creating symlink from %s to %s/picon_220x132" %(currentDir, basepath))
								os_symlink("%spicon_220x132" %(basepath), currentDir)
								
								if dir == "picon_50x30":
									self["symLink5030Label"].show()
									self["symLink5030Text"].show()
									self["symLink5030Text"].setText(_("Created"))
								elif dir == "picon":
									self["symLink10060Label"].show()
									self["symLink10060Text"].show()
									self["symLink10060Text"].setText(_("Created"))
								
		self.startDownload(self.downloadList)			
					
	def exit(self):
		self.close(True)
			
	# callback is called when a single download was finished
	def cbDownloadFinished(self, result, item):
		if not item.error:
			self.numDownloaded += 1
			self["filesCurrent"].setText(str(self.numDownloaded))
		else:
			self.numErrors += 1
			self.errorList.append(("%s:::%s:::%s\n" %(item.sref, item.tunerType, item.channelname)))
			self["errorsCurrent"].setText(str(self.numErrors))
			
		self["progressBar"].setValue(int(self.progressFactor * (self.numDownloaded + self.numErrors)))
		
	# callback is called when all downloads are finished
	def cbAllDownloadsFinished(self, result):
		print("[PiconLoader] clearing pixmap cache")
		pixmap_cache.clear()
		
		if len(self.errorList)>0:
			self.writeLog(self.errorList)
		
		self["key_green"].setText(_("OK"))
		self["actions"].actions.update({"green":self.exit})
		self["actions"].actions.update({"ok":self.exit})
		self["actions"].actions.update({"cancel":self.exit})
	
	def writeLog(self, errorList):
		with open("/etc/enigma2/piconloader.log", "w") as f:
			f.writelines(errorList)
		f.close()