# -*- coding: utf-8 -*-
#
#  ExternalMovieInfoLoader
#
#  Coded by dre (c) 2014 - 2023
#  Coding idea and design by dre
#  E-Mail: dre@dreamboxtools.de
#  Support: board.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.
# In case of any modification of the code or parts of it you MUST request your own API-key.

# TODO:
# - ContextMenu-Version entfernen
# - Uebersetzungen
# - Code vereinfachen
# - Cover vorhanden / Descr nicht vorhanden: was tun? Abfrage auf mode

from Components.config import config, ConfigSubsection, ConfigSelection, getConfigListEntry, ConfigYesNo, ConfigText, configfile
from Components.ActionMap import *
from Components.ConfigList import ConfigListScreen
from Components.Element import Element
from Components.Label import Label
from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaBlend
from Components.Pixmap import Pixmap
from Components.ScrollLabel import ScrollLabel
from Components.Sources.List import List
from Plugins.Plugin import PluginDescriptor
from Screens.HelpMenu import HelpableScreen
from Screens.MessageBox import MessageBox
from Screens.MovieSelection import MovieContextMenu, MovieSelection
from Screens.Screen import Screen
from Tools.Directories import fileExists, SCOPE_CURRENT_SKIN, resolveFilename
from Tools.LoadPixmap import LoadPixmap
from Tools.NumericalTextInput import NumericalTextInput

from enigma import eServiceCenter, eServiceReference, eEnv, iServiceInformation
from os import listdir as os_listdir, path as os_path, system as os_system
from re import compile
from twisted.web.client import downloadPage, getPage
from twisted.python.failure import Failure
from urllib import quote
import json
from os import path as os_path, stat as os_stat

from Components.UsageConfig import defaultMoviePath
from stat import ST_MTIME as stat_ST_MTIME, ST_SIZE as stat_ST_SIZE
from Components.Merlin import MerlinImage
from merlin.emerlin import eMerlin
from Components.ExternalMovieInfo import ExternalMovieInfo

if fileExists("/usr/lib/enigma2/python/Plugins/SystemPlugins/Toolkit/NTIVirtualKeyBoard.pyo"):
	from Plugins.SystemPlugins.Toolkit.NTIVirtualKeyBoard import NTIVirtualKeyBoard
	ntikb = True
else:
	from Screens.VirtualKeyBoard import VirtualKeyBoard
	ntikb = False

# for localized messages
from . import _

BASEURL = 'http://api.themoviedb.org/3/'

choiceList = [(eEnv.resolve("${datadir}/enigma2/cover"))]
                
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 "[EMIL] - 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)))):
			choiceList.extend([("/media/%s/cover" %(dir))])
	if os_path.ismount("/media/cf") or (os_path.islink("/media/cf") and os_path.ismount(os_path.realpath("/media/cf"))):
		choiceList.extend([("/media/cf/cover")])
	if os_path.ismount("/media/usb") or (os_path.islink("/media/usb") and os_path.ismount(os_path.realpath("/media/usb"))):
		choiceList.extend([("/media/usb/cover")])
	if os_path.ismount("/media/sdcard") or (os_path.islink("/media/sdcard") and os_path.ismount(os_path.realpath("/media/sdcard"))):
		choiceList.extend([("/media/sdcard/cover")])                   
	if os_path.ismount("/data"):
		choiceList.extend([("/data/cover")])
	if os_path.ismount("/dreambox-data"):
		choiceList.extend([("/dreambox-data/cover")])
	choiceList.extend([("directory of movie")])

# config			
config.plugins.externalmovieinfoloader = ConfigSubsection()
config.plugins.externalmovieinfoloader.coverpath = ConfigSelection(choiceList, default = eEnv.resolve("${datadir}/enigma2/cover"))
config.plugins.externalmovieinfoloader.askfortype = ConfigYesNo(default=False)
config.plugins.externalmovieinfoloader.usefirstentry = ConfigYesNo(default=False)
config.plugins.externalmovieinfoloader.infolanguage = ConfigSelection([("en", _("English")), ("de", _("German"))], default="en")
config.plugins.externalmovieinfoloader.askforreplace = ConfigYesNo(default=False)
config.plugins.externalmovieinfoloader.mode = ConfigSelection(choices = [("both", _("Cover & Description")),("cover",_("Cover")),("description",_("Description"))], default = "both")
config.plugins.externalmovieinfoloader.searchlanguage = ConfigSelection([("en", _("English")), ("de", _("German"))], default="en")
config.plugins.externalmovieinfoloader.apikey = ConfigText(default="", fixed_size=False)

#pattern: show.SnnEnn.filetype / show - SnnEnn - episode.filetype
tvseries = compile('(.*?)[\s\.-]*[S|s][0-9]+[E|e][0-9]+(.*?)')
#pattern: SnnEnn.show.filetype / SnnEnn - show.filetype
tvseriesalt = compile('^[S|s][0-9]+[E|e][0-9]+[\s\.\-]*(.*?)\.[ts|m2ts|mp3|wav|ogg|jpg|jpeg|jpe|png|bmp|mpg|mpeg|mkv|mp4|mov|divx|avi|mp2|m4a|flac|ifo|vob|iso|sh|flv|3gp|mod|wmv]')
#pattern: show - season (episodenr/total episodes) (no filetype!)
tvseriestotal= compile('(.*?)[\s-][\s](.*?)\([0-9]+\/[0-9]+\)') #.[ts|m2ts|mp3|wav|ogg|jpg|jpeg|jpe|png|bmp|mpg|mpeg|mkv|mp4|mov|divx|avi|mp2|m4a|flac|ifo|vob|iso|sh|flv|3gp|mod|wmv]')
#pattern: show - (nxn) episode (videodb)
tvseriesvideodb = compile('(.*?)[\s\.-]*\([0-9]+x[0-9]+\)(.*?)')
#pattern: movies
movietitle = compile('(.*\w)\.[ts|m2ts|mp3|wav|ogg|jpg|jpeg|jpe|png|bmp|mpg|mpeg|mkv|mp4|mov|divx|avi|mp2|m4a|flac|ifo|vob|iso|sh|flv|3gp|mod|wmv]')
#pattern: movie (year).filetype
movieyear = compile('(.*\w) \([0-9]+\)\.[ts|m2ts|mp3|wav|ogg|jpg|jpeg|jpe|png|bmp|mpg|mpeg|mkv|mp4|mov|divx|avi|mp2|m4a|flac|ifo|vob|iso|sh|flv|3gp|mod|wmv]')
seasonepisode = compile('(.*?)[S|s]([0-9]+)[E|e]([0-9]+)(.*?)')

serieswoepisodes = compile('(.*\w)\s*-\s*(.*?)\.[ts|m2ts|mp3|wav|ogg|jpg|jpeg|jpe|png|bmp|mpg|mpeg|mkv|mp4|mov|divx|avi|mp2|m4a|flac|ifo|vob|iso|sh|flv|3gp|mod|wmv]')

class ExternalMovieInfoLoaderSearchResult(Screen, HelpableScreen):
	skin = """
		<screen name="ExternalMovieInfoLoaderSearchResult" position="center,center" size="720,550" title="ExternalMovieInfoLoader - Search Result">
			<widget name="SearchString" font="Regular;18" position="0,0" size="720,20" transparent="1" halign="center" />
			<ePixmap alphatest="on" pixmap="skin_default/progress_small.png" position="80,30" size="560,3"/>
			<widget source="list" render="Listbox" position="0,40" size="720,375" scrollbarMode="showOnDemand">
				<convert type="TemplatedMultiContent">
					{"template": [
									MultiContentEntryText(pos=(0,0), size=(490,20), flags=RT_HALIGN_LEFT, text=0),
									MultiContentEntryText(pos=(490,0), size=(100,20), flags=RT_HALIGN_RIGHT, text=1),
									MultiContentEntryText(pos=(590,0), size=(100,20), flags=RT_HALIGN_RIGHT, text=6),
					],
					"fonts": [gFont("Regular",18)],
					"itemHeight": 25
					}
				</convert>
			</widget>
			<widget name="Description" position="30,440" size="570,40" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="Yellow" pixmap="skin_default/buttons/button_yellow.png" position="10,492" zPosition="10" size="16,16" transparent="1" alphatest="on" />			
			<widget name="Search" position="30,490" size="300,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="Red" pixmap="skin_default/buttons/button_red.png" position="10,522" zPosition="10" size="16,16" transparent="1" alphatest="on" />	
			<widget name="Abort" position="30,520" size="300,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="tmdblogo" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/ExternalMovieInfoLoader/tmdb.png" position="630,500" size="80,54" transparent="1" alphatest="blend" />
		</screen>
		<!-- 0 = name -->
		<!-- 1 = airdate -->
		<!-- 2 = filename poster -->
		<!-- 3 = ID -->
		<!-- 4 = download-URL -->
		<!-- 5 = filename used for cover -->
		<!-- 6 = type (movie / tv series -->
		<!-- 7 = name of file -->				
		"""

	def __init__(self,session, menulist, searchString="", folderSearch=False, descriptionOnly=False):
		self.skin = ExternalMovieInfoLoaderSearchResult.skin	
		self.menulist = menulist
		self.searchString = searchString
		self.folderSearch = folderSearch
		self.descriptionOnly = descriptionOnly
		Screen.__init__(self,session)
		HelpableScreen.__init__(self)
		
		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
		{
			"ok":		(self.initiateDownload, _("Start download")),
			"cancel":	(self.close, _("Close")),
		}, -1)		

		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"yellow":	(self.editSearchString,_("Edit movie title")),
			"red":		(self.forcedExit, _("Abort search")),
		}, -1)		

		self["list"] = List(self.menulist)
		self["Description"] = Label(_("Press OK to download data for the selected movie"))
		self["Search"] = Label(_("Edit movie title"))
		self["Abort"] = Label(_("Abort search"))
		self["Yellow"] = Pixmap()
		self["Red"] = Pixmap()
		self["tmdblogo"] = Pixmap()
		self["SearchString"] = Label(self.searchString)
		self.headers = {
			'Accept':'application/json',
		}

		self.result_dialog_text = ""
		self.onShown.append(self.setWindowTitle)
		if self.folderSearch and config.plugins.externalmovieinfoloader.usefirstentry.value:
			self.onShown.append(self.initiateDownload)
		
	def getDescription(self):
		print "[EMIL] - getDescription", self.selectedIndex
		if self.selectedIndex is None:
			self.selectedIndex = self["list"].index
			if self.folderSearch:
				self.selectedIndex = 0
		
		print self.menulist[self.selectedIndex][6]
		if self.menulist[self.selectedIndex][6] == _("movie"):
			type = "movie"
			getPage('%s%s/%s?api_key=%s&language=%s' %(BASEURL, type, self.menulist[self.selectedIndex][3], config.plugins.externalmovieinfoloader.apikey.value, config.plugins.externalmovieinfoloader.infolanguage.value), method='GET', headers=self.headers).addCallback(self.writeMovieData).addErrback(self.getError)				
		else:
			type = "tv"
			fullname = self.menulist[self.selectedIndex][8].getPath()
			if seasonepisode.match(fullname) != None:
				if len(seasonepisode.match(fullname).groups()) > 0:
					season = seasonepisode.match(fullname).groups()[1]
					episode = seasonepisode.match(fullname).groups()[2]
			
					if season != None and episode != None and season != "" and episode != "":
						getPage('%s%s/%s/season/%s/episode/%s?api_key=%s&language=%s' %(BASEURL, type, self.menulist[self.selectedIndex][3], season, episode, config.plugins.externalmovieinfoloader.apikey.value, config.plugins.externalmovieinfoloader.infolanguage.value), method='GET', headers=self.headers).addCallback(self.writeMovieData).addErrback(self.getError)
			else:
				self.close()

	def writeMovieData(self, result):
		print "result is", result
		info_dict = {}
		#convert result string to a dictionary
		info_dict = json.loads(result)

		ts = self.menulist[self.selectedIndex][8].getPath()
		
		os_system('touch "%s.extinfo"' %(ts))
		print "ts is", ts
		with open("%s.extinfo" %(ts),'we') as jfile:
			#write dictionary to a file (this must be a dictionary otherwise we will get a string when reading the file
			json.dump(info_dict, jfile)

		season = -1
		episode = -1
		duration = eMerlin.getInstance().getDuration(ts)
		extended_description = ""
		title = self.menulist[self.selectedIndex][0]
		short_description = ""
		if info_dict.has_key("episode_number"):
			episode =  info_dict["episode_number"]
	
		if info_dict.has_key("season_number"):
			season =  info_dict["season_number"]
		
		if info_dict.has_key("overview"):
			extended_description = info_dict["overview"]
	
		if info_dict.has_key("name"):
			short_description = info_dict["name"]
	
	
		if season != -1 and episode != -1:
			short_description = "%s (S%02dE%02d)" % (short_description, season, episode)
		
	
		with open("%s.txt" %(os_path.splitext(ts)[0]),'we') as textfile:
			textfile.write("title=%s\n" % title)
			if len(short_description):
				textfile.write("short-description=%s\n" % short_description)
			textfile.write("duration=%d\n" % duration)
			if len(extended_description):
				textfile.write("long-description=%s\n" % extended_description)

		filestats = os_stat(ts)
		mtime = filestats[stat_ST_MTIME]
		filesize = filestats[stat_ST_SIZE]
		
		# for now we disable overwriting again
		#eMerlin.getInstance().updateMeta(ts.encode('utf-8'), title.encode('utf-8'), short_description.encode('utf-8'), mtime, duration*90000, filesize)
		
		# might be updated with new title! Generate new cover filename
		self.cover_filename = ExternalMovieInfo.generateFilename(self.menulist[self.selectedIndex][8], config.plugins.externalmovieinfoloader.coverpath.value, ".jpg")
		
		self.result_dialog_text = _("Movie data was downloaded successfully")
		if not (config.plugins.externalmovieinfoloader.mode.value == "description" or self.descriptionOnly == True or self.menulist[self.selectedIndex][4] is None):
			self.downloadCover()
		else:
			if not self.folderSearch:	
				self.session.openWithCallback(self.forcedExit, MessageBox,self.result_dialog_text, MessageBox.TYPE_INFO, timeout=5, title=_("External Movie Info Loader"))
			else:
				self.close()	
		
	def forcedExit(self, *args):
		self.close("exitDialog")
		
	def editSearchString(self):
		if ntikb:
			self.session.openWithCallback(self.initiateSearch, NTIVirtualKeyBoard, title=_("Search for"), text="%s" %(self.searchString))
		else:
			self.session.openWithCallback(self.initiateSearch, VirtualKeyBoard, title=_("Search for"), text="%s" %(self.searchString))
		
	def initiateSearch(self, searchString):	
		if searchString is None:
			searchString = ""
		self.close(searchString)
		
	def setWindowTitle(self):
		self.setTitle(_("ExternalMovieInfoLoader - Search Result"))
	
	def initiateDownload(self):
		if len(self.menulist) <= 0:
			self.close()
		else:
			if self.folderSearch and config.plugins.externalmovieinfoloader.usefirstentry.value:
				self.selectedIndex = 0
			else:
				self.selectedIndex = self["list"].index
			self.cover_filename = self.menulist[self.selectedIndex][7]
			if config.plugins.externalmovieinfoloader.mode.value == "cover":
				self.downloadCover()
			else:
				self.getDescription()

	def downloadCover(self):
		print "[EMIL] - downloadCover"
		if len(self.menulist) > 0:
			url = self.menulist[self.selectedIndex][4]
			path = os_path.dirname(self.cover_filename)
			if not fileExists(path):
				os_system("mkdir %s" %(path))
			if url is not None:
				downloadPage(url, file(self.cover_filename,'wb')).addErrback(self.getError).addCallback(self.downloadFinished)
			else:
				self.downloadFinished("")
		else:
			self.close()
		
	def downloadFinished(self, result):
		if result != "":
			print "[EMIL] - finished download of %s" %(self.cover_filename)
			if self.result_dialog_text == "":
				txt = _("The cover %s was downloaded successfully") %(self.cover_filename)
			else:
				txt = _("Cover and movie data were downloaded successfully")
		else:
			if self.result_dialog_text == "":
				txt = _("No cover available")
			else:
				txt = _("No cover found. Movie data was downloaded successfully")	
		if not self.folderSearch:
			self.session.openWithCallback(self.forcedExit, MessageBox,txt, MessageBox.TYPE_INFO, timeout=5, title=_("External Movie Info Loader"))
		else:
			self.close()
		
	def getError(self, error):
		print "[EMIL]- Twisted error: ", error
		os_system("echo '%s' >> /tmp/emil.log" %(self.menulist[self.selectedIndex][8].getPath()))
		os_system("echo '%s' >> /tmp/emil.log" %(error.getErrorMessage()))
		os_system("echo '%s' >> /tmp/emil.log" %(error))
		
		if not self.folderSearch:
			self.session.open(MessageBox,_("Error:\n%s") %(error.getErrorMessage()), MessageBox.TYPE_ERROR, title="External Movie Info Loader", timeout=5)
		else:
			self.close()

class ExternalMovieInfoLoaderSetup(Screen, HelpableScreen, ConfigListScreen):
	skin = """
		<screen name="ExternalMovieInfoLoaderSetup" position="center,center" size="750,300" title="ExternalMovieInfoLoader Setup" >
			<widget name="config" position="0,0" size="750,200" scrollbarMode="showOnDemand" />
			<ePixmap alphatest="on" pixmap="skin_default/buttons/button_green.png" position="10,222" size="16,16"/>
			<widget name="Info" font="Regular;18" position="0,190" size="750,25" foregroundColor="red" halign="center" />
			<widget name="SaveText" font="Regular;18" position="40,220" size="460,25" transparent="0"/>
			<widget name="Credits" font="Regular;18" position="10,250" size="640,40" transparent="1"/>
		</screen>"""
		
	def __init__(self, session, args = 0):
		self.skin = ExternalMovieInfoLoaderSetup.skin
		self.session = session
		Screen.__init__(self, session)
		HelpableScreen.__init__(self)
		
		self.list = []
		
		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
		{
			"cancel":	(self.close, [_("Close"),"Close the setup"]),
		}, -1)
		
		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"green":	(self.saveConfig, _("Save config")),
		}, -1)
		
	
		self["SaveText"] = Label(_("Save config and close"))
		self["Info"] = Label(_("Cover and description must be activated in movielist separately"))
		self["Credits"] = Label("This product uses the TMDb API but is not endorsed or certified by TMDb.")
		
		ConfigListScreen.__init__(self, self.list)
		
		self.list.append(getConfigListEntry(_("Cover path:"), config.plugins.externalmovieinfoloader.coverpath))
		#self.list.append(getConfigListEntry(_("Ask for type in single mode:"), config.plugins.externalmovieinfoloader.askfortype))
		self.list.append(getConfigListEntry(_("Ask whether to replace existing cover in single mode:"), config.plugins.externalmovieinfoloader.askforreplace))
		self.list.append(getConfigListEntry(_("Always use first search result in multi mode:"), config.plugins.externalmovieinfoloader.usefirstentry))
		self.list.append(getConfigListEntry(_("Language for tmdb data:"), config.plugins.externalmovieinfoloader.infolanguage))
		self.list.append(getConfigListEntry(_("Language for search:"), config.plugins.externalmovieinfoloader.searchlanguage))
		self.list.append(getConfigListEntry(_("Download:"), config.plugins.externalmovieinfoloader.mode))
		self.list.append(getConfigListEntry(_("API-Key:"), config.plugins.externalmovieinfoloader.apikey))
		self["config"].setList(self.list)
		
	def saveConfig(self):
		for x in self.list:
			x[1].save()
		self.close()

class ExternalMovieInfoLoader(Screen, HelpableScreen):
	skin = """
			<screen name="ExternalMovieInfoLoader" position="center,center" size="900,630" title="External Movie Info Loader">
			<widget font="Regular;20" halign="center" name="scan" position="0,0" size="900,550" valign="center" zPosition="4"/>			
			<widget source="list" render="Listbox" position="0,0" size="900,550" scrollbarMode="showOnDemand" zPosition="2">
				<convert type="TemplatedMultiContent">
					{"template": [
									#MultiContentEntryPixmapAlphaBlend(pos=(10,0), size=(90,50), png= 5),
									MultiContentEntryText(pos=(10,0), size=(870,20), flags=RT_HALIGN_LEFT, text=2)
					],
					"fonts": [gFont("Regular",18)],
					"itemHeight": 50
					}
				</convert>
			</widget>
			<widget name="ButtonYellowText" position="30,570" size="350,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="ButtonYellow" pixmap="skin_default/buttons/button_yellow.png" position="10,570" zPosition="10" size="16,16" transparent="1" alphatest="on" />
			<widget name="ButtonBlueText" position="470,570" size="350,20" valign="center" halign="left" zPosition="10" font="Regular;18" transparent="1" />
			<widget name="ButtonBlue" pixmap="skin_default/buttons/button_blue.png" position="450,570" zPosition="10" size="16,16" transparent="1" alphatest="on" />
			<widget name="tmdblogo" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/ExternalMovieInfoLoader/tmdb.png" position="820,570" size="80,54" transparent="1" alphatest="blend" />
			<widget name="Credits" font="Regular;18" position="10,610" size="780,20" transparent="1"/>
		</screen>
		<!-- for skinners: -->
		<!-- 0 = serviceref -->
		<!-- 1 = name -->
		<!-- 2 = name used for search -->
		<!-- 3 = filename used for cover -->
		<!-- 4 = name of file -->
		<!-- 5 = pointer to pixmap (use to display cover with MultiContentEntryPixmapAlphaBlend) -->
		<!-- 5   example: MultiContentEntryPixmapAlphaBlend(pos=(10,0), size=(100,60), png=5) -->		
		"""

	def __init__(self, session, serviceref=None, parent=None, root=None):
		self.skin = ExternalMovieInfoLoader.skin
		self.session = session
		self.parent = parent
		self.root = root
		Screen.__init__(self, session, parent=parent)
		HelpableScreen.__init__(self)
		
		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
		{
			"ok":		(self.getMovieDataForSelected, _("Load movie data for selected movie")),
			"cancel":	(self.exitDialog, _("Close")),
		}, -1)
		
		self["MovieSelectionActions"] = HelpableActionMap(self, "MovieSelectionActions",
		{
			"contextMenu":	(self.openConfig, _("Open config")),
			"showEventInfo":	(self.showLog, _("Show log")),
		}, -1)

		
		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"blue": (self.getMovieDataForAll, _("Load movie data for all movies")),
			"yellow": (self.getMovieDataForSelected, _("Load movie data for selected movie")),
		}, -1)	
		
		self["NumberActions"] = NumberActionMap(["NumberActions","InputAsciiActions"],
		{
				"gotAsciiCode": self.keyAsciiCode,
				"0": self.keyNumberGlobal,
				"1": self.keyNumberGlobal,
				"2": self.keyNumberGlobal,
				"3": self.keyNumberGlobal,
				"4": self.keyNumberGlobal,
				"5": self.keyNumberGlobal,
				"6": self.keyNumberGlobal,
				"7": self.keyNumberGlobal,
				"8": self.keyNumberGlobal,
				"9": self.keyNumberGlobal,
		})
		
		self["ButtonBlue"] = Pixmap()
		self["ButtonBlueText"] = Label(_("Load movie data for all movies"))
		
		self["ButtonYellow"] = Pixmap()
		self["ButtonYellowText"] = Label(_("Load movie data for selected movie"))

		self["tmdblogo"] = Pixmap()
		self["Credits"] = Label("This product uses the TMDb API but is not endorsed or certified by TMDb.")
		
		self["scan"] = Label(_("Scanning movies...This may take a while."))

		self.movieList = []
		self["list"] = List(self.movieList)
		
		self.tmdbconfig = False
		self.folderSearch = False
		self.descriptionOnly = False
		self.movietype = "multi"

		self.numericalTextInput = NumericalTextInput()
		self.numericalTextInput.setUseableChars(u'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ')
				
		if serviceref is None:
			self.getMovies()
		else:
			self.getMovie(serviceref)		
			
	def keyAsciiCode(self):
		unichar = unichr(getPrevAsciiCode())
		charstr = unichar.encode("utf-8")
		if len(charstr) == 1:
			index = self.getFirstMatchingEntry(charstr[0])
			if index is not None:
				self["list"].setIndex(index)

	def keyNumberGlobal(self, number):
		unichar = self.numericalTextInput.getKey(number)
		charstr = unichar.encode("utf-8")
		if len(charstr) == 1:
			index = self.getFirstMatchingEntry(charstr[0])
			if index is not None:
				self["list"].setIndex(index)
			
	def getFirstMatchingEntry(self, char):
		for i in range(len(self.movieList)):
			if self.movieList[i][2].startswith(char):
				return i

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

	def showLog(self):
		self.session.open(ExternalMovieInfoLog)

	def prepareMovieList(self, name, meta_short_description, serviceref):
		extension = serviceref.getPath().split('.')[-1].lower()			
		if extension in MerlinImage.getAllExtensions():
			coverfilename = ExternalMovieInfo.generateFilename(serviceref, config.plugins.externalmovieinfoloader.coverpath.value, ".jpg")
			if not fileExists(coverfilename):
				jpg = resolveFilename(SCOPE_CURRENT_SKIN, "cover_default.png")
				if not fileExists(jpg):
					jpg = None
			else:
				jpg = coverfilename
			return (serviceref, name, jpg, meta_short_description, coverfilename)
		return None

	# used in movielist to get a single movie
	def getMovie(self, serviceref):
		self.movieList = []
		check = 1
		entry = None
		extension = serviceref.getPath().split('.')[-1].lower()
		if extension in MerlinImage.getExtensions():
				metaData = eMerlin.getInstance().parseMeta(serviceref.getPath())
				if metaData:
					check = 0
					entry = self.prepareMovieList(metaData.getName(), metaData.getDescription(), serviceref)	
		if check:					
			self.serviceHandler = eServiceCenter.getInstance()
			if serviceref.valid():
				info = self.serviceHandler.info(serviceref)
				if info:
					name = info.getName(serviceref)
					meta_short_description = info.getInfoString(serviceref, iServiceInformation.sDescription).encode('utf-8')
					
					entry = self.prepareMovieList(name, meta_short_description, serviceref)
				
		if entry is not None:
			self.movieList.append(entry)
		self["scan"].visible = False		
		self["list"].setList(self.movieList)
		self["list"].setBuildFunc(self.buildEntry)
		
	def buildEntry(self, serviceref, name, jpg, meta_short_description, coverfilename):
		if jpg is not None:
			ptr = LoadPixmap(jpg)
		else:
			ptr = None
		if meta_short_description != "":
			display = "%s % s" % (name, meta_short_description)
		else:
			display = name
		return (serviceref, name, display, jpg, serviceref.getPath(), ptr) # keep compatibility to skins
		
	# read all existing movies and add them to a list
	def getMovies(self):
		self.serviceHandler = eServiceCenter.getInstance()
		
		if self.root is None:
			self.root = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + defaultMoviePath())
		
		self.movieList = []
		
		dirList = [self.root]
		
		for dir in dirList:
			if '.@__thumb' in dir.getPath():
				continue
			fileList = self.serviceHandler.list(dir)
			while 1:
				serviceref = fileList.getNext()
				if not serviceref.valid():
					break
				entry = None
				check = 1
				extension = serviceref.getPath().split('.')[-1].lower()
				if extension in MerlinImage.getExtensions():
					metaData = eMerlin.getInstance().parseMeta(serviceref.getPath())
					if metaData:
						check = 0
						entry = self.prepareMovieList(metaData.getName(), metaData.getDescription(), serviceref)	
				if check:
					info = self.serviceHandler.info(serviceref)
					meta_short_description = ""
					if info:
						name = info.getName(serviceref)
						meta_short_description = info.getInfoString(serviceref, iServiceInformation.sDescription).encode('utf-8')
					if serviceref.flags & eServiceReference.mustDescent:
						folderName = info.getName(serviceref)
						if folderName != "..":
							dirList.append(serviceref)
					else:
						entry = self.prepareMovieList(name, meta_short_description, serviceref)
			
				if entry is not None:
					self.movieList.append(entry)
		
		# sort moiveList by name
		self.movieList.sort(key=lambda x: x[1].lower())
		self["scan"].visible = False			
		self["list"].setList(self.movieList)
		self["list"].setBuildFunc(self.buildEntry)		

	def prepareGetMovieData(self, index, folderSearch, descriptionOnly):
		self.index = index
		self.folderSearch = folderSearch
		self.descriptionOnly = descriptionOnly

		# only load description / no need to check existence of cover
		if config.plugins.externalmovieinfoloader.mode.value == "description":
			self.getMovieData()
		else:
			# check existence of cover
			if fileExists(self.movieList[self.index][4]):
				# check whether to ask
				if config.plugins.externalmovieinfoloader.askforreplace.value and self.folderSearch == False:
					self.session.openWithCallback(self.processAnswer, MessageBox,_("The cover %s already exists. Do you want to replace it?") %(self.movieList[self.index][4]), MessageBox.TYPE_YESNO, title="External Movie Info Loader")
				else:
					# don't ask
					self.getMovieData()
			# cover does not exist
			else:				
				self.getMovieData()		
		
	# get data for all movies in the list
	def getMovieDataForAll(self):
		self.prepareGetMovieData(index = 0, folderSearch = True, descriptionOnly = False)

	# get data for a single movie		
	def getMovieDataForSelected(self):
		self.prepareGetMovieData(index = self["list"].getIndex(), folderSearch = False, descriptionOnly = False)

	def processAnswer(self, answer):
		# delete cover
		if answer:
			os_system("rm %s" %(self.movieList[self.index][4]))
			self.getMovieData()
		# do not delete cover
		else:
			# only continue when description shall be loaded
			if config.plugins.externalmovieinfoloader.mode.value != "cover":
				self.descriptionOnly = True
				self.getMovieData()
			
	def getMovieData(self):
		if self.tmdbconfig == False:
			self.getTMDBConfig()
		else:
			self.getTMDBData(self.movieList[self.index][1])
			
	#get data from TMDB for the selected movie
	def getTMDBData(self, searchString=""):
		headers = {
			'Accept':'application/json',
		}
		
		if tvseriestotal.match(searchString) is not None:
			searchString = tvseriestotal.match(searchString).groups()[0]
		elif tvseriesalt.match(searchString) is not None:
			searchString = tvseriesalt.match(searchString).groups()[0]
		elif tvseries.match(searchString) is not None:
			searchString = tvseries.match(searchString).groups()[0]
		elif tvseriesvideodb.match(searchString) is not None:
			searchString = tvseriesvideodb.match(searchString).groups()[0]				
		elif movietitle.match(searchString) is not None:
			if serieswoepisodes.match(searchString) is not None:
				altSearchString = serieswoepisodes.match(searchString).groups()[0]	
			searchString = movietitle.match(searchString).groups()[0]
		elif movieyear.match(searchString) is not None:
			searchString = movieyear.match(searchString).groups()[0]
		
		searchString = searchString.rstrip()
					
		self.searchString = searchString
		
		getPage('%ssearch/%s?api_key=%s&query=%s&language=%s' %(BASEURL, self.movietype, config.plugins.externalmovieinfoloader.apikey.value, quote(self.searchString), config.plugins.externalmovieinfoloader.infolanguage.value), method='GET', headers=headers).addCallback(self.searchResult).addErrback(self.getError)
		

	# parse the result of the tmdb-call, then open a screen to display the result	
	def searchResult(self, result):
		self.result_dict = json.loads(result)
		
		#print "result", self.result_dict
		
		totalresults = 0
		totalresults = self.result_dict['total_results']
		totalpages = 0
		totalpages = self.result_dict['total_pages']
		maxvalue = 0
		self.menulist = []
		
		movie_id = ""
		poster = ""
		name = ""
		
		# don't show result screen when in folder search and no movie was found
		if totalresults == 0 and self.folderSearch:
			print "[EMIL] - empty search result in folder mode"
			self.closeOrSearch()
		else:
			if self.folderSearch and config.plugins.externalmovieinfoloader.usefirstentry.value:
				if totalresults > 0:
					maxvalue = 1
				else:
					self.closeOrSearch()
			else:			
				for p in range(0, totalpages):
					if p == totalpages-1:
						totalresultsreal = len(self.result_dict['results'])
						if totalresultsreal < totalresults:
							if totalresultsreal%20 == 0:
								maxvalue = totalresultsreal
							else:						
								maxvalue = totalresultsreal%20
						else:
							if totalresults%20 == 0:
								maxvalue = totalresults
							else:
								maxvalue = totalresults%20
					else:
						maxvalue = 20
						
			self.buildMenuList(maxvalue)	
			
			self.session.openWithCallback(self.closeOrSearch,ExternalMovieInfoLoaderSearchResult, self.menulist, self.searchString, self.folderSearch, self.descriptionOnly)

	# build a menulist with the search result			
	def buildMenuList(self, maxvalue):
		type = self.movietype
	
		for i in range(0,maxvalue):
			writeError = False
			try:
				poster = str(self.result_dict['results'][i]['poster_path'])
			except KeyError:
				print "[EMIL] - no poster_path available"
				continue
			try:
				movie_id = str(self.result_dict['results'][i]['id'])
			except KeyError:
				print "[EMIL] - no id available"
				continue
			if self.movietype == 'multi':
				try:
					type = self.result_dict['results'][i]['media_type']
				except KeyError:
					print "[EMIL] - no media_type available"				
					continue
			if type == "tv":
				movietype = _("TV series")
				try:
					name = self.result_dict['results'][i]['name']
				except KeyError:
					print "[EMIL] - no name available"			
					continue
				try:
					airdate = self.result_dict['results'][i]['first_air_date']
				except KeyError:
					print "[EMIL] - no first_air_date available"			
					continue
			elif type == "movie":
				movietype = _("movie")
				try:
					name = self.result_dict['results'][i]['title']
				except KeyError:
					print "[EMIL] - no title available"			
					continue
				try:
					airdate = self.result_dict['results'][i]['release_date']
				except KeyError:
					print "[EMIL] - no release_date available"				
					continue
			
			if poster is None:	
				url = None
			else:
				url = str(self.picurl) + str(self.picsize) + poster
			
			self.menulist.append((str(name), str(airdate), poster, movie_id, url, self.movieList[self.index][3], movietype, self.movieList[self.index][4], self.movieList[self.index][0]))
			
		print "menulist", self.menulist

	#get config mainly to get the available sizes and the url for the posters		
	def getTMDBConfig(self):
		headers = {
			'Accept':'application/json',
		}	
		getPage('%sconfiguration?api_key=%s' %(BASEURL, config.plugins.externalmovieinfoloader.apikey.value), method='GET', headers = headers).addCallback(self.configResult).addErrback(self.getError)
	
	# store values from config-call
	def configResult(self, result):
		config_dict = json.loads(result)
		
		self.picurl = config_dict['images']['base_url']
		
		sizes = config_dict['images']['poster_sizes']
		for s in sizes:
			if s == 'w185':
				self.picsize = s
				break
			if s == 'w342':
				self.picsize = s
				break
				
		self.tmdbconfig = True
		self.getTMDBData(self.movieList[self.index][1])

	# in case of an error print it			
	def getError(self, error):
		print "[EMIL] - There's an error", error		

	def exitDialog(self):
		if self.parent is not None:
			if isinstance(self.parent,MovieContextMenu):
				self.parent.csel.reloadList()
				self.parent.csel["CoverService"].changed((Element.CHANGED_ALL,))
				self.parent.csel["Service"].changed((Element.CHANGED_ALL,))
				self.parent.close()
			elif isinstance(self.parent,MovieSelection):
				self.parent.reloadList()
				self.parent["CoverService"].changed((Element.CHANGED_ALL,))
				self.parent["Service"].changed((Element.CHANGED_ALL,))
		self.close()	

	def closeOrSearch(self, searchString=""):
		if searchString == "":
			if self.folderSearch:
				if self.index < len(self.movieList)-1:
					self.index += 1
					self.getMovieData()
		elif searchString == "exitDialog":
			if len(self.movieList) == 1:
				self.exitDialog()
			else:
				current = self.movieList[self.index]
				coverfilename = ExternalMovieInfo.generateFilename(current[0], config.plugins.externalmovieinfoloader.coverpath.value, ".jpg")
				if fileExists(coverfilename):
					data = (self.movieList[self.index][0], self.movieList[self.index][1], coverfilename, self.movieList[self.index][3], coverfilename)
					self["list"].modifyEntry(self.index, data)
		else:
			self.getTMDBData(searchString)

class ExternalMovieInfoLog(Screen,HelpableScreen):
	skin = """
		<screen name="ExternalMovieInfoLog" position="center,center" size="900,630" title="External Movie Info Loader - Log">
			<widget name="logtext" position="10,10" size="880,610" font="Regular;20" />
		</screen>
		"""
		
	def __init__(self, session, args=0):
		self.skin = ExternalMovieInfoLog.skin
		self.session = session
		Screen.__init__(self, session)
		HelpableScreen.__init__(self)

		
		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
		{
			"cancel":	(self.close, _("Close")),
		}, -1)
		
		text = _("No log available")
		if fileExists("/tmp/emil.log"):
			logFile = open("/tmp/emil.log", "r")
			text = logFile.readlines().encode('utf-8')
			logFile.close()	
		
		self["logtext"] = ScrollLabel(text)
			
def context(session, service, **kwargs):
	session.open(ExternalMovieInfoLoader, service, session.current_dialog)
	
def contextfolder(session, service, **kwargs):
	session.open(ExternalMovieInfoLoader, None, session.current_dialog, service)
		
def main(session, **kwargs):
	if config.plugins.externalmovieinfoloader.apikey.value == "":
		if not fileExists("/usr/lib/enigma2/python/Plugins/Extensions/ExternalMovieInfoLoader/apikey.txt"):
			session.open(MessageBox, _("No API-key found. Please provide your API-key in apikey.txt"), MessageBox.TYPE_ERROR, title=_("External Movie Info Loader (EMIL)"), timeout=0)
		else:
			with open("/usr/lib/enigma2/python/Plugins/Extensions/ExternalMovieInfoLoader/apikey.txt", "r") as f:
				apikey = f.read().strip()
				if apikey == "":
					session.open(MessageBox, _("No API-key found. Please provide your API-key in apikey.txt"), MessageBox.TYPE_ERROR, title=_("External Movie Info Loader (EMIL)"), timeout=0)
				else:
					config.plugins.externalmovieinfoloader.apikey.value = apikey
					config.plugins.externalmovieinfoloader.save()
					configfile.save()
					
	if config.plugins.externalmovieinfoloader.apikey.value:
		session.open(ExternalMovieInfoLoader)

def Plugins(**kwargs):
	list = [
	PluginDescriptor(name=_("External Movie Info Loader (EMIL)"), description=_("Plugin to download external movie info"), where = [PluginDescriptor.WHERE_PLUGINMENU], fnc=main, icon = "externalmovieinfoloader.png")]
	
	if config.plugins.externalmovieinfoloader.apikey.value != "":
		list.append(PluginDescriptor(name=_("External Movie Info Loader (EMIL)"), description=_("External Movie Info Loader (EMIL)"), where = [PluginDescriptor.WHERE_MOVIELIST], fnc=context))
		list.append(PluginDescriptor(name=_("External Movie Info Loader (EMIL)"), description=_("External Movie Info Loader (EMIL)"), where = [PluginDescriptor.WHERE_MOVIELISTFOLDER], fnc=contextfolder))
	return list
	
def getCoverPathConfig():
	return config.plugins.externalmovieinfoloader.coverpath.value
