from __future__ import division
from __future__ import print_function

from __future__ import absolute_import
from enigma import eListbox, iServiceInformation, eServiceReference, eServiceCenter, eMediaDatabase
from skin import componentSizes

from Components.TemplatedMultiContentComponent import TemplatedMultiContentComponent
from Components.config import config
from Components.GUIComponent import GUIComponent
from ServiceReference import ServiceReference
from Tools.FuzzyDate import FuzzyTime
from Tools.Log import Log

# Dr.Best: folder in movielist
from Tools.LoadPixmap import LoadPixmap
from Tools.Directories import SCOPE_CURRENT_SKIN, resolveFilename, fileExists
import os

from skin import parseColor

# Dr.Best: recording flag
import NavigationInstance
from timer import TimerEntry
from stat import ST_MTIME as stat_ST_MTIME
from time import time as time_time
from math import fabs as math_fabs

from struct import calcsize as struct_calcsize, unpack as struct_unpack, pack as struct_pack
#Dr.Best --> meta and eit with Non-TS-Files
from merlin.emerlin import eMerlin, eMerlinMetaData
from Components.Merlin import MerlinImage, natural_keys, merlinFolderSort

MEDIAEXTENSIONS = {
	  "m2ts": ('movie', 'm2ts'),
	  "m4a": ('music', 'm4a'),
	  "mp2": ('music', 'mp2'),
	  "mp3": ('music', 'mp3'),
	  "wav": ('music', 'wav'),
	  "ogg": ('music', 'ogg'),
	  "wma": ('music', 'wma'),
	  "flac": ('music', 'flac'),
	  "jpg": ('picture', 'jpg'),
	  "jpeg": ('picture', 'jpg'),
	  "png": ('picture', 'png'),
	  "bmp": ('picture', 'bmp'),
	  "ts": ('movie', 'ts'),
	  "avi": ('movie', 'avi'),
	  "divx": ('movie', 'avi'),
	  "mpg": ('movie', 'mpg'),
	  "mpeg": ('movie', 'mpg'),
	  "mkv": ('movie', 'mkv'),
	  "mp4": ('movie', 'mp4'),
	  "mov": ('movie', 'mov'),
	  "wmv": ('movie', 'wmv'),
	  "flv": ('movie', 'flv'),
	  "m4v": ('movie', 'm4v'),
	  "dat": ('movie', 'dat')
	}

MEDIACACHELIST = {}

class MovieList(TemplatedMultiContentComponent):
	SORT_ALPHANUMERIC = 1
	SORT_RECORDED = 2
	SORT_DATE_ASC = 3
	SORT_DATE_DESC = 4

	LISTTYPE_ORIGINAL = 1
	LISTTYPE_COMPACT_DESCRIPTION = 2
	LISTTYPE_COMPACT = 3
	LISTTYPE_MINIMAL = 4
	# Dr.Best: merlin listtype
	LISTTYPE_MINIMAL_MERLIN = 5

	LIST_STYLES = {
		LISTTYPE_ORIGINAL : "default",
		LISTTYPE_COMPACT_DESCRIPTION : "compact_description",
		LISTTYPE_COMPACT : "compact",
		LISTTYPE_MINIMAL : "minimal",
		LISTTYPE_MINIMAL_MERLIN : "minimal_merlin"
	}

	HIDE_DESCRIPTION = 1
	SHOW_DESCRIPTION = 2

	COMPONENT_ID = componentSizes.MERLIN_MOVIE_LIST
	
	# Parameter Liste for Merlin Movielist
	#
	# default:
	# 1 = txt
	# 2 = tags
	# 3 = servicename
	# 4 = description
	# 5 = begin_string
	# 6 = len
	# 7 = folder_png (when show folders is activated)
	# 8 = folder_name  (when show folders is activated)
	# 9 = color (text color) (when status color is activated) (used in hex = 0xFF000009)
	# 10 = media_png (--> not in default skin)
	# 11 = perc (percent for progressbar)
	# 12 = backlcolor (selection mode = move or delete mode)  used in hex = 0xFF00000C)
	# 13 = backlcolor_sel (selection mode = move or delete mode) used in hex = 0xFF00000D)
	
	# compact_description:
	# 1 = txt
	# 2 = description
	# 3 = begin_string
	# 4 = servicename
	# 5 = len
	# 6 = folder_png (when show folders is activated)
	# 7 = folder_name  (when show folders is activated)
	# 8 = color (text color) (when status color is activated) (used in hex = 0xFF000008)
	# 9 = media_png (--> not in default skin)
	# 10 = perc (percent for progressbar)
	# 11 = backlcolor (selection mode = move or delete mode)  used in hex = 0xFF00000B)
	# 12 = backlcolor_sel (selection mode = move or delete mode) used in hex = 0xFF00000C)
	
	# compact:
	# 1 = txt
	# 2 = tags
	# 3 = servicename
	# 4 = begin_string
	# 5 = len
	# 6 = folder_png (when show folders is activated)
	# 7 = folder_name  (when show folders is activated)
	# 8 = color (text color) (when status color is activated) (used in hex = 0xFF000008)
	# 9 = media_png (--> not in default skin)
	# 10 = perc (percent for progressbar)
	# 11 = backlcolor (selection mode = move or delete mode)  used in hex = 0xFF00000B)
	# 12 = backlcolor_sel (selection mode = move or delete mode) used in hex = 0xFF00000C)
	
	# minimal:
	# 1 = txt
	# 2 = begin_string || len (when SHOW_DESCRIPTION on|off)
	# 3 = folder_png (when show folders is activated)
	# 4 = folder_name  (when show folders is activated)
	# 5 = color (text color) (when status color is activated) (used in hex = 0xFF000005)
	# 6 = media_png (--> not in default skin)
	# 7 = perc (percent for progressbar)
	# 8 = backlcolor (selection mode = move or delete mode)  used in hex = 0xFF000008)
	# 9 = backlcolor_sel (selection mode = move or delete mode) used in hex = 0xFF000009)
	
	# minimal_merlin:
	# 1 = merlin txt --> txt + "-" + description + "(" + min + "min)"
	# 2 = begin_string
	# 3 = folder_png (when show folders is activated)
	# 4 = folder_name  (when show folders is activated)
	# 5 = color (text color) (when status color is activated) (used in hex = 0xFF000005)
	# 6 = media_png (--> not in default skin)
	# 7 = perc (percent for progressbar)
	# 8 = backlcolor (selection mode = move or delete mode)  used in hex = 0xFF000008)
	# 9 = backlcolor_sel (selection mode = move or delete mode) used in hex = 0xFF000009)
	# 10 = servicename

	default_template = """{"templates":
		{
			"default" : (75, [
				MultiContentEntryText(pos=(0, 0), size=(full_width, 75), font=0, flags=RT_HALIGN_LEFT, text="", color=None, backcolor=0xFF00000C, backcolor_sel=0xFF00000D),
				MultiContentEntryText(pos=(0, 0), size=(width-182, 30), font=0, flags=RT_HALIGN_LEFT, text=1, color=0xFF000009),
				MultiContentEntryText(pos=(width-180, 0), size=(180, 30), font=3, flags=RT_HALIGN_RIGHT, text=2, color=0xFF000009),
				MultiContentEntryText(pos=(200, 50), size=(200, 20), font=2, flags=RT_HALIGN_LEFT, text=3, color=0xFF000009),
				MultiContentEntryText(pos=(0, 30), size=(width, 20), font=2, flags=RT_HALIGN_LEFT, text=4, color=0xFF000009),
				MultiContentEntryText(pos=(0, 50), size=(200, 20), font=2, flags=RT_HALIGN_LEFT, text=5, color=0xFF000009),
				MultiContentEntryText(pos=(width-200, 40), size=(198, 20), font=2, flags=RT_HALIGN_RIGHT, text=6, color=0xFF000009),
				MultiContentEntryPixmapAlphaBlend(pos = (0, 27), size = (20, 20), png =7),
				MultiContentEntryText(pos=(40, 22), size=(width-40, 30), font=0, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER, text=8),
				MultiContentEntryProgress(pos=(width-50,60), size=(50,6), percent=-11, borderWidth = 1, foreColor = 0xFF000009),
			]),
			"compact_description" : (37, [
				MultiContentEntryText(pos=(0, 0), size=(full_width, 37), font=0, flags=RT_HALIGN_LEFT, text="", color=None, backcolor=0xFF00000B, backcolor_sel=0xFF00000C),
				MultiContentEntryText(pos=(60, 0), size=(width-60-120, 20), font=1, flags=RT_HALIGN_LEFT, text=1, color=0xFF000008),
				MultiContentEntryText(pos=(60, 20), size=(width-60-212, 17), font=4, flags=RT_HALIGN_LEFT, text=2, color=0xFF000008),
				MultiContentEntryText(pos=(width-120, 6), size=(120, 20), font=4, flags=RT_HALIGN_RIGHT, text=3, color=0xFF000008),
				MultiContentEntryText(pos=(width-212, 20), size=(154, 17), font=4, flags=RT_HALIGN_RIGHT, text=4, color=0xFF000008),
				MultiContentEntryText(pos=(width-58, 20), size=(58, 20), font=4, flags=RT_HALIGN_RIGHT, text=5, color=0xFF000008),
				MultiContentEntryPixmapAlphaBlend(pos = (0, 9), size = (20, 20), png = 6),
				MultiContentEntryText(pos=(40, 3), size=(width-40, 30), font=0, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER, text=7),
				MultiContentEntryProgress(pos=(0,15), size=(50,6), percent=-10, borderWidth = 1, foreColor = 0xFF000008),
			]),
			"compact" : (37, [
				MultiContentEntryText(pos=(0, 0), size=(full_width, 37), font=0, flags=RT_HALIGN_LEFT, text="", color=None, backcolor=0xFF00000B, backcolor_sel=0xFF00000C),
				MultiContentEntryText(pos=(0, 0), size=(width-77, 20), font=1, flags=RT_HALIGN_LEFT, text=1, color=0xFF000008),
				MultiContentEntryText(pos=(width-200, 20), size=(200, 17), font=4, flags=RT_HALIGN_RIGHT, text=2, color=0xFF000008),
				MultiContentEntryText(pos=(200, 20), size=(200, 17), font=4, flags=RT_HALIGN_LEFT, text=3, color=0xFF000008),
				MultiContentEntryText(pos=(0, 20), size=(200, 17), font=4, flags=RT_HALIGN_LEFT, text=4, color=0xFF000008),
				MultiContentEntryText(pos=(width-75, 0), size=(75, 20), font=1, flags=RT_HALIGN_RIGHT, text=5, color=0xFF000008),
				MultiContentEntryPixmapAlphaBlend(pos = (0, 9), size = (20, 20), png = 6),
				MultiContentEntryText(pos=(40, 3), size=(width-40, 30), font=0, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER, text=7),
				MultiContentEntryProgress(pos=(width-50,25), size=(50,6), percent=-10, borderWidth = 1, foreColor = 0xFF000008),
			]),
			"minimal" : (25, [
				MultiContentEntryText(pos=(0, 0), size=(full_width, 25), font=0, flags=RT_HALIGN_LEFT, text="", color=None, backcolor=0xFF000008, backcolor_sel=0xFF000009),
				MultiContentEntryText(pos=(60, 0), size=(width-60-146, 20), font=1, flags=RT_HALIGN_LEFT, text=1, color=0xFF000005),
				MultiContentEntryText(pos=(width-145, 4), size=(145, 20), font=3, flags=RT_HALIGN_RIGHT, text=2, color=0xFF000005),
				MultiContentEntryPixmapAlphaBlend(pos = (0, 2), size = (20, 20), png = 3),
				MultiContentEntryText(pos=(40, 0), size=(width-40, 25), font=0, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER, text=4),
				MultiContentEntryProgress(pos=(0,10), size=(50,6), percent=-7, borderWidth = 1, foreColor = 0xFF000005), 
			]),
			"minimal_merlin" : (25, [
				MultiContentEntryText(pos=(0, 0), size=(full_width, 25), font=0, flags=RT_HALIGN_LEFT, text="", color=None, backcolor=0xFF000008, backcolor_sel=0xFF000009),
				MultiContentEntryText(pos=(60, 0), size=(120, 25), font=3, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER, text=2, color=0xFF000005),
				MultiContentEntryText(pos=(60+120, 0), size=(width-60-120-160, 25), font=2, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER, text=1, color=0xFF000005),
				MultiContentEntryText(pos=(width-160, 0), size=(160, 25), font=3, flags=RT_HALIGN_RIGHT|RT_VALIGN_CENTER, text=10, color=0xFF000005),
				MultiContentEntryPixmapAlphaBlend(pos = (0, 2), size = (20, 20), png = 3),
				MultiContentEntryText(pos=(40, 0), size=(width-40, 25), font=2, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER, text=4),
				MultiContentEntryProgress(pos=(0,10), size=(50,6), percent=-7, borderWidth = 1, foreColor = 0xFF000005), 
			])
		},
		"fonts" : [gFont("Regular", 22), gFont("Regular", 20), gFont("Regular", 18), gFont("Regular", 16), gFont("Regular", 14)]
	}"""
	
	SELECTION_MODE = 0

	def __init__(self, root, list_type=None, sort_type=None, descr_state=None, show_folders = False, show_statuscolor = False): # Dr.Best: folder in movielist, progressbar, staus icon and color status
		TemplatedMultiContentComponent.__init__(self)
		self.list = []

		self.descr_state = descr_state or self.HIDE_DESCRIPTION
		self.sort_type = sort_type or self.SORT_RECORDED
		
		# Dr.Best: folder in movielist
		self.show_folders = show_folders

		# Dr.Best: color status in movielist
		self.show_statuscolor = show_statuscolor

		self.use_mediacaching = config.merlin2.use_mediacache.value
		
		self.merlin_metaData = {}
		self.merlin_event = {}

		self.setListType(list_type or self.LISTTYPE_ORIGINAL)
		self.tags = set()
		self.default_sort_type = int(config.merlin2.default_moviesort.value)
		if root is not None:
			self.reload(root)

		self.l.setBuildFunc(self.buildMovieListEntry)
		self.onSelectionChanged = [ ]
		
		# Dr.Best: some color stuff
		try: self.watching_color =  parseColor("movieWatching").argb()    
		except: self.watching_color = 0x879CC1
		try: self.finished_color =  parseColor("movieFinished").argb()    
		except: self.finished_color = 0x56C856
		try: self.recording_color =  parseColor("movieRecording").argb()
		except: self.recording_color = 0xff4A3C
		# Dr.Best delete mode color
		try: self.deletemode_color =  parseColor("deleteMode").argb()
		except: self.deletemode_color = 0xd01709
		try: self.deletemodeselected_color =  parseColor("deleteModeSelected").argb()
		except: self.deletemodeselected_color = 0xa60f04
		# Dr.Best moveTo mode color
		try: self.movetomode_color =  parseColor("moveToMode").argb()
		except: self.movetomode_color = 0x5385c3
		try: self.movetomodeselected_color =  parseColor("moveToModeSelected").argb()
		except: self.movetomodeselected_color = 0x365b8a

		self.selectionList = {}

	def applySkin(self, desktop, parent):
		GUIComponent.applySkin(self, desktop, parent)
		self.applyTemplate(additional_locals={"width" : self.l.getItemSize().width()-30, "full_width" : self.l.getItemSize().width()})

	def redrawList(self):
		self.l.invalidate()

	def getLastPosition(self, filename):
		index1 = last = None
		if os.path.exists(filename):
			cuts_format = ">QI" # big endian, unsigned long long, unsigned int
			cuts_format_size = struct_calcsize(cuts_format)
			try:
				cuts_file = open(filename, 'rb')
			except IOError:
				cuts_file = None
			if cuts_file:
				last = 0
				while True:
					try:
						cuts_data = cuts_file.read(cuts_format_size)
					except IOError:
						break	
					if len(cuts_data) != cuts_format_size: # the string must contain exactly the amount of data required by the format
						break
					cuts_unpacked_data = struct_unpack(cuts_format, cuts_data)
					if cuts_unpacked_data[1] == 1:
						index1 = cuts_unpacked_data[0]
					elif cuts_unpacked_data[1] == 3: # CUT_TYPE_LAST = 3 --> InfoBarCueSheetSupport
						last = cuts_unpacked_data[0]
				cuts_file.close()
		return (index1, last)

	def createCuts(self, filename):
		pos = 0
		length = eMerlin.getInstance().getDuration(filename) * 90000
		cutList = []
		cuts_format = ">QI" # big endian, unsigned long long, unsigned int
		cuts_format_size = struct_calcsize(cuts_format)
		try:
			packed_data = struct_pack(cuts_format,long(length),1)
		except:
			return length
		cutList.append(packed_data)
		packed_data = struct_pack(cuts_format,long(pos),3)
		cutList.append(packed_data)
		try:
			f = open("%s.cuts" % (os.path.realpath(filename)), 'wb')
			f.write(''.join(cutList))
			f.close()
		except: pass
		return length
	
	def connectSelChanged(self, fnc):
		if not fnc in self.onSelectionChanged:
			self.onSelectionChanged.append(fnc)

	def disconnectSelChanged(self, fnc):
		if fnc in self.onSelectionChanged:
			self.onSelectionChanged.remove(fnc)

	def selectionChanged(self):
		for x in self.onSelectionChanged:
			x()

	def setListType(self, type):
		self.list_type = type
		self.setTemplate(self.LIST_STYLES[type])

	def setDescriptionState(self, val):
		self.descr_state = val

	def setSortType(self, type):
		if config.merlin2.use_custom_folder_moviesort.value:
			merlinFolderSort.dict[self.root.toString()] = type
		self.sort_type = type
		
	# Dr.Best: folder in movielist
	def showFolders(self, val):
		self.show_folders = val

	def showStatusColor(self, val):
		self.show_statuscolor = val		

	# | name of movie              |
	#
	def buildMovieListEntry(self, serviceref, info, begin, len, last_pos = None):
		global MEDIACACHELIST
		folder_png = None
		folder_name = None
		media_png = None
		if self.show_folders:
			# Dr.Best: folder in movielist
			if serviceref.flags & eServiceReference.mustDescent:
				folder_png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "extensions/directory.png"))
				folder_name = serviceref.getName()
		else:
			if serviceref.flags & eServiceReference.mustDescent:
				return None

		extension = serviceref.getPath().split('.')[-1].lower()
		
		if folder_name is None:
			#if MEDIAEXTENSIONS.has_key(extension):
			if extension in MEDIAEXTENSIONS:
				media=MEDIAEXTENSIONS[extension]
				media_png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "extensions/" + media[1] + ".png"))
				if media_png is None:
					media_png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "extensions/" + media[0] + ".png"))
		  
		
			if len >0 and last_pos is not None:
				mps = (len,last_pos)
			else:
				
				if extension in MerlinImage.getAllExtensions():
					cutsfilename = os.path.realpath("%s.cuts" % serviceref.getPath())
					mps = self.getLastPosition(cutsfilename)
				else:
					mps = (None,None)

			if len <= 0: #recalc len when not already done
				cur_idx = self.l.getCurrentSelectionIndex()
				x = self.list[cur_idx]
				if config.usage.load_length_of_movies_in_moviellist.value:
					len = x[1].getLength(x[0]) #recalc the movie length...
					if len <= 0:
						len = mps[0]
						if len is None:
							if config.merlin2.calculate_non_ts_video_duration_in_movielist.value and extension in MerlinImage.getExtensions():
								len = self.createCuts(serviceref.getPath()) 
								mps = (len, 0)
								len = len // 90000
							else:
								len = 0
						else:
							len = len // 90000
				else:
					len = 0 #dont recalc movielist to speedup loading the list
				self.list[cur_idx] = (x[0], x[1], x[2], len, mps[1]) #update entry in list... so next time we don't need to recalc
				if self.use_mediacaching:
					MEDIACACHELIST[serviceref.toString()] = (x[0], x[1], x[2], len, mps[1])
			
			
			# Dr.Best: movie status flag
			length = len
			
			if len > 0:
				hours = len//3600
				if config.merlin2.lengthinmovielist.value == "hms" and hours >= 1:
					len = "%d:%02d:%02d" % (len//3600, (len//60)%60, len%60)
				else:
					len = "%d:%02d" % (len // 60, len % 60)
			else:
				len = ""
				
			
				
				
			color = backcolor = backcolor_sel = None 
			# Dr.Best: recording flag
			recording = False
			if NavigationInstance.instance.getRecordings():
				for timer in NavigationInstance.instance.RecordTimer.timer_list:
					if timer.state == TimerEntry.StateRunning:
						try:
							filename = timer.Filename
						except:
							filename = ""
						if filename and os.path.realpath(filename) == os.path.realpath(serviceref.getPath()):
							recording = True
							break
			if not recording:
				filename = os.path.realpath(serviceref.getPath())
				if os.path.exists("%s.sc" % filename) and not os.path.exists("%s.ap" % filename):
					# double check, sometimes ap file was not created (e.g. after enigma2 crash)
					filestats = os.stat(filename)
					currentTime = time_time()
					mtime = filestats[stat_ST_MTIME]
					if math_fabs(mtime - int(currentTime)) <= 10:
						recording = True
			if recording and self.show_statuscolor:
				color = self.recording_color
			
			
			if recording:
				media_png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "extensions/movie_recording.png"))
				
				
			# Dr.Best: movie status flag
			last = mps[1]

			perc = 0
			if last is not None and length > 0:
				perc = int((float(last)  / 90000 / float(length)) * 100)
				if perc > 100: perc = 100
				if self.show_statuscolor and not recording:
					if (perc > 1) and (perc <= config.merlin2.moviepercentseen.value):
						color = self.watching_color
					elif (perc > config.merlin2.moviepercentseen.value):
						color = self.finished_color
				if not recording:
					if (perc > 1) and (perc <= config.merlin2.moviepercentseen.value):
						media_png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "extensions/movie_watching.png"))
					elif (perc > config.merlin2.moviepercentseen.value):
						media_png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "extensions/movie_finished.png"))
						
						
			# Dr.Best selection mode
			if self.SELECTION_MODE:
				value = self.selectionList.get(serviceref.toString(),False)
				if value:
					if self.SELECTION_MODE == 1: # Dr.Best delete mode
						backcolor = self.deletemode_color
						backcolor_sel = self.deletemodeselected_color
					else: # Dr.Best moveTo mode color
						backcolor = self.movetomode_color
						backcolor_sel = self.movetomodeselected_color
					color = None

		res = [ None ]
		
		
		if folder_name is None:
			folder_name = ""
			#Dr.Best --> meta with Non-TS-Files
			check = 1
			if extension in MerlinImage.getExtensions():
				if serviceref.toString() not in self.merlin_metaData:
					metaData = eMerlinMetaData(serviceref.getPath())
					self.merlin_metaData[serviceref.toString()] = metaData
				else:
					metaData = self.merlin_metaData[serviceref.toString()]
				if metaData:
					check = 0
					description = metaData.getDescription()
					txt = metaData.getName()
					tags = metaData.getTags()
					service = ServiceReference(metaData.getServiceReference())
				else:
					#if not self.merlin_event.has_key(serviceref.toString()):
					if serviceref.toString() not in self.merlin_event:
						event = eMerlin.getInstance().getEvent(serviceref)
						self.merlin_event[serviceref.toString()] = event
					else:
						event = self.merlin_event[serviceref.toString()]
					if event:
						check = 0
						description = event.getShortDescription()
						txt = event.getEventName()
						tags = ""
						service = None
			if check:
				txt = info.getName(serviceref)
				service = ServiceReference(info.getInfoString(serviceref, iServiceInformation.sServiceref))
				description = info.getInfoString(serviceref, iServiceInformation.sDescription)
				tags = info.getInfoString(serviceref, iServiceInformation.sTags)
			servicename = ""
			if service is not None:
				servicename = service.getServiceName()

			begin_string = ""
			# Dr.Best: recording flag
			if recording:
				begin_string = _("recording...")
				if self.use_mediacaching and serviceref.toString() in MEDIACACHELIST:
					del MEDIACACHELIST[serviceref.toString()]
			else:
				if begin > 0:
					t = FuzzyTime(begin)
					begin_string = t[0] + ", " + t[1]
		else:
			txt = description = tags = servicename = begin_string = len = ""
			color = backcolor = backcolor_sel = None 
			perc = -1
			
		
		if self.list_type == MovieList.LISTTYPE_ORIGINAL:
			res.extend((txt, tags, servicename, description, begin_string, len, folder_png, folder_name, color, media_png, perc, backcolor, backcolor_sel))
		elif self.list_type == MovieList.LISTTYPE_COMPACT_DESCRIPTION:
			res.extend((txt, description, begin_string, servicename, len, folder_png, folder_name, color, media_png, perc, backcolor, backcolor_sel))
		elif self.list_type == MovieList.LISTTYPE_COMPACT:
			res.extend((txt, tags, servicename, begin_string, len, folder_png, folder_name, color, media_png, perc, backcolor, backcolor_sel))
		elif self.list_type == MovieList.LISTTYPE_MINIMAL_MERLIN:
			displaytext = txt
			if description:
				displaytext = displaytext + " - " + description
			if len:
				displaytext = displaytext + " (" + len + " min)"
			res.extend((displaytext, begin_string, folder_png, folder_name, color, media_png, perc, backcolor, backcolor_sel, servicename))
		else:
			assert(self.list_type == MovieList.LISTTYPE_MINIMAL)
			if self.descr_state == MovieList.SHOW_DESCRIPTION:
				res.extend((txt, begin_string, folder_png, folder_name, color, media_png, perc, backcolor, backcolor_sel))
			else:
				res.extend((txt, len, folder_png, folder_name, color, media_png, perc, backcolor, backcolor_sel))
		
		return res

	def moveToIndex(self, index):
		self.instance.moveSelectionTo(index)

	def getCurrentIndex(self):
		return self.instance.getCurrentIndex()

	def getCurrentEvent(self):
		l = self.l.getCurrentSelection()
		#Dr.Best --> eit with Non-TS-Files
		event = None
		if l and l[0]:
			event =  l[1] and l[1].getEvent(l[0])
			if not event:
				event = eMerlin.getInstance().getEvent(l[0])
		return event

	def getCurrent(self):
		l = self.l.getCurrentSelection()
		return l and l[0]

	GUI_WIDGET = eListbox

	def postWidgetCreate(self, instance):
		instance.setContent(self.l)
		self.selectionChanged_conn = instance.selectionChanged.connect(self.selectionChanged)

	def preWidgetRemove(self, instance):
		instance.setContent(None)
		self.selectionChanged_conn = None

	def reload(self, root = None, filter_tags = None):
		if root is not None:
			self.load(root, filter_tags)
		else:
			self.load(self.root, filter_tags)
		self.l.setList(self.list)

	def removeService(self, service):
		for l in self.list[:]:
			if l[0] == service:
				self.list.remove(l)
		self.l.setList(self.list)

	def __len__(self):
		return len(self.list)

	def load(self, root, filter_tags):
		global MEDIACACHELIST
		# this lists our root service, then building a 
		# nice list
		
		self.merlin_metaData = {}
		self.merlin_event = {}
		
		self.list = [ ]
		self.serviceHandler = eServiceCenter.getInstance()
		
		self.root = root
		list = self.serviceHandler.list(root)
		if list is None:
			print("listing of movies failed")
			list = [ ]	
			return
		tags = set()
		
		# Dr.Best: folder in movielist
		dirs = []

		while True:
			serviceref = list.getNext()
			if not serviceref.valid():
				break

			if self.show_folders:
				# Dr.Best: folder in movielist
				if serviceref.flags & eServiceReference.mustDescent:
					tempDir = serviceref.getPath()
					parts = tempDir.split("/")
					dirName = parts[-2]
					if not config.merlin2.show_hidden_folders_in_movielist.value and (dirName.startswith('.') or dirName.startswith('@')): # weazle: hide .dotdirs
						continue
					else:
						serviceref.setName(dirName)
						dirs.append((serviceref, None, -1, -1))
						continue
			else:
				if serviceref.flags & eServiceReference.mustDescent:
					continue

			if not self.use_mediacaching or (self.use_mediacaching and serviceref.toString() not in MEDIACACHELIST):
				info = self.serviceHandler.info(serviceref)
				if info is None:
					continue

				begin = info.getInfo(serviceref, iServiceInformation.sTimeCreate)
			else:

				info = MEDIACACHELIST[serviceref.toString()][1]
				begin = MEDIACACHELIST[serviceref.toString()][2]
		
			# convert space-seperated list of tags into a set
			this_tags = info.getInfoString(serviceref, iServiceInformation.sTags).split(' ')
			if this_tags == ['']:
				this_tags = []
			this_tags = set(this_tags)
			tags |= this_tags
		
			# filter_tags is either None (which means no filter at all), or 
			# a set. In this case, all elements of filter_tags must be present,
			# otherwise the entry will be dropped.			
			if filter_tags is not None and not this_tags.issuperset(filter_tags):
				continue
		
			if not self.use_mediacaching or (self.use_mediacaching and serviceref.toString() not in MEDIACACHELIST):
				self.list.append((serviceref, info, begin, -1))
			else:
				self.list.append(MEDIACACHELIST[serviceref.toString()])
		
		if config.merlin2.use_custom_folder_moviesort.value:	
			sort_type = merlinFolderSort.dict.get(self.root.toString(), self.default_sort_type)
		else:
			sort_type = self.sort_type

		if sort_type == MovieList.SORT_ALPHANUMERIC:
			self.list.sort(key=self.buildAlphaNumericSortKey)
		else:
			if sort_type ==  MovieList.SORT_DATE_ASC:
				self.list.sort(self.sortbyDateAsc)
			else:
				self.list.sort(self.sortbyDateDesc)
#			# sort: key is 'begin'
#			self.list.sort(key=lambda x: -x[2])

		# Dr.Best: folder in movielist
		if self.show_folders:
			dirs.sort(self.sortFolders)
			for servicedirs in dirs:
				self.list.insert(0,servicedirs)
			tmp = self.root.getPath()
			if len(tmp) > 1:
				tt = eServiceReference(eServiceReference.idFile, eServiceReference.flagDirectory, ".." )
				tt.setName("..")
				tmpRoot = os.path.dirname(tmp[:-1])
				if len(tmpRoot) > 1:
					tmpRoot = tmpRoot + "/"
				tt.setPath(tmpRoot)
				self.list.insert(0,(tt,None,-1,-1))
	
		# finally, store a list of all tags which were found. these can be presented
		# to the user to filter the list
		self.tags = tags

	# Dr.Best: sort by Date asc and desc
	def sortbyDateAsc(self, a, b):
		return cmp(a[2],b[2])

	def sortbyDateDesc(self, a, b):
		return cmp(b[2],a[2])

	# Dr.Best: sort folders
	def sortFolders(self, a, b):
		return cmp(b[0].getName().lower(), a[0].getName().lower())

	def buildAlphaNumericSortKey(self, x):
		ref = x[0]
		info = self.serviceHandler.info(ref)
		name = info and info.getName(ref)
		return (natural_keys(name and name.lower() or ""), -x[2])
		#return (name and name.lower() or "", -x[2])

	def moveTo(self, serviceref):
		count = 0
		for x in self.list:
			if x[0] == serviceref:
				self.instance.moveSelectionTo(count)
				return True
			count += 1
		return False
	
	def moveDown(self):
		self.instance.moveSelection(self.instance.moveDown)

	# Dr.Best set Movie Status
	def setMovieStatus(self, serviceref, status):
		global MEDIACACHELIST
		info = self.serviceHandler.info(serviceref)
		if info is None:
			return
		cur_idx = self.l.getCurrentSelectionIndex()
		filename = os.path.realpath(serviceref.getPath())
		if os.path.exists("%s.cuts" % (filename)):
			cutList = []
			cuts_format = ">QI" # big endian, unsigned long long, unsigned int
			cuts_format_size = struct_calcsize(cuts_format)
			cuts_file = open("%s.cuts" % (filename), 'rb')
			if cuts_file:
				while True:
					cuts_data = cuts_file.read(cuts_format_size)
					if len(cuts_data) != cuts_format_size: # the string must contain exactly the amount of data required by the format
						break
					cuts_unpacked_data = struct_unpack(cuts_format, cuts_data)
					if cuts_unpacked_data[1] != 3: # CUT_TYPE_LAST = 3 --> InfoBarCueSheetSupport
						 cutList.append(cuts_data)
				cuts_file.close()
				x = self.list[cur_idx]
				last_pos = 0
				if status:
					length = x[1].getLength(x[0]) * 90000
					if length < 0:
						cutsfilename = "%s.cuts" % filename
						mps = self.getLastPosition(cutsfilename)
						length = mps[0]
					if length and length >=0:
						last_pos = length
						packed_data = struct_pack(cuts_format,long(length ),3)
						cutList.append(packed_data)
				self.list[cur_idx] = (x[0],x[1],x[2],x[3],last_pos)
				if self.use_mediacaching:
					MEDIACACHELIST[serviceref.toString()] = (x[0],x[1],x[2],x[3],last_pos) 
				try:
					f = open("%s.cuts" % (filename), 'wb')
					f.write(''.join(cutList))
					f.close()
					self.l.invalidateEntry(self.instance.getCurrentIndex())
				except: pass

	# Dr.Best selection mode
	def switchSelectionMode(self, status):
		self.SELECTION_MODE = status	
		self.selectionList = {}

	# Dr.Best selection mode
	def getSelectionMode(self):
		return self.SELECTION_MODE

	# Dr.Best selection mode
	def getSelectionList(self):
		return self.selectionList

	# Dr.Best selection mode
	def setDeleteValue(self, servicereference):
		value = self.selectionList.get(servicereference.toString(),False)
		self.selectionList[servicereference.toString()] = not value
		if value:
			del self.selectionList[servicereference.toString()]
		index = self.getCurrentIndex()
		self.l.invalidateEntry(index)

class MovieListEntry(object):
	def __init__(self, entryType, serviceref, info):
		self.type = entryType
		self.info = info
		self.serviceref = serviceref
		self.service = ServiceReference(info.getInfoString(serviceref, iServiceInformation.sServiceref))
		self.name = info.getName(serviceref)
		self.title = self.name
		self.servicename = ""
		if self.service:
			self.servicename = self.service.getServiceName()

class MovieListDir(MovieListEntry):
	def __init__(self, serviceref, info):
		MovieListEntry.__init__(self, MovieListDB.ENTRY_TYPE_DIR, serviceref, info)

class MovieListVDir(MovieListEntry):
	def __init__(self, serviceref, info):
		MovieListEntry.__init__(self, MovieListDB.ENTRY_TYPE_VDIR, serviceref, info)

class MovieInfo(MovieListEntry):
	def __init__(self, serviceref, info, duration):#
		MovieListEntry.__init__(self, MovieListDB.ENTRY_TYPE_FILE, serviceref, info)
		self.begin = info.getInfo(serviceref, iServiceInformation.sTimeCreate)
		self._duration = duration
		self.description = info.getInfoString(serviceref, iServiceInformation.sDescription)
		self.episode = self.description
		self.tags = set(self.info.getInfoString(self.serviceref, iServiceInformation.sTags).split(' '))

	@property
	def duration(self):
		if not config.usage.load_length_of_movies_in_moviellist.value:
			return 0
		if self._duration <= 0:
			self._duration = self.info.getLength(self.serviceref) #recalc the movie length...
		return self._duration

	def __getitem__(self, index):
		if index == 0:
			return self.service
		elif index == 1:
			return self.info
		else:
			return None

	def __str__(self, *args, **kwargs):
		return self.name

	def __repr__(self, *args, **kwargs):
		return "~MovieInfo: %s - %s" %(self.title, self.episode)

class MovieListDB(TemplatedMultiContentComponent):
	SORT_ALPHANUMERIC = 1
	SORT_RECORDED = 2

	LISTTYPE_ORIGINAL = 1
	LISTTYPE_COMPACT_DESCRIPTION = 2
	LISTTYPE_COMPACT = 3
	LISTTYPE_MINIMAL = 4

	LIST_STYLES = {
		LISTTYPE_ORIGINAL : "default",
		LISTTYPE_COMPACT_DESCRIPTION : "compact_description",
		LISTTYPE_COMPACT : "compact",
		LISTTYPE_MINIMAL : "minimal"
	}

	HIDE_DESCRIPTION = 1
	SHOW_DESCRIPTION = 2

	ENTRY_TYPE_FILE = 0
	ENTRY_TYPE_DIR = 1
	ENTRY_TYPE_VDIR = 2
	ENTRY_TYPE_UP = 4
	ENTRY_TYPE_VUP = 6
	ENTRY_TYPE_VIRTUAL = ENTRY_TYPE_VDIR | ENTRY_TYPE_VUP

	COMPONENT_ID = componentSizes.MOVIE_LIST

	default_template = """{"templates":
		{
			"default" : (75, [
				MultiContentEntryText(pos=(0, 0), size=(width-182, 30), font=0, flags=RT_HALIGN_LEFT, text=1),
				MultiContentEntryText(pos=(width-180, 0), size=(180, 30), font=3, flags=RT_HALIGN_RIGHT, text=2),
				MultiContentEntryText(pos=(200, 50), size=(200, 20), font=2, flags=RT_HALIGN_LEFT, text=3),
				MultiContentEntryText(pos=(0, 30), size=(width, 20), font=2, flags=RT_HALIGN_LEFT, text=4),
				MultiContentEntryText(pos=(0, 50), size=(200, 20), font=2, flags=RT_HALIGN_LEFT, text=5),
				MultiContentEntryText(pos=(width-200, 50), size=(198, 20), font=2, flags=RT_HALIGN_RIGHT, text=6),
			]),
			"compact_description" : (37, [
				MultiContentEntryText(pos=(0, 0), size=(width-120, 20), font=1, flags=RT_HALIGN_LEFT, text=1),
				MultiContentEntryText(pos=(0, 20), size=(width-212, 17), font=4, flags=RT_HALIGN_LEFT, text=2),
				MultiContentEntryText(pos=(width-120, 6), size=(120, 20), font=4, flags=RT_HALIGN_RIGHT, text=3),
				MultiContentEntryText(pos=(width-212, 20), size=(154, 17), font=4, flags=RT_HALIGN_RIGHT, text=4),
				MultiContentEntryText(pos=(width-58, 20), size=(58, 20), font=4, flags=RT_HALIGN_RIGHT, text=5),
			]),
			"compact" : (37, [
				MultiContentEntryText(pos=(0, 0), size=(width-77, 20), font=1, flags=RT_HALIGN_LEFT, text=1),
				MultiContentEntryText(pos=(width-200, 20), size=(200, 17), font=4, flags=RT_HALIGN_RIGHT, text=2),
				MultiContentEntryText(pos=(200, 20), size=(200, 17), font=4, flags=RT_HALIGN_LEFT, text=3),
				MultiContentEntryText(pos=(0, 20), size=(200, 17), font=4, flags=RT_HALIGN_LEFT, text=4),
				MultiContentEntryText(pos=(width-75, 0), size=(75, 20), font=1, flags=RT_HALIGN_RIGHT, text=5),
			]),
			"minimal" : (25, [
				MultiContentEntryText(pos=(0, 0), size=(width-146, 20), font=1, flags=RT_HALIGN_LEFT, text=1),
				MultiContentEntryText(pos=(width-145, 4), size=(145, 20), font=3, flags=RT_HALIGN_RIGHT, text=2),
			])
		},
		"fonts" : [gFont("Regular", 22), gFont("Regular", 20), gFont("Regular", 18), gFont("Regular", 16), gFont("Regular", 14)]
	}"""

	def __init__(self, root, list_type=None, sort_type=None, descr_state=None, show_folders = False, show_statuscolor = False): # Dr.Best: folder in movielist, progressbar, staus icon and color status
		TemplatedMultiContentComponent.__init__(self)
		self.virtualList = []
		self.list = []
		self._vlist = []

		self.descr_state = descr_state or self.HIDE_DESCRIPTION
		self.sort_type = sort_type or self.SORT_RECORDED
		self.setListType(list_type or self.LISTTYPE_ORIGINAL)
		self.tags = set()
		self.root = root
		self.filter_tags = []

		self.l.setBuildFunc(self.buildMovieListEntry)
		self.onSelectionChanged = [ ]
		self.__mediaDbScanFinished_conn = eMediaDatabase.getInstance().scanFinished.connect(self._onMediaDbScanFinished)

	def applySkin(self, desktop, parent):
		GUIComponent.applySkin(self, desktop, parent)
		self.applyTemplate(additional_locals={"width" : self.l.getItemSize().width()-30})

	def redrawList(self):
		pass

	def connectSelChanged(self, fnc):
		if not fnc in self.onSelectionChanged:
			self.onSelectionChanged.append(fnc)

	def disconnectSelChanged(self, fnc):
		if fnc in self.onSelectionChanged:
			self.onSelectionChanged.remove(fnc)

	def selectionChanged(self):
		for x in self.onSelectionChanged:
			x()

	def setListType(self, listType):
		self.list_type = listType
		self.setTemplate(self.LIST_STYLES[listType])

	def setDescriptionState(self, val):
		self.descr_state = val

	def setSortType(self, sortType):
		self.sort_type = sortType

	# | name of movie              |
	#
	def buildMovieListEntry(self, movieInfo):
		res = [ None ]
		if movieInfo.serviceref.flags & eServiceReference.mustDescent:
			res.extend(("[%s]" %(movieInfo.name,) , "", "", "", "", ""))
			return res

		duration = movieInfo.duration
		if duration > 0:
			duration = "%d:%02d" % (duration // 60, duration % 60)
		else:
			duration = ""


		begin_string = ""
		if movieInfo.begin > 0:
			t = FuzzyTime(movieInfo.begin)
			begin_string = t[0] + ", " + t[1]
		tags = " ".join(movieInfo.tags)
		if self.list_type == MovieList.LISTTYPE_ORIGINAL:
			res.extend((movieInfo.name, tags, movieInfo.servicename, movieInfo.description, begin_string, duration))
		elif self.list_type == MovieList.LISTTYPE_COMPACT_DESCRIPTION:
			res.extend((movieInfo.name, movieInfo.description, begin_string, movieInfo.servicename, duration))
		elif self.list_type == MovieList.LISTTYPE_COMPACT:
			res.extend((movieInfo.name, tags, movieInfo.servicename, begin_string, duration))
		else:
			assert(self.list_type == MovieList.LISTTYPE_MINIMAL)
			if self.descr_state == MovieList.SHOW_DESCRIPTION:
				res.extend((movieInfo.name, begin_string))
			else:
				res.extend((movieInfo.name, duration))
		return res

	def moveToIndex(self, index):
		self.instance.moveSelectionTo(index)

	def getCurrentIndex(self):
		return self.instance.getCurrentIndex()

	def getCurrentEvent(self):
		l = self.l.getCurrentSelection()
		return l and l[0].info.getEvent(l[0].serviceref)

	def getCurrent(self):
		l = self.l.getCurrentSelection()
		return l and l[0].serviceref

	GUI_WIDGET = eListbox

	def postWidgetCreate(self, instance):
		instance.setContent(self.l)
		self.selectionChanged_conn = instance.selectionChanged.connect(self.selectionChanged)

	def preWidgetRemove(self, instance):
		instance.setContent(None)
		self.selectionChanged_conn = None

	def _onMediaDbScanFinished(self, directory, total, successful, skipped):
		if not config.media_database.readmeta.value:
			return
		Log.w("%s" %(directory,))
		if self.root.getPath().startswith(directory):
			selected = self.getCurrent()
			self.load(self.root, self.filter_tags)
			if selected:
				self.moveTo(selected)

	def onShow(self):
		if self.root:
			self.reload(self.root)

	def reload(self, root = None, filter_tags = None):
		root = root or self.root
		if root and config.media_database.readmeta.value:
			eMediaDatabase.getInstance().rescanPath(root.getPath(), True)
		self.load(root, filter_tags)
		self.l.setList(self.list)

	def removeService(self, service):
		for l in self.list[:]:
			if l[0].serviceref == service:
				self.list.remove(l)
		self.l.setList(self.list)

	def __len__(self):
		return len(self.list)

	def load(self, root, filter_tags):
		# this lists our root service, then building a 
		# nice list
		
		self.list = [ ]
		self._vlist = {}
		self.serviceHandler = eServiceCenter.getInstance()

		self.root = root
		self.filter_tags = filter_tags
		services = self.serviceHandler.list(root)
		if services is None:
			print("listing of movies failed")
			services = []
			return
		tags = set()

		while True:
			serviceref = services.getNext()
			if not serviceref.valid():
				break
			if serviceref.flags & eServiceReference.mustDescent:
				continue
			info = self.serviceHandler.info(serviceref)
			if info is None:
				continue

			movieInfo = MovieInfo(serviceref, info, -1)
			tags |= movieInfo.tags

			# filter_tags is either None (which means no filter at all), or 
			# a set. In this case, all elements of filter_tags must be present,
			# otherwise the entry will be dropped.			
			if filter_tags and not movieInfo.tags.issuperset(filter_tags):
				continue
		
			self.list.append((movieInfo,))
			self._virtualize(movieInfo)
		
		if self.sort_type == MovieList.SORT_ALPHANUMERIC:
			self.list.sort(key=self._sortAlphaNumeric)
		else:
			# sort: key is 'begin'
			self.list.sort(key=lambda m: -m[0].begin)
		
		# finally, store a list of all tags which were found. these can be presented
		# to the user to filter the list
		self.tags = tags
		self._finalize()

	def _virtualize(self, movieInfo):
		vlist = self._vlist
		self._assignEpisode(movieInfo)
		entries = vlist.get(movieInfo.title, [])
		entries.append(movieInfo)
		vlist[movieInfo.title] = entries

	def _finalize(self):
		rootlist = []
		for key, entries in self._vlist.iteritems():
			if len(entries) == 1:
				rootlist.append(entries[0])
			else:
				rootlist.append(entries)
		Log.w(rootlist)

	def _assignEpisode(self, movieInfo):
		separator = (": ", " - ")
		split = [movieInfo.name]
		for sep in separator:
			split = movieInfo.name.split(sep, 1)
			if len(split) > 1:
				break
		if len(split) > 1:
			movieInfo.title = split[0]
			movieInfo.episode = split[1]

	def _sortAlphaNumeric(self, movieInfo):
		movieInfo = movieInfo[0]
		name = movieInfo.name
		return (name and name.lower() or "", -movieInfo.begin)

	def moveTo(self, serviceref):
		count = 0
		for movie in self.list:
			if movie[0].serviceref == serviceref:
				self.instance.moveSelectionTo(count)
				return True
			count += 1
		return False
	
	def moveDown(self):
		self.instance.moveSelection(self.instance.moveDown)
