# MerlinContextMenu
#
# Coded by dre (c) 2015 - 2016
# Support: www.dreambox-tools.info
# E-Mail: dre@dreambox-tools.info
#
# This module is open source but it is NOT free software.
#
# This module 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 module or its source code in ANY way
# to hardware which is NOT licensed by Dream Multimedia GmbH.
# It's NOT allowed to execute this module 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
from Components.Label import Label
from Components.PluginComponent import plugins
from Components.Sources.List import List
from Plugins.Plugin import PluginDescriptor
from Screens.Screen import Screen
from Tools.BoundFunction import boundFunction
from Tools.Directories import resolveFilename, SCOPE_CURRENT_SKIN
from Tools.LoadPixmap import LoadPixmap

from enigma import eServiceReference

class MerlinContextMenu:
	CATEGORY=0
	MENUITEM=1
	
	skin = """		
	<screen name="MerlinContextMenu" position="center,220" size="470,320">
		<widget source="list2" render="Listbox" position="10,10" size="450,300" scrollbarMode="showOnDemand">
			<convert type="TemplatedMultiContent">
				{"templates": {
					"default": (45, [
						MultiContentEntryPixmapAlphaBlend(pos=(0,0), size=(25,45), png=6),
						MultiContentEntryText(pos=(30,0), size=(445,25), font=0, text=0),
						MultiContentEntryText(pos=(30,25), size=(445,20), font=1, text=1), 
					]),
					"small": (25, [
						MultiContentEntryText(pos=(0,0), size=(450,25), font=0, text=0),
					]),
				},
					"fonts": [gFont("Regular",18), gFont("Regular",14)],
				}
			</convert>
		</widget>   
	</screen>
	"""
	
	def __init__(self, service = None, contextMenuList=[], windowTitle="Merlin Context Menu"):
		#TODO: handling for service being None
		self.service = service	
		self.contextMenuList = contextMenuList
		self.windowTitle = windowTitle
		
		self["actions"] = ActionMap(["OkCancelActions"],
			{
				"ok": self.selectItem,
				"cancel": self.goBack
			})
			
		self.list = []
		self["list2"] = List(self.list)
		self.inMenu=False
		self.lastCategory = 0
		self.categoryCount = 0
		self.setTitle(self.windowTitle)
		self.getEntries()
		self["list2"].onSelectionChanged.append(self.updateMerlinContextMenuSummary)
		self.onShown.append(self.updateMerlinContextMenuSummary)
		
	def updateMerlinContextMenuSummary(self):
		text = ""
		if len(self.list):
			text = self["list2"].getCurrent()[0]
			
		self.summaries.setText(text)	
		
	#get all relevant entries for display	
	def getEntries(self, parent=None):
		self.categoryCount = 0
		self.list = []
		for entry in self.contextMenuList:
			if (entry[4] == parent and entry[5] != self.CATEGORY) or (parent == None and entry[5] == self.CATEGORY):
				self.addItem(entry[0], entry[1], entry[2], entry[3], entry[4], entry[5])
		
		if parent==None:
			self["list2"].style = "default"
		else:
			self["list2"].style = "small"

		self["list2"].setList(self.list)
		
		#move cursor back to the previous position after leaving a menu
		if parent== None:
			self["list2"].setIndex(self.lastCategory)	
			
	# build the menu for the selected category
	def enterMenu(self, category=None):
		if category is not None:
			self.lastCategory = self["list2"].getIndex()
			self.setTitle(self["list2"].getCurrent()[0])				
			self.getEntries(category)

	def addItem(self, itemName=None, itemDescription="", function=None, validFor=["item"], category=None, type=None):
		#validFor options: "item","folder", "marker"
		#a type must be provided
		if type is None:
			return

		png = None

		#build entry for categories	
		if type == self.CATEGORY:
			#categories have an icon
			icon = resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/folder.png")
			png = LoadPixmap(cached=True, path=icon)		
			
			#if a category is defined then use it as a parameter
			if category is not None:
				func = boundFunction(function, category)
			else:
				func = function
				
		elif type == self.MENUITEM:
			func = function
				
		#only add items to the list when an itemName is provided
		if itemName is not None:
			
			if not self.service is None:
				#it's a folder
				if (self.service.flags & eServiceReference.mustDescent):
					#don't add item to the list when not allowed for folders
					if not "folder" in validFor:
						return
			
				#it's not a folder	
				if not (self.service.flags & eServiceReference.mustDescent):
					#don't add item to the list when not allowed for regular entries
					if not "item" in validFor:
						return
					
				#it's a marker
				if (self.service.flags & eServiceReference.isMarker):
					#don't add item to the list when not allowed for markers
					if not "marker" in validFor:
						return
			
			if type == self.CATEGORY:
				#this is to make sure that categories come before menu entries
				self.list.insert(self.categoryCount,(itemName, itemDescription, func, validFor, category, type, png))
				self.categoryCount += 1
			else:
				self.list.append((itemName, itemDescription, func, validFor, category, type, png))
	
	#add a plugin menu
	def addPluginMenu(self, description="", category="plugins"):
		self.contextMenuList.extend([(_("Plugins"), _("%s" %(description)), self.enterMenu, ["item","folder","marker"], category, self.CATEGORY)])

	#add external plugins. Format is: [(hook, validFor)]. hook: list of plugin hooks. validFor: list
	def addPlugins(self, hooks=[]):
		for i in range(len(hooks)):
			for p in plugins.getPlugins(hooks[i][0]):
				self.contextMenuList.extend([(p.description, "", boundFunction(self.execPluginFromContextMenu, p), hooks[i][1], "plugins", self.MENUITEM)])

	#add function to execute plugins
	def execPluginFromContextMenu(self, plugin):
		plugin(session=self.session, service=self.service)	
	
	# execute function		
	def selectItem(self):
		self.inMenu=True
		self["list2"].getCurrent()[2]()

	# go back to the parent menu / close the context menu
	def goBack(self):
		if self.inMenu==True:
			self.inMenu=False
			self.setTitle(_("%s" %(self.windowTitle)))			
			self.getEntries()
		else:
			self.close(False)
			
class MerlinContextMenuSummary(Screen):
	skin = (
		"""
		<screen name="MerlinContextMenuSummary" position="0,0" size="132,64" id="1">
			<widget name="entry" position="5,5" size="122,54" halign="center" valign="center" font="Regular;25" />
		</screen>""",
		"""
		<screen name="MerlinContextMenuSummary" position="0,0" size="96,64" id="2">
			<widget name="entry" position="5,5" size="86,54" halign="center" valign="center" font="Regular;25" />
		</screen>""",	
		"""
		<screen name="MerlinContextMenuSummary" position="0,0" size="400,240" id="3">
			<widget name="entry" position="5,5" size="390,230" halign="center" valign="center" font="Regular;40" />
		</screen>""",
		"""
		<screen name="MerlinContextMenuSummary" position="0,0" size="240,80" id="100">
			<widget name="entry" position="5,5" size="230,70" halign="center" valign="center" font="Regular;30" />
		</screen>"""
		)
		
	def __init__(self, session, parent):
		Screen.__init__(self, session)
		self["entry"] = Label()
		
	def setText(self, text):
		self["entry"].setText(text)