# -*- coding: utf-8 -*-
import sys
from datetime import datetime
from .. import xml_unescape, LANG
from .EPGConfig import enumerateXML, get_histtimings, td2str
from .EPGImport import ISO639_associations
from ..log import logger
from ..modules.six.moves import filter, map
from collections import defaultdict
if sys.version_info[0:2] < (3, 7):
	from collections import OrderedDict as _dict
else:
	_dict = dict
from functools import partial

LANG = ISO639_associations.get(LANG, 'eng')
EPOCH_ORD = datetime(1970, 1, 1)
PY3 = sys.version_info[0:2] > (2, 7)
histseconds, timespan = get_histtimings()

def get_event_data(elem, tags=['title', 'sub-title', 'desc']):
	"""The priority given to event data with the language of the Enigma2 image interface,
	if an event with this language is not found in the "programme" node, then "eng" is taken,
	if it is not there, then the first one found
	"""
	res = defaultdict(lambda: _dict.fromkeys(tags, ''))
	try:
		for node in filter(lambda x: x.tag in tags, elem.iter()):
			lang = ISO639_associations.get(node.get('lang'), 'eng')
			tags_dict = res[lang]
			tags_dict[node.tag] = xml_unescape(node.text)
			res[lang] = tags_dict

		if LANG in res:
			res = res[LANG]
		elif 'eng' in res:
			res = res['eng']
		elif res:
			res = res.popitem()[1]
		else:
			raise Exception("[%s] values not found" % ', '.join(tags))
	except Exception as err:
		logger.debug("XMLTVConverter %s: %s" % (sys._getframe().f_code.co_name, err))
		res = res['error']
	return list(res.values()) if PY3 else res.values()


def get_timestamp_utc(xmltv_date):
	"""Calculate Unix timestamp from XMLTV time format
	'20180920140000 +0300'
	"""
	try:
		dt = datetime(*map(int, (xmltv_date[:4], xmltv_date[4:6], xmltv_date[6:8], xmltv_date[8:10], xmltv_date[10:12])))
		dt = (dt - EPOCH_ORD).total_seconds()
		utc_offset = int(xmltv_date[15:] or 0)
		if utc_offset:
			dt -= (3600 * utc_offset) / 100
	except Exception as err:
		logger.error("XMLTVConverter %s: %s" % (sys._getframe().f_code.co_name, err))
		dt = 0
	finally:
		return int(dt)


def getDataTuple(now_timestamp_utc, elem):
	"""here we get an event tuple of tuples or None

	ETSI EN 300 468 v V1.11.1
	0. start time (long)
	1. duration (long)
	2. event title (string)
	3. short description (string) -> 6.2.37 Short event descriptor
	4. extended description (string) -> 6.2.15 Extended event descriptor
	5. event type (byte - uint8_t) or list or tuple of event types -> 6.2.9 Content descriptor
	6. optional event ID (int - uint16_t), if not supplied, it will default to 0, which implies an
	   an auto-generated ID based on the start time.
	7. optional list or tuple of tuples
	   (country[string 3 bytes], parental_rating [byte - u_char]) -> 6.2.28 Parental rating descriptor
	"""
	try:
		l = [get_timestamp_utc(elem.get(x, '').strip()) for x in ('start', 'stop')]
		if all(l) and (l[0] <= timespan) and (now_timestamp_utc <= l[1] > l[0]):
			l[1] -= l[0]
			if l[1] > 86400:
				raise Exception("an unacceptably large 'duration' value")
			l.extend(get_event_data(elem))
			if not l[2]:
				raise Exception("the empty value of 'title' is not allowed")
			while len(l[2].encode('utf-8')) > 220:
				l[2] = l[2][:-1]
			l.append(0)
			return (tuple(l),)

	except Exception as err:
		logger.debug("XMLTVConverter %s: %s" % (sys._getframe().f_code.co_name, err))
	return None


def pairwise(gen):
	c = next(gen, None)
	for n in gen:
		yield c, n
		c = n.__copy__()
	yield c, None


class XMLTVConverter(object):

	@classmethod
	def enumFile(cls, fileobj, source):
		"""Here we yield a tuple consisting of a list of services and a tuple of event tuples or None.
		Return a None object to give up time to the reactor.
		"""
		if not source.channels.items:
			logger.warn("['%s'] Nothing to process. The channels table is empty" % source.description)
			return
		now_timestamp_utc = (datetime.utcnow() - EPOCH_ORD).total_seconds()
		if histseconds:
			now_timestamp_utc -= histseconds
			logger.info("Keep outdated EPG events to -%s" % td2str(histseconds))
		logger.info("['%s'] Processing events information ..." % source.description)
		elem2tuple = partial(getDataTuple, int(now_timestamp_utc))
		events, curr_id = (), None
		for elem, next_elem in pairwise(enumerateXML(fileobj, 'programme')):
			if curr_id is None:
				curr_id = elem.get('channel', '')
				services = list(source.channels.items[xml_unescape(curr_id.lower())])
				if not services:
					curr_id = None
					continue
			elem = elem2tuple(elem)
			next_id = next_elem.get('channel', '') if next_elem is not None else ''
			if elem:
				events += elem
				if next_id == curr_id:
					continue
				yield services, events
			elif elem is None and next_id == curr_id:
				continue
			elif events:
				yield services, events
			else:
				yield None
			events, curr_id = (), None
