# -*- coding: utf-8 -*-
# plugin.video.alkoflix
# resources/lib/windows/people.py

import json
from windows import BaseDialog, location
from indexers.tmdb_api import tmdb_people_info, tmdb_people_full_info, tmdb_image_base
from indexers.imdb_api import imdb_videos
from menus.images import Images
from modules import dialogs
from modules.utils import calculate_age
from modules.kodi_utils import media_path, notification, local_string as ls, build_url, execute_builtin, close_all_dialog
from modules.info_windows import extras_window_xml, person_window_xml, log_info_window_event, log_info_window_error, start_info_window_task
from modules.settings import extras_enable_scrollbars, extras_exclude_non_acting, get_resolution

fanart = media_path('transparent.png')
backup_thumbnail = media_path('box_office.png')
backup_cast_thumbnail = media_path('people.png')
roles_exclude = ('himself', 'herself', 'self', 'narrator', 'voice (voice)')
favourites_id = 20
more_movies_button_id, more_tvshows_button_id, more_director_button_id = 60, 61, 62
button_ids = (10, 50, favourites_id, more_movies_button_id, more_tvshows_button_id, more_director_button_id)
genres_exclude = (10763, 10764, 10767)
gender_dict = {0: '', 1: ls(32844), 2: ls(32843), 3: ls(32466)}
more_from_movies_id, more_from_tvshows_id, imdb_videos_id, more_from_director_id = 2050, 2051, 2052, 2053
PERSON_CREDIT_CONTROL_IDS = (more_from_movies_id, more_from_tvshows_id, more_from_director_id)
PERSON_CREDIT_ROUTES = {
	'movie': ('build_movie_list', 'tmdb_person_movies', 'Films'),
	'tvshow': ('build_tvshow_list', 'tmdb_person_tvshows', 'Séries'),
	'director': ('build_movie_list', 'tmdb_person_director', 'Réalisations')
}
PERSON_CREDIT_BUTTONS = {
	more_movies_button_id: 'movie',
	more_tvshows_button_id: 'tvshow',
	more_director_button_id: 'director'
}

class People(BaseDialog):
	def __init__(self, *args, **kwargs):
		BaseDialog.__init__(self, args)
		self.control_id = None
		self.selected = None
		self.kwargs = kwargs
		self.after_close = None
		self.return_to_extras = kwargs.get('return_to_extras')
		self.return_media_type = kwargs.get('return_media_type')
		self.set_starting_constants()
		self.make_person_data()
		self.set_properties()

	def onInit(self):
		self.start_background_sections()

	def start_background_sections(self):
		# Les crédits Personnes proviennent déjà de combined_credits dans make_person_data.
		# On évite donc trois threads séparés pour du traitement local et on ne lance
		# les sections que lorsqu'il y a réellement quelque chose à préparer.
		if self.imdb_id:
			start_info_window_task('Personnes', 'imdb_videos', self.make_imdb_videos)
		if self.movie_data or self.tvshow_data or self.director_data:
			start_info_window_task('Personnes', 'credits', self.make_credit_sections)

	def make_credit_sections(self):
		for credit_type in PERSON_CREDIT_ROUTES:
			if self.get_credit_data(credit_type):
				self.make_more_from(credit_type)

	def run(self):
		self.doModal()
		after_close = self.after_close
		selected = self.selected
		return_to_extras = self.return_to_extras
		return_media_type = self.return_media_type
		self.after_close = None
		self.clearProperties()
		if callable(after_close):
			return after_close()
		if selected:
			# Depuis une recherche lancée par un skin, la fenêtre de recherche du skin
			# peut encore être considérée comme modale au moment où Kodi reçoit
			# ActivateWindow(Videos,...). On laisse donc un court délai après la
			# fermeture réelle de la fiche avant de lancer la navigation.
			self.sleep(300)
			return execute_builtin(selected)
		if return_to_extras:
			try:
				log_info_window_event('Personnes', 'return to parent info window media=%s' % return_media_type)
				from windows.extras import Extras
				return Extras(extras_window_xml(return_media_type), location, **return_to_extras).run()
			except Exception as exc:
				log_info_window_error('Personnes', 'return to parent info window', exc)

	def onClick(self, controlID):
		self.control_id = None
		if controlID in button_ids:
			if controlID == 10:
				params = {'mode': 'people_image_results', 'actor_name': self.person_name, 'actor_id': self.person_id,
						'actor_image': self.person_image, 'page_no': 1, 'rolling_count': 0}
				return self.open_person_images_with_return(params, 'images')
			elif controlID == 50:
				self.open_window(('windows.extras', 'ShowTextMedia'), 'textviewer_media.xml', text=self.person_biography, poster=self.person_image, title=self.person_name, subtitle='Biographie')
			elif controlID == favourites_id:
				params = {'mode': 'favourites_choice', 'media_type': 'person', 'fav_type': 'person', 'tmdb_id': self.person_id, 'title': self.person_name, 'icon': self.person_image}
				return dialogs.favourites_choice(params)
			elif controlID in PERSON_CREDIT_BUTTONS:
				return self.open_person_credits(PERSON_CREDIT_BUTTONS[controlID])
		else: self.control_id = controlID

	def onAction(self, action):
		if action in self.closing_actions: self.close()
		if not self.control_id: return
		if action in self.selection_actions:
			try:
				chosen_listitem = self.get_listitem(self.control_id)
				chosen_var = chosen_listitem.getProperty(self.item_action_dict[self.control_id])
			except Exception as exc:
				log_info_window_error('Personnes', 'onAction control=%s' % self.control_id, exc)
				return
			if self.control_id in PERSON_CREDIT_CONTROL_IDS:
				media_type = 'tvshow' if self.control_id == more_from_tvshows_id else 'movie'
				params = {'tmdb_id': chosen_var, 'media_type': media_type, 'is_widget': 'false'}
				return dialogs.extras_menu(params)
			elif self.control_id == 2052:
				params = json.loads(chosen_var)
				chosen = dialogs.imdb_videos_choice(params['videos'], params['thumb'])
				if not chosen: return
				return self.open_window(('windows.videoplayer', 'VideoPlayer'), 'videoplayer.xml', video=chosen)

	def open_person_credits(self, credit_type):
		try: mode, action, label = PERSON_CREDIT_ROUTES[credit_type]
		except Exception as exc:
			log_info_window_error('Personnes', 'open_person_credits(%s)' % credit_type, exc)
			return
		if not self.has_person_credit_data(credit_type):
			log_info_window_event('Personnes', 'open %s credits skipped: no credits for tmdb_id=%s' % (credit_type, self.person_id))
			return notification(32760, 1500)
		prepared_count = None
		try:
			from menus.people import set_person_credit_ids_cache
			credit_data = self.get_credit_data(credit_type)
			prepared_count = len(set_person_credit_ids_cache(self.person_id, credit_type, credit_data, self.exclude_non_acting) or [])
		except Exception as exc:
			log_info_window_error('Personnes', 'cache %s credits before open' % credit_type, exc)
		url_params = {
			'mode': mode,
			'action': action,
			'actor_id': self.person_id,
			'name': '%s - %s' % (label, self.person_name),
			'search_name': self.person_name,
			'person_credit_prepared': 'true',
			'person_credit_type': credit_type
		}
		if prepared_count is not None: url_params['person_credit_count'] = str(prepared_count)
		log_info_window_event('Personnes', 'open %s credits for tmdb_id=%s' % (credit_type, self.person_id))
		self.selected = 'ActivateWindow(Videos,%s,return)' % build_url(url_params)
		# Si la fiche Personne a été ouverte depuis la recherche globale d'un skin,
		# la fenêtre de recherche du skin peut rester modale derrière notre fiche.
		# Kodi refuse alors d'ouvrir Videos. On ferme donc les dialogues actifs avant
		# de naviguer vers Films/Séries/Réalisations.
		try: close_all_dialog()
		except: pass
		self.close()

	def get_credit_data(self, credit_type):
		try:
			return {
				'movie': self.movie_data,
				'tvshow': self.tvshow_data,
				'director': self.director_data
			}.get(credit_type, [])
		except Exception as exc:
			log_info_window_error('Personnes', 'get_credit_data(%s)' % credit_type, exc)
			return []

	def has_person_credit_data(self, credit_type):
		return bool(self.get_credit_data(credit_type))

	def open_person_images_with_return(self, params, source):
		# Comme les infos supplémentaires des fiches Films/Séries : on évite
		# d'empiler galerie + diaporama au-dessus d'une fiche Personne encore modale.
		# Le retour est plus fiable : fermeture de la fiche, galerie, puis réouverture.
		return_kwargs = {
			'query': None,
			'actor_id': self.person_id,
			'actor_name': self.person_name,
			'actor_image': self.person_image,
			'return_to_extras': self.return_to_extras,
			'return_media_type': self.return_media_type
		}
		def callback():
			try:
				log_info_window_event('Personnes', 'open image browser source=%s tmdb_id=%s' % (source, self.person_id))
				Images().run(params)
			except Exception as exc:
				log_info_window_error('Personnes', 'image browser source=%s' % source, exc)
			try:
				log_info_window_event('Personnes', 'return to person info window source=%s tmdb_id=%s' % (source, self.person_id))
				self.open_window(('windows.people', 'People'), person_window_xml(), **return_kwargs)
			except Exception as exc:
				log_info_window_error('Personnes', 'return to person info window source=%s' % source, exc)
		self.after_close = callback
		self.close()

	def make_person_data(self):
		if self.kwargs['query']:
			try: self.person_id = tmdb_people_info(self.kwargs['query'])[0]['id']
			except Exception as exc:
				log_info_window_error('Personnes', 'tmdb_people_info(%s)' % self.kwargs['query'], exc)
				notification(32760)
		else: self.person_id = self.kwargs['actor_id']
		person_info = tmdb_people_full_info(self.person_id)
		if person_info.get('biography') in ('', None): person_info = tmdb_people_full_info(self.person_id, 'en')
		self.person_name = person_info['name']
		image_path = person_info['profile_path']
		if image_path: self.person_image = tmdb_image_base % ('h632', image_path)
		else: self.person_image = backup_cast_thumbnail
		try: self.person_gender = gender_dict[person_info.get('gender')]
		except: self.person_gender = ''
		place_of_birth = person_info.get('place_of_birth')
		if place_of_birth: self.person_place_of_birth = place_of_birth
		else: self.person_place_of_birth = ''
		biography = person_info.get('biography')
		if biography: self.person_biography = biography
		else: self.person_biography = ls(32760)
		birthday = person_info.get('birthday')
		if birthday: self.person_birthday = birthday
		else: self.person_birthday = ''
		deathday = person_info.get('deathday')
		if deathday: self.person_deathday = deathday
		else: self.person_deathday = ''
		if self.person_deathday: self.person_age = calculate_age(self.person_birthday, '%Y-%m-%d', self.person_deathday)
		elif self.person_birthday: self.person_age = calculate_age(self.person_birthday, '%Y-%m-%d')
		else:self.person_age = ''
		self.imdb_id = person_info['imdb_id']
		more_from_data = person_info['combined_credits']
		acting_data = more_from_data['cast']
		directing_data = more_from_data['crew']
		self.movie_data = [i for i in acting_data if i['media_type'] == 'movie']
		self.tvshow_data = [i for i in acting_data if i['media_type'] == 'tv']
		self.director_data = [i for i in directing_data if i['job'].lower() == 'director']

	def make_more_from(self, media_type):
		try:
			if media_type == 'movie':
				list_type = media_type
				_id = more_from_movies_id
				data = self.movie_data
				if self.exclude_non_acting:
					try: data = [i for i in data if not 99 in i['genre_ids'] and not i['character'].lower() in roles_exclude]
					except: pass
			elif media_type == 'tvshow':
				list_type = media_type
				_id = more_from_tvshows_id
				data = self.tvshow_data
				if self.exclude_non_acting:
					try: data = [i for i in data if not any(x in genres_exclude for x in i['genre_ids']) and not i['character'].lower() in roles_exclude]
					except: pass
			else:#director
				list_type = 'movie'
				_id = more_from_director_id
				data = self.director_data
			item_list = list(self.make_tmdb_listitems(data, list_type))
			self.setProperty('alkoskins.person.more_from_%s.number' % media_type, '(x%02d)' % len(item_list))
			self.item_action_dict[_id] = 'alkoskins.person.tmdb_id'
			control = self.getControl(_id)
			control.addItems(item_list)
		except Exception as exc:
			log_info_window_error('Personnes', 'make_more_from(%s)' % media_type, exc)

	def make_imdb_videos(self):
		def builder():
			for count, item in enumerate(data, 1):
				try:
					listitem = self.make_listitem()
					listitem.setProperty('alkoskins.person.name', '%01d. %s' % (count, item['title']))
					listitem.setProperty('alkoskins.person.thumbnail', item['poster'])
					listitem.setProperty('alkoskins.person.params', json.dumps({'videos': json.dumps(item['videos']), 'thumb': item['poster']}))
					yield listitem
				except: pass
		try:
			if not self.imdb_id:
				self.setProperty('alkoskins.person.imdb_videos.number', '(x00)')
				return
			try: data = imdb_videos(self.imdb_id)
			except ValueError:
				# IMDb retourne parfois une réponse vide/non JSON pour les vidéos de personnes.
				# Ce n'est pas une erreur bloquante de la fiche : on garde simplement la section vide.
				log_info_window_event('Personnes', 'no imdb videos available for imdb_id=%s' % self.imdb_id)
				self.setProperty('alkoskins.person.imdb_videos.number', '(x00)')
				return
			if not data:
				self.setProperty('alkoskins.person.imdb_videos.number', '(x00)')
				return
			item_list = list(builder())
			self.setProperty('alkoskins.person.imdb_videos.number', '(x%02d)' % len(item_list))
			self.item_action_dict[imdb_videos_id] = 'alkoskins.person.params'
			control = self.getControl(imdb_videos_id)
			control.addItems(item_list)
		except Exception as exc:
			log_info_window_error('Personnes', 'make_imdb_videos', exc)

	def make_tmdb_listitems(self, data, media_type):
		used_ids = []
		append = used_ids.append
		name_key = 'title' if media_type == 'movie' else 'name'
		release_key = 'release_date' if media_type == 'movie' else 'first_air_date'
		for item in sorted(data, key=lambda x: x.get(release_key) or '', reverse=True):
			try:
				tmdb_id = item['id']
				if tmdb_id in used_ids: continue
				listitem = self.make_listitem()
				poster_path = item['poster_path']
				if not poster_path: thumbnail = backup_thumbnail
				else: thumbnail = tmdb_image_base % (self.poster_resolution, poster_path)
				year = item.get(release_key)
				if year in (None, ''): year = 'N/A'
				else:
					try: year = year.split('-')[0]
					except: pass
				listitem.setProperty('alkoskins.person.name', item[name_key])
				listitem.setProperty('alkoskins.person.release_date', year)
				listitem.setProperty('alkoskins.person.vote_average', '%.1f' % item['vote_average'])
				listitem.setProperty('alkoskins.person.thumbnail', thumbnail)
				listitem.setProperty('alkoskins.person.tmdb_id', str(tmdb_id))
				append(tmdb_id)
				yield listitem
			except: pass

	def set_starting_constants(self):
		self.item_action_dict = {}
		self.enable_scrollbars = extras_enable_scrollbars()
		self.exclude_non_acting = extras_exclude_non_acting()
		self.poster_resolution = get_resolution()['poster']
		self.plugin_runner = 'RunPlugin(%s)'

	def _format_gender_age(self):
		parts = []
		try:
			if self.person_gender: parts.append(self.person_gender)
		except: pass
		try:
			if self.person_age: parts.append('%s ans' % self.person_age)
		except: pass
		return '  •  '.join(parts)

	def set_properties(self):
		self.setProperty('alkoskins.person.name', self.person_name)
		self.setProperty('alkoskins.person.id', str(self.person_id))
		self.setProperty('alkoskins.person.image', self.person_image)
		self.setProperty('alkoskins.person.fanart', fanart)
		self.setProperty('alkoskins.person.gender', self.person_gender)
		self.setProperty('alkoskins.person.gender_age', self._format_gender_age())
		self.setProperty('alkoskins.person.place_of_birth', self.person_place_of_birth)
		self.setProperty('alkoskins.person.biography', self.person_biography)
		self.setProperty('alkoskins.person.birthday', self.person_birthday)
		self.setProperty('alkoskins.person.deathday', self.person_deathday)
		self.setProperty('alkoskins.person.age', str(self.person_age))
		self.setProperty('alkoskins.person.enable_scrollbars', self.enable_scrollbars)

