import mc
import re
import time
import threading

ASTRAL_BASE_URL = 'http://www.vrak.tv'
DATA_VERSION = 2
DATA_EXPIRATION = 60 * 60 * 24
KEY_DATA_VERSION = "dataVersion"
KEY_LAST_SHOW_LIST_UPDATE = "showsLastUpdated"
KEY_SHOWS = "shows"
KEY_SUFFIX_VIEW_COUNTER = ".viewCounter"
KEY_SUFFIX_VIDEO_TYPE = ".videoType"
KEY_SUFFIX_SHOW_PATH = ".path"
KEY_SUFFIX_EPISODE_LIST = ".episodes"
KEY_SUFFIX_LAST_UPDATED = ".lastUpdated"
KEY_SUFFIX_EPISODE_TITLE = ".title"
KEY_SUFFIX_EPISODE_THUMBNAIL = ".thumbnail"
KEY_SUFFIX_EPISODE_DESCRIPTION = ".description"
KEY_SUFFIX_EPISODE_VIDEO_URL = ".videoUrl"
KEY_SUFFIX_EPISODE_SEASON = ".season"
KEY_SUFFIX_EPISODE_NUMBER = ".number"
SEPARATOR = "<itemseparator>"


locks = dict()
MASTER_LOCK = threading.RLock()

def initCache():
	log = "call initCache"
	print log
	appConfig = mc.GetApp().GetLocalConfig()
	# TODO : remove this once testing has proven it works
	#appConfig.ResetAll()
	
	currentDataVersion = appConfig.GetValue(KEY_DATA_VERSION)
	lastUpdate = appConfig.GetValue(KEY_LAST_SHOW_LIST_UPDATE)
	
	log = "Current config is: DataVersion(" + currentDataVersion + "), lastUpdate(" + lastUpdate + "), Shows(" + appConfig.Implode(SEPARATOR, KEY_SHOWS) + ")"
	print log
	
	if currentDataVersion != "":
		if DATA_VERSION > int(currentDataVersion):
			log = "Resetting all config because new data version is " + str(DATA_VERSION) + " and old data version was " + currentDataVersion
			print log
			appConfig.ResetAll()
			appConfig.SetValue(KEY_DATA_VERSION, str(DATA_VERSION))
	else:
		appConfig.SetValue(KEY_DATA_VERSION, str(DATA_VERSION))
	
	lastUpdate = appConfig.GetValue(KEY_LAST_SHOW_LIST_UPDATE)
	
	if lastUpdate == "":
		updateShowListCache()
	else:
		expirationTime = float(lastUpdate) + DATA_EXPIRATION
		
		log = "Calculated expiration time is: " + str(expirationTime) + ", currentTime is: " + str(time.time())
		print log
		
		if time.time() > expirationTime:
			updateShowListCache()
		else:
			log = "List of shows still valid, last updated: " + lastUpdate
			print log
		
	log = "dataversion: " + str(DATA_VERSION)
	print log
	
	return 0

def updateShowListCache():
	log = "call updateShowListCache"
	print log
	appConfig = mc.GetApp().GetLocalConfig()
	shows = fetchShows()
	log = "Acquiring master lock..."
	print log
	MASTER_LOCK.acquire()
	try:
		# Workaround for reset just removing the first entry for that key instead of clearing all 
		# stacked values
		appConfig.SetValue(KEY_SHOWS, "")
		appConfig.Reset(KEY_SHOWS)
		
		for show in shows:
			appConfig.PushBackValue(KEY_SHOWS, show.name)
			showPathKey = show.name + KEY_SUFFIX_SHOW_PATH
			appConfig.SetValue(showPathKey, show.path)
			locks[show.name] = threading.RLock()
		
		lastUpdateTime = time.time()
		appConfig.SetValue(KEY_LAST_SHOW_LIST_UPDATE, str(lastUpdateTime))
		
		log = "Updated list of shows and last update time set to:" + str(lastUpdateTime)
		print log
	finally:
		log = "Releasing master lock"
		print log
		MASTER_LOCK.release()
	return 1

def getShowsFromCache():
	log = "call getShowsFromCache"
	print log
	appConfig = mc.GetApp().GetLocalConfig()
	
	log = "Acquiring master lock to read shows from cache..."
	print log
	MASTER_LOCK.acquire()
	try:
		showsString = appConfig.Implode(SEPARATOR, KEY_SHOWS)
		log = "Shows string: " + showsString
		print log
		
		shows = []
		if showsString != "":
			showlist = showsString.split(SEPARATOR)
			for showname in showlist:
				show = Show()
				show.name = showname
				showPathKey = show.name + KEY_SUFFIX_SHOW_PATH
				show.path = appConfig.GetValue(showPathKey)
				shows.append(show)
				locks[show.name] = threading.RLock()
		else:
			log = "Warning, no shows list found in cache, forgot to initialize cache?"
			print log
			
		showsString = appConfig.Implode(SEPARATOR, KEY_SHOWS)
		log = "Shows string after: " + showsString
		print log
	finally:
		log = "Releasing master lock after reading shows from cache"
		print log
		MASTER_LOCK.release()
	
	return shows

def fetchShows():
	log = "call fetchShows"
	print log

	sg = mc.Http()
	html = sg.Get(ASTRAL_BASE_URL + "/webtele/recherche/")
	#Charset of the website are in iso-8859-1
	html = html.decode('iso-8859-1').encode('utf-8')
	shows = []

	results = re.compile('<option value="(.+?)">(.+?)</option>').findall(html)
	mc.LogDebug("trying to load list of shows...")
	for num, name in results:
		if int(num) > 10:
			url = ASTRAL_BASE_URL + '/webtele/recherche/?type=0&emission=' + num + '&episode=0&tri=0'
			show = Show()
			show.name = name
			show.path = url
			shows.append(show)
	return shows
	
def getShowListItems(shows):
	log = "call getShowListItems"
	print log
	showItems = mc.ListItems()
	for show in shows:
		showItem = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
		showItem.SetLabel(show.name)
		showItem.SetPath(show.path)
		showItems.append(showItem)
	return showItems

def getEpisodeListItems(show, episodes):
	log = "call getEpisodeListItems"
	print log
	episodeItems = mc.ListItems()
	for episode in episodes:
		episodeItem = mc.ListItem(mc.ListItem.MEDIA_VIDEO_EPISODE)
		episodeItem.SetLabel(episode.title)
		episodeItem.SetSeason(episode.season)
		episodeItem.SetTVShowTitle(show.name)
		episodeItem.SetDescription(episode.description)
		episodeItem.SetThumbnail(episode.thumbnailUrl)
		episodeItem.SetTitle(episode.title)
		episodeItem.SetProperty("DetailUrl", episode.detailUrl)
		episodeItem.SetProperty("DoesDetailUrlIncludeData", episode.doesDetailUrlIncludeData)
		
		episodeItem.SetProperty("videoType", episode.videoType)
		episodeItem.SetProperty("viewCounter", str(episode.viewCounter))

		# Dummy path that simply needs to be unique, otherwise boxee won't refresh the thumbnail if the path is empty or always the same
		episodeItem.SetPath(episode.thumbnailUrl)
		#episodeItem.SetProperty("PlayPath", episode.videoPath)
		episodeItem.SetProperty("show.path", show.path)
		episodeItem.SetProperty("background", show.backgroundUrl)
		episodeItem.SetEpisode(episode.episode)
		episodeItems.append(episodeItem)
		
	return episodeItems
	
def updateShowDataCache(show, appConfig):
	log = "call updateShowDataCache"
	print log
	# Waiting for master lock
	log = "Waiting for Master lock to update show data cache..."
	print log
	MASTER_LOCK.acquire()
	try:
		log = "Acquiring lock to update show data cache (" + show.name + ")"
		print log
		locks[show.name].acquire()
	finally:
		log = "Releasing master lock after getting show data lock (" + show.name + ")"
		print log
		MASTER_LOCK.release()
		
	try:
		episodes = fetchShowEpisodes(show)
		clearShowDataCache(show, appConfig)
	
		for episode in episodes:
			key = show.name + KEY_SUFFIX_EPISODE_LIST
			appConfig.PushBackValue(key, episode.detailUrl)
			key = episode.detailUrl + KEY_SUFFIX_EPISODE_TITLE
			appConfig.SetValue(key, episode.title)

			key = episode.detailUrl + KEY_SUFFIX_VIDEO_TYPE
			appConfig.SetValue(key, episode.videoType)

			key = episode.detailUrl + KEY_SUFFIX_VIEW_COUNTER
			appConfig.SetValue(key, episode.viewCounter)
		
			key = episode.detailUrl + KEY_SUFFIX_EPISODE_THUMBNAIL
			appConfig.SetValue(key, episode.thumbnailUrl)
			key = episode.detailUrl + KEY_SUFFIX_EPISODE_DESCRIPTION
			appConfig.SetValue(key, episode.description)
			#key = episode.detailUrl + KEY_SUFFIX_EPISODE_VIDEO_URL
			#appConfig.SetValue(key, episode.videoUrl)
			key = episode.detailUrl + KEY_SUFFIX_EPISODE_SEASON
			appConfig.SetValue(key, str(episode.season))
			key = episode.detailUrl + KEY_SUFFIX_EPISODE_NUMBER
			appConfig.SetValue(key, str(episode.episode))
	
		lastUpdateTime = time.time()
		key = show.name + KEY_SUFFIX_LAST_UPDATED
		appConfig.SetValue(key, str(lastUpdateTime))
	
		log = "Updated list of episodes for show (" + show.name + ") and set episode list last update time to:" + str(lastUpdateTime)
		print log
	finally:
		locks[show.name].release()
		log = "Releasing lock after update to show data cache (" + show.name + ")"
		print log
	return 1
	
def clearShowDataCache(show, appConfig):
	log = "call clearShowDataCache"
	print log
	# Waiting for master lock
	log = "Waiting for Master lock to clear show data cache..."
	print log
	MASTER_LOCK.acquire()
	try:
		log = "Acquiring lock to clear show data cache (" + show.name + ")"
		print log
		locks[show.name].acquire()
	finally:
		log = "Releasing master lock after getting lock for show data cache (" + show.name + ")"
		print log
		MASTER_LOCK.release()
		
	try:
		key = show.name + KEY_SUFFIX_EPISODE_LIST
		episodesString = appConfig.Implode(SEPARATOR, key)
		log = "Episodes string: " + episodesString
		print log		
	
		episodes = []
		if episodesString != "":
			episodes = episodesString.split(SEPARATOR)
			for detailUrl in episodes:
				key = detailUrl + KEY_SUFFIX_EPISODE_TITLE
				appConfig.Reset(key)
				key = detailUrl + KEY_SUFFIX_VIEW_COUNTER
				appConfig.Reset(key)
				key = detailUrl + KEY_SUFFIX_VIDEO_TYPE
				appConfig.Reset(key)				
				key = detailUrl + KEY_SUFFIX_EPISODE_THUMBNAIL
				appConfig.Reset(key)
				key = detailUrl + KEY_SUFFIX_EPISODE_DESCRIPTION
				appConfig.Reset(key)
				#key = detailUrl + KEY_SUFFIX_EPISODE_VIDEO_URL
				#appConfig.Reset(key)
				key = detailUrl + KEY_SUFFIX_EPISODE_SEASON
				appConfig.Reset(key)
				key = detailUrl + KEY_SUFFIX_EPISODE_NUMBER
				appConfig.Reset(key)
			key = show.name + KEY_SUFFIX_EPISODE_LIST
			# Workaround for reset just removing the first entry for that key instead of clearing all 
			# stacked values
			appConfig.SetValue(key, "")
			appConfig.Reset(key)
			key = show.name + KEY_SUFFIX_LAST_UPDATED
			appConfig.Reset(key)
		else:
			log = "Warning, no episode list found in cache, cache inconsistent"
			print log
	finally:
		locks[show.name].release()
		log = "Releasing lock after clearing show data cache (" + show.name + ")"
		print log
	log = "Cleared show data from cache (" + show.name + ")"
	print log
	return 1
	
def getShowDataFromCache(show):
	log = "call getShowDataFromCache"
	print log
	appConfig = mc.GetApp().GetLocalConfig()
	
	# Waiting for master lock
	log = "Waiting for Master lock to retrieve show data cache..."
	print log
	MASTER_LOCK.acquire()
	try:
		log = "Acquiring lock to retrieve show data cache (" + show.name + ")"
		print log
		locks[show.name].acquire()
	finally:
		log = "Releasing master lock after getting lock for show data cache (" + show.name + ")"
		print log
		MASTER_LOCK.release()
		
	try:
		key = show.name + KEY_SUFFIX_LAST_UPDATED
		lastUpdate = appConfig.GetValue(key)
		
		if lastUpdate == "":
			updateShowDataCache(show, appConfig)
		else:
			expirationTime = float(lastUpdate) + DATA_EXPIRATION
			
			log = "Calculated expiration time for episodes is: " + str(expirationTime) + ", currentTime is: " + str(time.time())
			print log
			
			if time.time() > expirationTime:
				updateShowDataCache(show, appConfig)
			else:
				log = "List of episodes still valid, last updated: " + lastUpdate
				print log
		
		key = show.name + KEY_SUFFIX_EPISODE_LIST
		episodesString = appConfig.Implode(SEPARATOR, key)
		log = "Episodes string: " + episodesString
		print log
		
	
		episodes = []
		if episodesString != "":
			detailUrls = episodesString.split(SEPARATOR)
			for detailUrl in detailUrls:
				episode = Episode()
				episode.detailUrl = detailUrl
				log = "parsing cache data for episode: " + str(detailUrl)
				print log
				key = detailUrl + KEY_SUFFIX_EPISODE_TITLE
				episode.title = appConfig.GetValue(key)				
				key = detailUrl + KEY_SUFFIX_EPISODE_THUMBNAIL
				episode.thumbnailUrl = appConfig.GetValue(key)
				key = detailUrl + KEY_SUFFIX_EPISODE_DESCRIPTION
				episode.description = appConfig.GetValue(key)				
				key = detailUrl + KEY_SUFFIX_VIEW_COUNTER
				episode.viewCounter = appConfig.GetValue(key)
				key = detailUrl + KEY_SUFFIX_VIDEO_TYPE
				episode.videoType = appConfig.GetValue(key)
				key = detailUrl + KEY_SUFFIX_EPISODE_SEASON
				episode.season = int(appConfig.GetValue(key))
				key = detailUrl + KEY_SUFFIX_EPISODE_NUMBER
				episode.episode = int(appConfig.GetValue(key))
				episodes.append(episode)
		else:
			log = "Warning, no episode list found in cache, something is wrong"
			print log
	finally:
		locks[show.name].release()
		log = "Releasing lock after retrieving show data cache (" + show.name + ")"
		print log
	showsString = appConfig.Implode(SEPARATOR, KEY_SHOWS)
	log = "Finished loading episodes from cache for show (" + show.name + ")"
	print log
	
	return episodes
	
def fetchShowEpisodes(show, page = 0):
	log = "call fetchShowEpisodes"
	print log
	log = "Fetch episode list for " + show.name + " for path: " + show.path
	print log
	
	episodes = []
	
	sg = mc.Http()	
	showpage = sg.Get(show.path + "&page=" + str(page))
	log = "fetch for page " + show.path + str(page)
	print log
	showpage = showpage.decode('iso-8859-1').encode('utf-8')

	pItem = re.compile('<div id="resultingvideos"(.+?)>(.+?)</div><!-- fin // resultingvideos -->', re.S)
	results = pItem.findall(showpage)

	for trash, item in results:	
		episode = Episode()
		episode.detailUrl = ASTRAL_BASE_URL + re.search('<div class="tablecell"><a href="(.+?)">', item).group(1)
		episode.thumbnailUrl = re.search('<img src="(.+?)" height="72" border="0" alt="" title="(.+?)"></a></div>', item).group(1)
		
		episode.title = re.search('<img src="(.+?)" height="72" border="0" alt="" title="(.+?)"></a></div>', item).group(2)

		removeSeasonInTitle = False
		
		seasonValues = re.search('Saison (\d+)', episode.title)
		if seasonValues is not None:
			episode.season = int(seasonValues.group(1))		
			removeSeasonInTitle = True
		episodeValues = re.search('Ep. (\d+)', episode.title)
		if  episodeValues is not None:
			episode.episode = int(episodeValues.group(1))
			removeSeasonInTitle = True
		
		if removeSeasonInTitle == True:
			p = re.compile('\(Saison (\d+) - Ep. (\d+)\)')
			episode.title = p.sub('', episode.title)			
			
		episode.viewCounter = re.search('Vue&nbsp;(\d+)&nbsp', item).group(1)
		episode.videoType = re.search('<img src="(.+?)" width="23" height="15" border="0" alt="" title="(.+?)" align="absmiddle"/>', item).group(2)
		episode.description = re.search('<div class="font11">(.+?)</div>', item).group(1)		
		episodes.append(episode)

	return episodes

def addVideoDataToItem(episodeItem):
	log = "call addVideoDataToItem"
	print log
	sg = mc.Http()

	videopageurl = episodeItem.GetProperty("detailUrl")
	log = "Interpreting video page at url " + videopageurl
	print log

	videopage = sg.Get(videopageurl)
	p = re.compile("videoId: (\d+),")
	pid = p.findall(videopage)
	
	videodef = getVideoInformation(pid[0])
	playurl = re.search('<highFlvUrl>(.+?)</highFlvUrl>', videodef).group(1)
	if playurl: 
		episodeItem.SetPath(playurl)
	else:
		mc.LogError("videopagedefinition: " + videodef)
	return episodeItem

def getVideoInformation(idVideo):
	definitionUrl =  ASTRAL_BASE_URL + "/webtele/_dyn/getVideoDataXml.jsp?videoId=" + idVideo
	return mc.Http().Get(definitionUrl)

def getVideoIdFromURL(url):
	videopage = mc.Http().Get(url)
	return re.compile("videoId: (\d+),").findall(videopage)[0]	

class Episode:
	title = ""
	videoType = ""
	viewCounter = 0
	thumbnailUrl = ""
	description = ""
	videoUrl = ""
	videoPath = ""
	detailUrl = ""
	doesDetailUrlIncludeData = "false"
	season = 0
	episode = 0
	
	
	def __init__(self):
		self.title = ""
		self.videoType = ""
		self.viewCounter = 0
		self.thumbnailUrl = ""
		self.description = ""
		self.videoUrl = ""
		self.videoPath = ""
		self.detailUrl = ""
		self.doesDetailUrlIncludeData = "false"
		self.season = 0
		self.episode = 0

class Show:
	name = ""
	path = ""
	backgroundUrl = ""
	
	def __init__(self):
		self.name = ""
		self.path = ""
		self.backgroundUrl = ""	
