#
# PiconMapper
#
# Coded by dre (c) 2013
# Support: www.dreamboxtools.de
# E-Mail: dre@dreamboxtools.de
#
# This plugin is open source but it is NOT free software.
#
# This plugin may only be distributed to and executed on hardware which
# is licensed by Dream Multimedia GmbH.
# In other words:
# It's NOT allowed to distribute any parts of this plugin or its source code in ANY way
# to hardware which is NOT licensed by Dream Multimedia GmbH.
# It's NOT allowed to execute this plugin and its source code or even parts of it in ANY way
# on hardware which is NOT licensed by Dream Multimedia GmbH.
#
# If you want to use or modify the code or parts of it,
# you have to keep MY license and inform me about the modifications by mail.

from Components.ActionMap import ActionMap, HelpableActionMap
from Components.ConfigList import ConfigListScreen, ConfigList
from Components.Label import Label
from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaBlend
from Components.Pixmap import Pixmap
from Components.Sources.List import List
from Components.config import getConfigListEntry, config, ConfigSubsection, ConfigSelection, ConfigOnOff, ConfigText, NoSave, ConfigNumber, ConfigSubList
from Screens.HelpMenu import HelpableScreen
from Screens.InputBox import InputBox
from Screens.MessageBox import MessageBox
from Screens.Screen import Screen
from ServiceReference import ServiceReference
from Tools.Directories import fileExists, SCOPE_SKIN_IMAGE, SCOPE_CURRENT_SKIN, resolveFilename
from Tools.HardwareInfo import HardwareInfo
from Tools.LoadPixmap import LoadPixmap
from enigma import eListboxPythonMultiContent, gFont, loadPNG, eServiceCenter, eServiceReference, RT_HALIGN_RIGHT, eEnv, eConsoleAppContainer
import os
import xml.etree.cElementTree

config.plugins.piconmapper = ConfigSubsection()
config.plugins.piconmapper.activate = ConfigOnOff(default = False)
config.plugins.piconmapper.sats = ConfigSelection(default = "none", choices = [("astra", _("Astra")), ("hotbird", _("Hotbird")), ("astra2", _("Astra2")), ("astrahotbird", _("Astra & Hotbird")), ("astraastra2", _("Astra & Astra2")), ("hotbirdastra2", _("Hotbird & Astra2")), ("hotbirdastraastra2", _("Hotbird & Astra & Astra2")), ("none", _("None")) ])

def buildServiceRefString(service):
	sRef = service.toString()
	x = sRef.split(':')
	
	if len(x) < 11:
		return ""

	# streamrelay
	if x[6] == "21" and len(x) >= 11:
		pos = x[10].rfind("/")
		streamrelayref = x[10][pos+1:]
		x = streamrelayref.split("%3a")
		
	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

class PiconMapperConfigScreen(Screen, HelpableScreen, ConfigListScreen):
	skin = """
		<screen position="center,center" size="600,220" title="PiconMapper - Config">
			<widget name="config" position="0,0" size="600,180" scrollbarMode="showOnDemand" />
			<widget name="ButtonGreentext" position="20,180" size="350,40" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="ButtonGreen" pixmap="skin_default/buttons/button_green.png" position="0,190" zPosition="10" size="16,16" transparent="1" alphatest="on" />
		</screen>
		"""
		
	def __init__(self,session):
		self.skin = PiconMapperConfigScreen.skin
		self.session = session
		Screen.__init__(self, session)
		HelpableScreen.__init__(self)
		
		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
		{
			"cancel":	(self.close, _("Close")),
		}, -1)
		
		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"green":	(self.saveSelection, _("Select")),
		}, -1)
		
		self["ButtonGreen"] = Pixmap()
		self["ButtonGreentext"] = Label(_("Save settings"))
		
		self.container = eConsoleAppContainer()
		self.appClosed_conn = self.container.appClosed.connect(self.runFinished)
		self.dataAvail_conn = self.container.dataAvail.connect(self.dataAvail)
		self.stdErrAvail_conn = self.container.stderrAvail.connect(self.printErr)
		
		self.sats = config.plugins.piconmapper.sats.value
		
		self.list = []

		ConfigListScreen.__init__(self, self.list)
		self.createConfig()

	def keyLeft(self):
		ConfigListScreen.keyLeft(self)
		self.createConfig()
		
	def keyRight(self):
		ConfigListScreen.keyRight(self)
		self.createConfig()

	def printErr(self, data):
		print "error", data
		self.downloadMsg["text"].setText( _("An error occurred. Please download via AddonManager"))
		self.downloadMsg["Text"].setText( _("An error occurred. Please download via AddonManager"))
		self.downloadMsg["InfoPixmap"].hide()
		self.downloadMsg["ErrorPixmap"].show()	
		
	def dataAvail(self, data):
		print data
		
	def runFinished(self, retval):
		if retval == 0:
			if self.run == 0:
				self.run = 1
				self.container.execute(self.commandList[self.run])
			elif self.run == 1:
				self.downloadMsg["text"].setText("Download finished")
				self.downloadMsg["Text"].setText("Download finished")
				self.close()

	def checkDownloadAnswer(self, answer):
		if answer:
			self.downloadMsg = self.session.open(MessageBox, _("Download started"), MessageBox.TYPE_INFO)
			self.downloadMsg.setTitle(_("PiconMapper"))
			self.run = 0
			self.commandList = ["apt-get update","apt-get -f --allow-unauthenticated install enigma2-piconmapper-%s" %(config.plugins.piconmapper.sats.value)]
			self.container.execute(self.commandList[self.run])
		else:
			self.close()
		
	def saveSelection(self):
		for x in self["config"].list:
			x[1].save()
			
		if config.plugins.piconmapper.activate.value:
			if self.sats != config.plugins.piconmapper.sats.value or config.plugins.piconmapper.sats.value == "none":
				if config.plugins.piconmapper.sats.value == "none":
					msg = self.session.openWithCallback(self.checkAnswer, MessageBox, _("PiconMapper requires a sat to map channels. Do you want to set this now?"), MessageBox.TYPE_YESNO)
					msg.setTitle(_("PiconMapper"))
				else:
					msg = self.session.openWithCallback(self.checkDownloadAnswer, MessageBox, _("Do you want to download the required mapping file now?"), MessageBox.TYPE_YESNO)
					msg.setTitle(_("PiconMapper"))
		else:	
			self.close()

	def checkAnswer(self, answer):
		if not answer:
			self.close()

	def createConfig(self, configElement=""):
		self.list = []
				
		self.list.append(getConfigListEntry(_("Activate Picon Mapper:"), config.plugins.piconmapper.activate))
		if config.plugins.piconmapper.activate.value:
			self.list.append(getConfigListEntry(_("Mapping files for:"), config.plugins.piconmapper.sats))
		else:
			config.plugins.piconmapper.sats.value = "none"

		self["config"].setList(self.list)

class PiconMapperScreen(Screen, HelpableScreen):
	skin = """
		<screen position="center,center" size="500,480" title="PiconMapper">
			<widget name="description" position="0,0" size="150,25" font="Regular;18" transparent="1" />
			<widget name="unmappedsref" position="150,0" size="250,25" font="Regular;18" transparent="1" />
			<widget name="HDCheckbox" position="400,0" size="25,25" zPosition="10" transparent="1" alphatest="blend" />
			<widget name="hd" position="450,0" size="50,25" font="Regular;18" transparent="1" />
			<widget source="list" render="Listbox" position="0,25" size="500,360" scrollbarMode="showOnDemand">
				<convert type="TemplatedMultiContent">
					{"template": [
									MultiContentEntryText(pos=(0,0), size=(500,25), text=0), 
					],
					"fonts": [gFont("Regular",18)],
					"itemHeight": 30
					}
				</convert>
			</widget>				
			<widget name="instruction" position="0,385" size="500,40" font="Regular;18" transparent="1" />
			<widget name="ButtonYellowtext" position="50,433" size="400,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="ButtonYellow" pixmap="skin_default/buttons/button_yellow.png" position="10,435" zPosition="10" size="16,16" transparent="1" alphatest="blend" />
<widget name="ButtonGreentext" position="50,453" size="400,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="ButtonGreen" pixmap="skin_default/buttons/button_green.png" position="10,455" zPosition="10" size="16,16" transparent="1" alphatest="blend" />			
		</screen>
		"""
		
	def __init__(self, session, serviceList):
		self.skin = PiconMapperScreen.skin
		self.session = session
		Screen.__init__(self, session)
		HelpableScreen.__init__(self)
		
		self.serviceList = serviceList
		self.serviceIterator = iter(self.serviceList)
		self.serviceCounter = 0
		self.searchName = ""
		self.preparationRunning = False
		self.SDHD = ""
		self.toggleUsed = False
		
		
		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
		{
			"ok":		(self.addMapping, _("Add mapping")),
			"cancel":	(self.closeOrSkip, _("Close")),
		}, -1)
		
		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"yellow": 	(self.searchChannel, _("Manual search")),
			"green":	(self.toggleHDFlag, _("Toggle HD flag")),
		}, -1)
		
		self["ButtonYellow"] = Pixmap()
		self["ButtonYellowtext"] = Label(_("Manually enter (part of) channel name"))
		self["ButtonGreen"] = Pixmap()
		self["ButtonGreentext"] = Label(_("Toggle HD flag"))		
		self["description"] = Label(_("Channel:"))
		self["HDCheckbox"] = Pixmap()
		self["hd"] = Label(_("HD"))
		self["unmappedsref"] = Label()
		self["instruction"] = Label(_("Select entry and press OK to add mapping"))

		self.PiconEntryList = []
		self["list"] = List([])
		
		self.getChannelData()
		
		self.onShown.append(self.buildScreen)

	def searchChannel(self):
		self.session.openWithCallback(self.setChannelData, InputBox, title = _("Enter search string"), text=self.channelname)
		
	def toggleHDFlag(self):
		if len(self.PiconEntryList) > 0:
			if self.toggleUsed:
				self.toggleUsed = False
			else:
				self.toggleUsed = True
				
			icon = ""
	
			if (self.SDHD == "19" and self.toggleUsed) or (self.SDHD == "1" and self.toggleUsed == False):
				icon = resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_off.png")
			elif (self.SDHD == "19" and self.toggleUsed == False) or (self.SDHD == "1" and self.toggleUsed):
				icon = resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_on.png")
			if icon:
				self["HDCheckbox"].instance.setPixmapFromFile(icon)						
			
		
	def setChannelData(self, searchName):
		self.searchName = searchName
		self.buildScreen()

	def closeOrSkip(self):
		if self.serviceCounter == len(self.serviceList):
			self.close()
		else:
			info = self.session.openWithCallback(self.checkAnswer,MessageBox,_("Continue mapping with next channel?"), MessageBox.TYPE_YESNO)
			info.setTitle(_("PiconMapper"))

	def checkAnswer(self, ret):
		if not ret:
			self.close()
		else:
			self.getChannelData()
			self.buildScreen()
		
	def getNextService(self):
		name = ""
		sRef = ""
		try:
			service = self.serviceIterator.next()
			self.isPartnerbox = False
				
			if service.getPath():
				self.isPartnerbox = True
				
			eServiceCenterInstance = eServiceCenter.getInstance()
			info = eServiceCenterInstance.info(service)
			sRef = buildServiceRefString(service)
			
			name = info.getName(service) or ServiceReference(service).getServiceName() or ""
			name = name.replace('\xc2\x86', '').replace('\xc2\x87', '')
			
			print "[PiconMapper] current channel: ", name
		
			if self.isPartnerbox:
				pos = name.rfind("(")
				name = name[:pos-1]
				
			self.serviceCounter += 1
					
		except StopIteration:
			print "[PiconMapper] no more items"
			self.close()
			
		return name, sRef

	def getChannelData(self):
		# read all entries from sref2name and store it in a dictionary
		self.channelnamedict = {}
		
		self.preparationRunning = True
		
		if fileExists("/usr/share/enigma2/sref2name"):
			lamefile = file("/usr/share/enigma2/sref2name","r")
			
			for line in lamefile.readlines():
				linesplit = line.split(":::")
				linesplit[1] = linesplit[1].strip("\n").strip("\r")
				self.channelnamedict[linesplit[0]] = linesplit[1]

			
			lamefile.close()
			
			self.channelname, self.unmappedsref = self.getNextService()
		
	def buildScreen(self):
		while self.preparationRunning == False:
			pass
		else:
			self.PiconEntryList = []
		
			self.index = 0
		
			if self.channelname != "" and self.unmappedsref != "":
				#search channel in channelnamedict and add it to the list		
				for ref, name in self.channelnamedict.iteritems():
					self.matchFound = False	
					if self.searchName == "":
						if name.replace('\xc2\x86', '').replace('\xc2\x87', '').upper() == self.channelname.replace('\xc2\x86', '').replace('\xc2\x87', '').upper():
							refarray = ref.split(":")
							self.matchFound = True
					elif self.searchName != "" and self.searchName != None:
						if self.searchName.upper() in name.replace('\xc2\x86', '').replace('\xc2\x87', '').upper():
							refarray = ref.split(":")
							self.matchFound = True
				
					if self.matchFound:
						unmappedarray = self.unmappedsref.split("_")
				
						for i in range(len(refarray)):
							while(refarray[i].startswith("0")):
								refarray[i] = refarray[i][1:]
						 
						self.SDHD = unmappedarray[2]

						if self.SDHD == "1F":
							self.SDHD = "19"
						
						#generate the service reference of the sat channel  
						mappedsref = unmappedarray[0] + "_" + unmappedarray[1] + "_" + self.SDHD + "_" + refarray[0] + "_" + refarray[2] + "_" + refarray[3] + "_" + refarray[1] + "_0_0_0"

						satposition = float(int(refarray[1][:2],16)) / 10
						name = name.replace('\xc2\x86', '').replace('\xc2\x87', '')
						name = name + " (%.1f)" %(satposition)
				
						self.PiconEntryList.append((name,self.unmappedsref, mappedsref))

			self["list"].setList(self.PiconEntryList)
			self["list"].index = self.index

			if not self.unmappedsref is None:
				if self.channelname != "":
					servicename = self.channelname
				else:
					servicename = "N/V"

			if self.searchName != "":
				self["unmappedsref"].setText(self.searchName)
			else:
				self["unmappedsref"].setText(servicename)
				
			if self.SDHD == "19":
				icon = resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_on.png")
			else:
				icon = resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_off.png")
			if icon:
				self["HDCheckbox"].instance.setPixmapFromFile(icon)
		
	def addMapping(self):
		if not fileExists("/etc/enigma2/piconmapper.xml"):
			os.system("touch /etc/enigma2/piconmapper.xml")
			os.system("echo '<piconmappings></piconmappings>' > /etc/enigma2/piconmapper.xml")

		if len(self.PiconEntryList) > 0:			
			if self.SDHD == "1" and self.toggleUsed:
				newref = self.PiconEntryList[self["list"].getIndex()][2].replace("1_0_1","1_0_19")
			elif self.SDHD == "19" and self.toggleUsed:
				newref = self.PiconEntryList[self["list"].getIndex()][2].replace("1_0_19","1_0_1")
			else:
				newref = self.PiconEntryList[self["list"].getIndex()][2]
		
			if self.channelname == "":
				self.channelname = "N/V"
			self.updatePiconMapping(self.channelname, self.unmappedsref, newref)
		
	def updatePiconMapping(self, channel, sref, mappedsref):
		srefmapped = False
			
		piconmapper = file("/etc/enigma2/piconmapper.xml", "r")
			
		piconmapperdom = xml.etree.cElementTree.parse(piconmapper)
			
		xmldata = piconmapperdom.getroot()
			
		for x in xmldata.findall('piconmapping'):
			if x.get('localsref') == sref and x.get('satsref') == mappedsref:
				srefmapped = True
			elif x.get('localsref') == sref:
				x.set('satsref', mappedsref)
				srefmapped = True
				piconmapperdom.write("/etc/enigma2/piconmapper.xml")				
						
		if srefmapped == False:
			child = xml.etree.cElementTree.Element('piconmapping')
			xmldata.append(child)
			child.attrib['satsref'] = mappedsref
			child.attrib['localsref'] = sref
			child.attrib['channel'] = channel
			piconmapperdom.write("/etc/enigma2/piconmapper.xml")
				
		piconmapper.close()
			
		info = self.session.open(MessageBox,_("Picon mapping was updated."), MessageBox.TYPE_INFO, timeout=2)
		info.setTitle(_("PiconMapper"))
		if len(self.serviceList) == 1:
			self.close(0)
		else:
			self.searchName = ""
			self.getChannelData()
			self.buildScreen()

class PiconMappingViewerScreen(Screen, HelpableScreen):
	skin = """
		<screen position="center,center" size="650,435" title="PiconMapper - Viewer">
			<widget source="list" render="Listbox" position="0,0" size="650,360" scrollbarMode="showOnDemand">
				<convert type="TemplatedMultiContent">
					{"template": [
									MultiContentEntryPixmapAlphaBlend(pos=(0,0), size=(100,60), png=0),
									MultiContentEntryText(pos=(110,0), size=(140,20), text=_('Channel:')), 
									MultiContentEntryText(pos=(110,20), size=(140,20), text=_('Local:')), 
									MultiContentEntryText(pos=(110,40), size=(140,20), text=_('Sat:')), 																		
									MultiContentEntryText(pos=(250,0), size=(370,20), flags=RT_HALIGN_RIGHT, text=1),
									MultiContentEntryText(pos=(250,20), size=(370,20), flags=RT_HALIGN_RIGHT, text=2),	
									MultiContentEntryText(pos=(250,40), size=(370,20), flags=RT_HALIGN_RIGHT, text=3),																	
					],
					"fonts": [gFont("Regular",18)],
					"itemHeight": 60
					}
				</convert>
			</widget>			
			<widget name="ButtonRedtext" position="50,405" size="300,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="ButtonRed" pixmap="skin_default/buttons/button_red.png" position="10,405" zPosition="10" size="16,16" transparent="1" alphatest="on" />
			<widget name="ButtonMenutext" position="50,375" size="200,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="ButtonMenu" pixmap="skin_default/buttons/key_menu.png" position="10,375" zPosition="10" size="40,20" transparent="1" alphatest="on" />
		</screen>
		"""

	def __init__(self, session, args=0):
		self.skin = PiconMappingViewerScreen.skin
		self.session = session
		Screen.__init__(self, session)
		HelpableScreen.__init__(self)
		
		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
		{
			"ok":		(self.deleteMapping, _("Delete selected mapping")),
			"cancel":	(self.close, _("Close")),
		}, -1)

		self["MenuActions"] = HelpableActionMap(self, "MenuActions",
		{
			"menu":		(self.openConfig, _("Show config")),
		}, -1)
		
		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"red":	(self.deleteMapping, _("Delete selected mapping")),
		}, -1)		
		
		self["ButtonMenu"] = Pixmap()
		self["ButtonMenutext"] = Label(_("Open config"))
		
		self["ButtonRed"] = Pixmap()
		self["ButtonRedtext"] = Label(_("Delete selected mapping"))
		
		self.list = []
		self.oldindex = 0
		self["list"] = List(self.list)
		
		self.onShown.append(self.buildScreen)

	def openConfig(self):
		self.session.open(PiconMapperConfigScreen)

	def buildEntry(self, picon, channel, localsref, satsref):
		piconpng = LoadPixmap(cached=True, path=picon)
		
		return [piconpng, channel, localsref, satsref]
		
	def buildScreen(self):
	
		self.list = []
		
		if fileExists("/usr/share/enigma2/piconmapper.xml"):
			os.system("mv /usr/share/enigma2/piconmapper.xml /etc/enigma2/piconmapper.xml")
	
		if fileExists("/etc/enigma2/piconmapper.xml"):
			
			piconmapper = file("/etc/enigma2/piconmapper.xml", "r")
			
			piconmapperdom = xml.etree.cElementTree.parse(piconmapper)
			
			xmldata = piconmapperdom.getroot()
			
			piconmapper.close()
			
			for x in xmldata.findall('piconmapping'):
				localsref = x.get('localsref')
				satsref = x.get('satsref')
				channel = x.get('channel')
			
				if localsref is None:
					picon = resolveFilename(SCOPE_CURRENT_SKIN, "picon_default.png")
				else:
					for path in (eEnv.resolve('${datadir}/enigma2/picon/'),'/media/cf/picon/','/media/usb/picon/','/data/picon/'):
						picon = path + localsref + ".png"
						if fileExists(picon):
							break
						else:
							picon = resolveFilename(SCOPE_CURRENT_SKIN, "picon_default.png")
							if not fileExists(picon):
								picon = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/picon_default.png")

				self.list.append((picon, channel, localsref, satsref))	

		self["list"].setList(self.list)
		self["list"].setBuildFunc(self.buildEntry)
		self["list"].setIndex(self.oldindex)
		
	def deleteMapping(self):
		self.index = self["list"].getIndex()
		self.oldindex = self.index
		
		if fileExists("/etc/enigma2/piconmapper.xml"):
		
			piconmapper = file("/etc/enigma2/piconmapper.xml", "r")
			
			piconmapperdom = xml.etree.cElementTree.parse(piconmapper)
			
			xmldata = piconmapperdom.getroot()
		
			writeUpdate = False
		
			for x in xmldata.findall('piconmapping'):
				if x.get('localsref') == self.list[self.index][2] and x.get('satsref') == self.list[self.index][3]:
					xmldata.remove(x)
					writeUpdate = True
					info = self.session.open(MessageBox,_("Picon mapping was deleted."), MessageBox.TYPE_INFO, timeout=	5)
					info.setTitle(_("PiconMapper"))
		
			if writeUpdate == True:
				piconmapperdom.write("/etc/enigma2/piconmapper.xml")
		
			piconmapper.close()
