# -*- coding: utf-8 -*-
"""
	luc_kodi Add-on
"""

from resources.lib.modules import control, log_utils
from resources.lib.modules.catalog_updater import CatalogService
from sys import version_info, platform as sys_platform
from threading import Thread
window = control.homeWindow
pythonVersion = '{}.{}.{}'.format(version_info[0], version_info[1], version_info[2])
plugin = 'plugin://plugin.video.luc_kodi/'
LOGINFO = log_utils.LOGINFO


class CheckSettingsFile:
	def run(self):
		try:
			control.log('[ plugin.video.luc_kodi ]  CheckSettingsFile Service Starting...', LOGINFO)
			window.clearProperty('luc_kodi_settings')
			profile_dir = control.dataPath
			if not control.existsPath(profile_dir):
				success = control.makeDirs(profile_dir)
				if success: control.log('%s : created successfully' % profile_dir, LOGINFO)
			else: control.log('%s : already exists' % profile_dir, LOGINFO)
			settings_xml = control.joinPath(profile_dir, 'settings.xml')
			if not control.existsPath(settings_xml):
				control.setSetting('trakt.message2', '')
				control.log('%s : created successfully' % settings_xml, LOGINFO)
			else: control.log('%s : already exists' % settings_xml, LOGINFO)
			return control.log('[ plugin.video.luc_kodi ]  Finished CheckSettingsFile Service', LOGINFO)
		except:
			log_utils.error()

class SettingsMonitor(control.monitor_class):
	def __init__ (self):
		control.monitor_class.__init__(self)
		control.refresh_playAction()
		control.refresh_libPath()
		window.setProperty('luc_kodi.debug.reversed', control.setting('debug.reversed'))
		control.log('[ plugin.video.luc_kodi ]  Settings Monitor Service Starting...', LOGINFO)

	def onSettingsChanged(self): # Kodi callback when the addon settings are changed
		window.clearProperty('luc_kodi_settings')
		control.sleep(50)
		refreshed = control.make_settings_dict()
		control.refresh_playAction()
		control.refresh_libPath()
		control.refresh_debugReversed()

class ReuseLanguageInvokerCheck:
	def run(self):
		control.log('[ plugin.video.luc_kodi ]  ReuseLanguageInvokerCheck Service Starting...', LOGINFO)
		try:
			import xml.etree.ElementTree as ET
			from resources.lib.modules.language_invoker import gen_file_hash
			addon_xml = control.joinPath(control.addonPath('plugin.video.luc_kodi'), 'addon.xml')
			tree = ET.parse(addon_xml)
			root = tree.getroot()
			current_addon_setting = control.addon('plugin.video.luc_kodi').getSetting('reuse.languageinvoker')
			try: current_xml_setting = [str(i.text) for i in root.iter('reuselanguageinvoker')][0]
			except: return control.log('[ plugin.video.luc_kodi ]  ReuseLanguageInvokerCheck failed to get settings.xml value', LOGINFO)
			if current_addon_setting == '':
				current_addon_setting = 'true'
				control.setSetting('reuse.languageinvoker', current_addon_setting)
			if current_xml_setting == current_addon_setting:
				return control.log('[ plugin.video.luc_kodi ]  ReuseLanguageInvokerCheck Service Finished', LOGINFO)
			control.okDialog(message='%s\n%s' % (control.lang(33023), control.lang(33020)))
			for item in root.iter('reuselanguageinvoker'):
				item.text = current_addon_setting
				hash_start = gen_file_hash(addon_xml)
				tree.write(addon_xml)
				hash_end = gen_file_hash(addon_xml)
				control.log('[ plugin.video.luc_kodi ]  ReuseLanguageInvokerCheck Service Finished', LOGINFO)
				if hash_start != hash_end:
					current_profile = control.infoLabel('system.profilename')
					control.execute('LoadProfile(%s)' % current_profile)
				else: control.okDialog(title='default', message=33022)
			return
		except:
			log_utils.error()

class AddonCheckUpdate:
	def run(self):
		control.log('[ plugin.video.luc_kodi ]  Addon checking available updates', LOGINFO)
		try:
			import re
			import requests
			repo_url = 'https://raw.githubusercontent.com/apoyotech/luc_repo/main/addons.xml'
			if not repo_url:
				return control.log('[ plugin.video.luc_kodi ]  ReuseLanguageInvokerCheck: repo_url empty, skipping remote version check', LOGINFO)
			repo_xml = requests.get(repo_url, timeout=15)
			if not repo_xml.status_code == 200:
				return control.log('[ plugin.video.luc_kodi ]  Could not connect to remote repo XML: status code = %s' % repo_xml.status_code, LOGINFO)
			repo_version = re.findall(r'<addon id=\"plugin.video.luc_kodi\".+version=\"(\d*.\d*.\d*)\"', repo_xml.text)[0]
			local_version = control.getluc_kodiVersion()[:5] # 5 char max so pre-releases do try to compare more chars than github version
			def check_version_numbers(current, new): # Compares version numbers and return True if github version is newer
				current = current.split('.')
				new = new.split('.')
				step = 0
				for i in current:
					if int(new[step]) > int(i): return True
					if int(i) > int(new[step]): return False
					if int(i) == int(new[step]):
						step += 1
						continue
				return False
			if check_version_numbers(local_version, repo_version):
				while control.condVisibility('Library.IsScanningVideo'):
					control.sleep(10000)
				control.log('[ plugin.video.luc_kodi ]  A newer version is available. Installed Version: v%s, Repo Version: v%s' % (local_version, repo_version), LOGINFO)
				control.notification(message=control.lang(35523) % repo_version)
			return control.log('[ plugin.video.luc_kodi ]  Addon update check complete', LOGINFO)
		except:
			log_utils.error()

class VersionIsUpdateCheck:
	def run(self):
		try:
			from resources.lib.database import cache
			isUpdate = False
			oldVersion, isUpdate = cache.update_cache_version()
			if isUpdate:
				window.setProperty('luc_kodi.updated', 'true')
				curVersion = control.getluc_kodiVersion()
				clearDB_version = '6.5.6' # set to desired version to force any db clearing needed
				do_cacheClear = (int(oldVersion.replace('.', '')) < int(clearDB_version.replace('.', '')) <= int(curVersion.replace('.', '')))
				if do_cacheClear:
					clr_fanarttv = False
					cache.clrCache_version_update(clr_providers=False, clr_metacache=True, clr_cache=True, clr_search=False, clr_bookmarks=False)
					from resources.lib.database import traktsync
					clr_traktSync = {'bookmarks': False, 'hiddenProgress': False, 'liked_lists': False, 'movies_collection': False, 'movies_watchlist': False, 'popular_lists': False,
											'public_lists': False, 'shows_collection': False, 'shows_watchlist': False, 'trending_lists': False, 'user_lists': False, 'watched': False}
					cleared = traktsync.delete_tables(clr_traktSync)
					if cleared:
						control.notification(message='Forced traktsync clear for version update complete.')
						control.log('[ plugin.video.luc_kodi ]  Forced traktsync clear for version update complete.', LOGINFO)
					if clr_fanarttv:
						from resources.lib.database import fanarttv_cache
						cleared = fanarttv_cache.cache_clear()
						control.notification(message='Forced fanarttv.db clear for version update complete.')
						control.log('[ plugin.video.luc_kodi ]  Forced fanarttv.db clear for version update complete.', LOGINFO)
				control.setSetting('trakt.message2', '') # force a settings write for any added settings that may have been added in new version
				control.log('[ plugin.video.luc_kodi ]  Forced new User Data settings.xml saved', LOGINFO)
				control.log('[ plugin.video.luc_kodi ]  Plugin updated to v%s' % curVersion, LOGINFO)
		except:
			log_utils.error()

class SyncTraktCollection:
	def run(self):
		control.log('[ plugin.video.luc_kodi ]  Trakt Collection Sync Starting...', LOGINFO)
		control.execute('RunPlugin(%s?action=library_tvshowsToLibrarySilent&url=traktcollection)' % plugin)
		control.execute('RunPlugin(%s?action=library_moviesToLibrarySilent&url=traktcollection)' % plugin)
		control.log('[ plugin.video.luc_kodi ]  Trakt Collection Sync Complete', LOGINFO)

class LibraryService:
	def run(self):
		control.log('[ plugin.video.luc_kodi ]  Library Update Service Starting (Update check every 6hrs)...', LOGINFO)
		from resources.lib.modules import library
		library.lib_tools().service() # method contains control.monitor().waitForAbort() while loop every 6hrs

class SyncTraktService:
	def run(self):
		service_syncInterval = control.setting('trakt.service.syncInterval') or '15'
		control.log('[ plugin.video.luc_kodi ]  Trakt Sync Service Starting (sync check every %s minutes)...' % service_syncInterval, LOGINFO)
		from resources.lib.modules import trakt
		trakt.trakt_service_sync() # method contains "control.monitor().waitForAbort()" while loop every "service_syncInterval" minutes

try:
	kodiVersion = control.getKodiVersion(full=True)
	addonVersion = control.addon('plugin.video.luc_kodi').getAddonInfo('version')
#	repoVersion = control.addon('repository.luc_repo').getAddonInfo('version')
#	fsVersion = control.addon('script.module.jacksparrowscrapers').getAddonInfo('version')
	log_utils.log('########   CURRENT luc_kodi VERSIONS REPORT   ########', level=LOGINFO)
#	log_utils.log('##   Platform: %s' % str(sys_platform), level=LOGINFO)
	log_utils.log('##   Kodi Version: %s' % str(kodiVersion), level=LOGINFO)
	log_utils.log('##   python Version: %s' % pythonVersion, level=LOGINFO)
	log_utils.log('##   plugin.video.luc_kodi Version: %s' % str(addonVersion), level=LOGINFO)
#	log_utils.log('##   repository.luc_kodi Version: %s' % str(repoVersion), level=LOGINFO)
#	log_utils.log('##   script.module.jacksparrowscrapers Version: %s' % str(fsVersion), level=LOGINFO)
	log_utils.log('######   luc_kodi SERVICE ENTERING KEEP ALIVE   #####', level=LOGINFO)
except:
	log_utils.log('## ERROR GETTING luc_kodi VERSION - Missing Repo or failed Install ', level=LOGINFO)

class DisplayResolutionDetector:
	"""
	Dynamic display resolution monitor that runs as a background service.

	- Checks resolution every 60 seconds throughout the entire Kodi session.
	- First run ever: shows a welcome notification and auto-configures hosts.quality.
	- Any time resolution changes (new TV, resolution profile switch, etc.):
	    * Updates hosts.quality instantly and notifies the user.
	    * Respects manual overrides: if the user changed hosts.quality by hand,
	      the detector tracks the screen change but does NOT overwrite their choice.
	- Safe: uses the same control.monitor.abortRequested() pattern as all other services.

	Settings used (all hidden from UI):
	    device.detected.resolution  → last known resolution label (4K/1080p/720p/SD)
	    device.auto.quality         → the hosts.quality value we last set automatically
	"""

	# Poll interval in seconds — frequent enough to react to mid-session TV changes
	POLL_INTERVAL = 60

	# hosts.quality enum index (matches settings.xml values="4K|1080p|720p|480p(SD)")
	QUALITY_MAP = {'4K': '0', '1080p': '1', '720p': '2', 'SD': '3', 'QHD': '0'}

	# Human-readable label for notifications (QHD shows as 4K-capable)
	NOTIFY_LABEL = {'4K': '4K', '1080p': '1080p', '720p': '720p', 'SD': 'SD', 'QHD': '4K'}

	def _get_screen_resolution(self):
		try:
			w = int(xbmc.getInfoLabel('System.ScreenWidth') or 0)
			h = int(xbmc.getInfoLabel('System.ScreenHeight') or 0)
			return w, h
		except Exception:
			return 0, 0

	def _resolution_label(self, width, height):
		"""
		Map pixel dimensions to a resolution tier label.

		Always uses max(width, height) so orientation (portrait/landscape)
		on phones and tablets does not affect the result.

		Tiers:
		  4K    max ≥ 3840  → hosts.quality '0'  (4K sources)
		  QHD   max ≥ 2560  → hosts.quality '0'  (4K sources — hardware decodes natively)
		            Covers: QHD 2560x1440, WQHD, QHD+ 2800x1752, Galaxy S/Tab, iPad Pro, etc.
		  1080p max ≥ 1920  → hosts.quality '1'  (1080p sources)
		  720p  max ≥ 1280  → hosts.quality '2'  (720p sources)
		  SD    anything below → hosts.quality '3'

		Device examples:
		  TV 4K             3840x2160 → max=3840 → 4K
		  PC QHD monitor    2560x1440 → max=2560 → QHD → 4K sources
		  Galaxy Tab S8+    2800x1752 → max=2800 → QHD → 4K sources
		  Phone FHD+ port.  1080x2400 → max=2400 → QHD → 4K sources
		  TV 1080p          1920x1080 → max=1920 → 1080p
		  Android box 4K   3840x2160 → max=3840 → 4K
		  Fire TV Stick HD  1920x1080 → max=1920 → 1080p
		  Raspberry Pi 4K  3840x2160 → max=3840 → 4K
		"""
		longest = max(width, height)
		if longest >= 3840: return '4K'
		if longest >= 2560: return 'QHD'
		if longest >= 1920: return '1080p'
		if longest >= 1280: return '720p'
		return 'SD'

	def _show_welcome(self, line1='Welcome to luc_kodi', line2='', duration=7):
		"""Launch the colored XML welcome notification in the current thread."""
		try:
			from resources.lib.windows.display_welcome import DisplayWelcomeXML
			addon_path = control.addonPath('plugin.video.luc_kodi')
			win = DisplayWelcomeXML('display_welcome.xml', addon_path, 'Default',
				line1=line1, line2=line2, icon=control.addonIcon(), duration=duration)
			win.show_and_close(duration=duration)
			del win
		except Exception:
			# Fallback to standard Kodi notification if XML window fails
			xbmc.executebuiltin('Notification(%s,%s,%s)' % (line1, line2, duration * 1000))
			log_utils.error()

	def _apply_resolution(self, detected_label, last_detected, is_first_run, width=0, height=0):
		"""
		Core logic: compare detected vs stored resolution and act accordingly.
		Returns the new last_detected value to store for the next cycle.
		"""
		detected_quality_value = self.QUALITY_MAP[detected_label]

		if is_first_run:
			# Very first time the plugin runs — auto-configure and welcome the user
			control.setSetting('hosts.quality', detected_quality_value)
			control.setSetting('device.auto.quality', detected_quality_value)
			if detected_label == 'QHD':
				msg = control.lang(40208) % (width, height)
			else:
				msg_map = {'4K': 40201, '1080p': 40202, '720p': 40203, 'SD': 40204}
				msg = control.lang(msg_map[detected_label])
			self._show_welcome(line2=msg, duration=7)
			control.log('[ plugin.video.luc_kodi ]  DisplayResolutionDetector: FIRST RUN — screen=%dx%d tier=%s → hosts.quality=%s' % (width, height, detected_label, detected_quality_value), LOGINFO)

		elif detected_label != last_detected:
			# Resolution changed during this session or since last boot
			current_quality = control.setting('hosts.quality') or '0'
			auto_quality    = control.setting('device.auto.quality') or ''

			if current_quality == auto_quality:
				# User has not manually overridden quality — safe to auto-update
				control.setSetting('hosts.quality', detected_quality_value)
				control.setSetting('device.auto.quality', detected_quality_value)
				if detected_label == 'QHD':
					msg = control.lang(40208) % (width, height)
				else:
					display_label = self.NOTIFY_LABEL[detected_label]
					msg = control.lang(40207) % display_label
				self._show_welcome(line2=msg, duration=5)
				control.log('[ plugin.video.luc_kodi ]  DisplayResolutionDetector: changed %s → %s (%dx%d), hosts.quality updated to %s' % (last_detected, detected_label, width, height, detected_quality_value), LOGINFO)
			else:
				# User has a custom quality setting — respect it, only log
				control.log('[ plugin.video.luc_kodi ]  DisplayResolutionDetector: screen changed %s → %s but user has custom hosts.quality=%s — not overwriting' % (last_detected, detected_label, current_quality), LOGINFO)

		return detected_label

	def run(self):
		control.log('[ plugin.video.luc_kodi ]  DisplayResolutionDetector Service Starting (polling every %ss)...' % self.POLL_INTERVAL, LOGINFO)
		try:
			# Give Kodi a moment to fully initialise the display before first read
			if control.monitor.waitForAbort(3): return

			# Log platform info once at startup for diagnostics
			try:
				platform_info = xbmc.getInfoLabel('System.OSVersionInfo') or sys_platform
				control.log('[ plugin.video.luc_kodi ]  DisplayResolutionDetector: platform=%s' % platform_info, LOGINFO)
			except Exception:
				pass

			last_detected = control.setting('device.detected.resolution') or ''
			is_first_run  = (last_detected == '')

			while not control.monitor.abortRequested():
				try:
					w, h = self._get_screen_resolution()
					if w > 0 and h > 0:
						detected_label = self._resolution_label(w, h)
						last_detected  = self._apply_resolution(detected_label, last_detected, is_first_run, w, h)
						# Persist the current resolution so it survives restarts
						control.setSetting('device.detected.resolution', last_detected)
						is_first_run = False  # only True on the very first cycle ever
					else:
						control.log('[ plugin.video.luc_kodi ]  DisplayResolutionDetector: could not read screen resolution this cycle, will retry.', LOGINFO)
				except Exception:
					log_utils.error()

				# Sleep in short increments so we react quickly to abortRequested
				elapsed = 0
				while elapsed < self.POLL_INTERVAL and not control.monitor.abortRequested():
					control.monitor.waitForAbort(5)
					elapsed += 5

		except Exception:
			log_utils.error()
		control.log('[ plugin.video.luc_kodi ]  DisplayResolutionDetector Service Stopped.', LOGINFO)


class CheckUndesirablesDatabase:
	def run(self):
		from resources.lib.jacksparrow.undesirables import Undesirables, add_new_default_keywords
		try:
			control.log('[ plugin.video.luc_kodi ]  CheckUndesirablesDatabase Service Starting', LOGINFO)
			old_database = Undesirables().check_database()
			if old_database: add_new_default_keywords()
			control.log('[ plugin.video.luc_kodi ]  CheckUndesirablesDatabase Service Finished', LOGINFO)
		except:
			log_utils.error()

def getTraktCredentialsInfo():
	username = control.setting('trakt.username').strip()
	token = control.setting('trakt.token')
	refresh = control.setting('trakt.refresh')
	if (username == '' or token == '' or refresh == ''): return False
	return True

def main():
	while not control.monitor.abortRequested():
		control.log('[ plugin.video.luc_kodi ]  Service Started', LOGINFO)
		schedTrakt = None
		libraryService = None
		CheckSettingsFile().run()
		CheckUndesirablesDatabase().run()
		ReuseLanguageInvokerCheck().run()
		displayDetectorService = Thread(target=DisplayResolutionDetector().run)
		displayDetectorService.start()
		if control.setting('library.service.update') == 'true':
			libraryService = Thread(target=LibraryService().run)
			libraryService.start()
#		if control.setting('general.checkAddonUpdates') == 'true':
#			AddonCheckUpdate().run()
		VersionIsUpdateCheck().run()

		syncTraktService = Thread(target=SyncTraktService().run) # run service in case user auth's trakt later, sync will loop and do nothing without valid auth'd account
		syncTraktService.start()

		catalogService = Thread(target=CatalogService().run)
		catalogService.start()


		if getTraktCredentialsInfo():
			if control.setting('autoTraktOnStart') == 'true':
				SyncTraktCollection().run()
			if int(control.setting('schedTraktTime')) > 0:
				import threading
				log_utils.log('#################### STARTING TRAKT SCHEDULING ################', level=LOGINFO)
				log_utils.log('#################### SCHEDULED TIME FRAME '+ control.setting('schedTraktTime')  + ' HOURS ###############', level=LOGINFO)
				timeout = 3600 * int(control.setting('schedTraktTime'))
				schedTrakt = threading.Timer(timeout, SyncTraktCollection().run) # this only runs once at the designated interval time to wait...not repeating
				schedTrakt.start()
		break
	SettingsMonitor().waitForAbort()
	control.log('[ plugin.video.luc_kodi ]  Settings Monitor Service Stopping...', LOGINFO)
	del catalogService # prob does not kill a running thread
	control.log('[ plugin.video.luc_kodi ]  Catalog Service Stopping...', LOGINFO)
	del syncTraktService # prob does not kill a running thread
	control.log('[ plugin.video.luc_kodi ]  Trakt Sync Service Stopping...', LOGINFO)
	if libraryService:
		del libraryService # prob does not kill a running thread
		control.log('[ plugin.video.luc_kodi ]  Library Update Service Stopping...', LOGINFO)
	if schedTrakt:
		schedTrakt.cancel()
	control.log('[ plugin.video.luc_kodi ]  Service Stopped', LOGINFO)

main()