# -*- coding: UTF-8 -*-
from __future__ import print_function

from AutoTimerComponent import preferredAutoTimerComponent
from Logger import doLog

from RecordTimer import AFTEREVENT
from Tools.XMLTools import stringToXML
from ServiceReference import ServiceReference

from enigma import eServiceReference

"""
Configuration Version.
To be bumped for any modification of the config format.
Incompatible changes (e.g. different parameter names) require a compatible
parser to be implemented as for example parseConfigOld which is capable of
parsing every config format before version 5.
Previously this variable was only bumped for incompatible changes, but as this
is the only reliable way to make remote tools aware of our capabilities without
much overhead (read: a special api just for this) we chose to change the meaning
of the version attribue.
"""
CURRENT_CONFIG_VERSION = "8"

def getValue(definitions, default):
	# Initialize Output
	ret = ""

	# How many definitions are present
	if isinstance(definitions, list):
		Len = len(definitions)
		ret = definitions[Len-1].text if Len else ""
	else:
		ret = definitions.text

	# Return stripped output or (if empty) default
	return ret.strip() or default

def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
	try:
		intVersion = int(version)
	except ValueError:
		doLog('[AutoTimer] Config version "%s" is not a valid integer, assuming old version' % version)
		intVersion = -1

	if intVersion < 5:
		parseConfigOld(configuration, list, uniqueTimerId)
		return

	if defaultTimer is not None:
		# Read in defaults for a new timer
		for defaults in configuration.findall("defaults"):
			parseEntry(defaults, defaultTimer, True)

	for timer in configuration.findall("timer"):
		uniqueTimerId += 1
		baseTimer = preferredAutoTimerComponent(
			uniqueTimerId,
			'',
			'',
			True
		)

		if parseEntry(timer, baseTimer):
			list.append(baseTimer)

def parseEntry(element, baseTimer, defaults = False):
	if not defaults:
		# Read out match
		baseTimer.match = element.get("match", "").encode("UTF-8")
		if not baseTimer.match:
			doLog('[AutoTimer] Erroneous config is missing attribute "match", skipping entry')
			return False

		# Read out name
		baseTimer.name = element.get("name", "").encode("UTF-8")
		if not baseTimer.name:
			doLog('[AutoTimer] Timer is missing attribute "name", defaulting to match')
			baseTimer.name = baseTimer.match

		# Read out enabled
		enabled = element.get("enabled", "yes")
		if enabled == "no":
			baseTimer.enabled = False
		elif enabled == "yes":
			baseTimer.enabled = True
		else:
			doLog('[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling')
			baseTimer.enabled = False

		# Read timeframe
		before = element.get("before")
		after = element.get("after")
		if before and after:
			baseTimer.timeframe = (int(after), int(before))

	# VPS-Plugin settings
	vps_enabled = element.get("vps_enabled", "no")
	vps_overwrite = element.get("vps_overwrite", "no")
	baseTimer.vps_enabled = True if vps_enabled == "yes" else False
	baseTimer.vps_overwrite = True if vps_overwrite == "yes" else False
	del vps_enabled, vps_overwrite

	# SeriesPlugin settings
	series_labeling = element.get("series_labeling", "no")
	series_save_filter = element.get("series_save_filter", "no")
	baseTimer.series_labeling = True if series_labeling == "yes" else False
	baseTimer.series_save_filter = True if series_save_filter == "yes" else False
	del series_labeling, series_save_filter

	# Read out search type/case
	baseTimer.searchType = element.get("searchType", baseTimer.searchType)
	baseTimer.searchCase = element.get("searchCase", baseTimer.searchCase)

	# Read out if we should change to alternative services
	baseTimer.overrideAlternatives = int(element.get("overrideAlternatives", baseTimer.overrideAlternatives))

	# Read out timespan
	start = element.get("from")
	end = element.get("to")
	if start and end:
		start = [int(x) for x in start.split(':')]
		end = [int(x) for x in end.split(':')]
		baseTimer.timespan = (start, end)

	# Read out max length
	maxduration = element.get("maxduration")
	if maxduration:
		baseTimer.maxduration = int(maxduration)*60

	# Read out recording path
	default = baseTimer.destination or ""
	baseTimer.destination = element.get("location", default).encode("UTF-8") or None

	# Read out offset
	offset = element.get("offset")
	if offset:
		offset = offset.split(",")
		if len(offset) == 1:
			before = after = int(offset[0] or 0) * 60
		else:
			before = int(offset[0] or 0) * 60
			after = int(offset[1] or 0) * 60
		baseTimer.offset = (before, after)

	# Read out counter
	baseTimer.matchCount = int(element.get("counter", 0))
	baseTimer.matchFormatString = element.get("counterFormat", "")
	if not defaults:
		baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
		baseTimer.matchLimit = element.get("lastActivation", "")
		baseTimer.lastBegin = int(element.get("lastBegin", 0))

	# Read out justplay
	baseTimer.justplay = int(element.get("justplay", 0))
	baseTimer.setEndtime = int(element.get("setEndtime", 1))

	# Read out avoidDuplicateDescription
	baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
	baseTimer.searchForDuplicateDescription = int(element.get("searchForDuplicateDescription", 2))

	# Read out allowed services
	l = element.findall("serviceref")
	if l:
		servicelist = []

		for service in l:
			value = service.text
			if value:
				myref = eServiceReference(str(value))
				if not (myref.flags & eServiceReference.isGroup):
					# strip all after last :
					pos = value.rfind(':')
					if pos != -1:
						if value[pos-1] == ':':
							pos -= 1
						value = value[:pos+1]

				servicelist.append(value)
		baseTimer.services = servicelist

	# Read out allowed bouquets
	l = element.findall("bouquet")
	if l:
		bouquets = []
		for bouquet in l:
			value = bouquet.text
			if value:
				bouquets.append(value)
		baseTimer.bouquets = bouquets

	# Read out afterevent
	l = element.findall("afterevent")
	if l:
		idx = {
			"none": AFTEREVENT.NONE,
			"deepstandby": AFTEREVENT.DEEPSTANDBY,
			"shutdown": AFTEREVENT.DEEPSTANDBY,
			"standby": AFTEREVENT.STANDBY,
			"auto": AFTEREVENT.AUTO
		}
		afterevents = []
		for afterevent in l:
			value = afterevent.text

			if value in idx:
				value = idx[value]
			else:
				doLog('[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition')
				continue

			start = afterevent.get("from")
			end = afterevent.get("to")
			if start and end:
				start = [int(x) for x in start.split(':')]
				end = [int(x) for x in end.split(':')]
				afterevents.append((value, (start, end)))
			else:
				afterevents.append((value, None))
		baseTimer.afterevent = afterevents

	# Read out exclude
	l = element.findall("exclude")
	idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
	if l:
		excludes = ([], [], [], [])
		for exclude in l:
			where = exclude.get("where")
			value = exclude.text
			if not (value and where):
				continue

			if where in idx:
				excludes[idx[where]].append(value.encode("UTF-8"))
		baseTimer.exclude = excludes

	# Read out includes (use same idx)
	l = element.findall("include")
	if l:
		includes = ([], [], [], [])
		for include in l:
			where = include.get("where")
			value = include.text
			if not (value and where):
				continue

			if where in idx:
				includes[idx[where]].append(value.encode("UTF-8"))
		baseTimer.include = includes

	# Read out recording tags
	l =  element.findall("tag")
	if l:
		tags = []
		for tag in l:
			value = tag.text
			if not value:
				continue

			tags.append(value.encode("UTF-8"))
		baseTimer.tags = tags

	return True

def parseConfigOld(configuration, list, uniqueTimerId = 0):
	doLog("[AutoTimer] Trying to parse old config")

	# Iterate Timers
	for timer in configuration.findall("timer"):
		# Increment uniqueTimerId
		uniqueTimerId += 1

		# Get name (V2+)
		name = timer.get("name")
		if name:
			name = name.encode("UTF-8")
		# Get name (= match) (V1)
		else:
			# Read out name
			name = getValue(timer.findall("name"), "").encode("UTF-8")

		if not name:
			doLog('[AutoTimer] Erroneous config is missing attribute "name", skipping entry')
			continue

		# Read out match (V3+)
		match = timer.get("match")
		if match:
			# Read out match
			match = match.encode("UTF-8")
			if not match:
				doLog('[AutoTimer] Erroneous config contains empty attribute "match", skipping entry')
				continue
		# V2-
		else:
			# Setting match to name
			match = name


		# See if Timer is ensabled (V2+)
		enabled = timer.get("enabled")
		if enabled:
			if enabled == "no":
				enabled = False
			elif enabled == "yes":
				enabled = True
			else:
				doLog('[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry')
				enabled = False
		# V1
		else:
			elements = timer.findall("enabled")
			if elements:
				if getValue(elements, "yes") == "no":
					enabled = False
				else:
					enabled = True
			else:
				enabled = True

		# Read out timespan (V4+; Falling back on missing definition should be OK)
		start = timer.get("from")
		end = timer.get("to")
		if start and end:
			start = [int(x) for x in start.split(':')]
			end = [int(x) for x in end.split(':')]
			timetuple = (start, end)
		# V3-
		else:
			elements = timer.findall("timespan")
			Len = len(elements)
			if Len:
				# Read out last definition
				start = elements[Len-1].get("from")
				end = elements[Len-1].get("to")
				if start and end:
					start = [int(x) for x in start.split(':')]
					end = [int(x) for x in end.split(':')]
					timetuple = (start, end)
				else:
					doLog('[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition')
					timetuple = None
			else:
				timetuple = None

		# Read out allowed services (V*)
		elements = timer.findall("serviceref")
		if elements:
			servicelist = []
			for service in elements:
				value = service.text
				if value:
					myref = eServiceReference(str(value))
					if not (myref.flags & eServiceReference.isGroup):
						# strip all after last :
						pos = value.rfind(':')
						if pos != -1:
							if value[pos-1] == ':':
								pos -= 1
							value = value[:pos+1]

					servicelist.append(value)
		else:
			servicelist = None

		# Read out allowed bouquets (V* though officially supported since V4)
		bouquets = []
		for bouquet in timer.findall("bouquet"):
			value = bouquet.text
			if value:
				bouquets.append(value)

		# Read out offset (V4+)
		offset = timer.get("offset")
		if offset:
			offset = offset.split(",")
			if len(offset) == 1:
				before = after = int(offset[0] or 0) * 60
			else:
				before = int(offset[0] or 0) * 60
				after = int(offset[1] or 0) * 60
			offset = (before, after)
		# V3-
		else:
			elements = timer.findall("offset")
			Len = len(elements)
			if Len:
				value = elements[Len-1].get("both")
				if value == '':
					before = int(elements[Len-1].get("before", 0)) * 60
					after = int(elements[Len-1].get("after", 0)) * 60
				else:
					before = after = int(value) * 60
				offset = (before, after)
			else:
				offset = None

		# Read out counter
		counter = int(timer.get("counter", '0'))
		counterLeft = int(timer.get("left", counter))
		counterLimit = timer.get("lastActivation")
		counterFormat = timer.get("counterFormat", "")
		lastBegin = int(timer.get("lastBegin", 0))

		# Read out justplay
		justplay = int(timer.get("justplay", '0'))
		setEndtime = int(timer.get("setEndtime", '1'))

		# Read out avoidDuplicateDescription
		avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
		searchForDuplicateDescription = int(timer.get("searchForDuplicateDescription", 3)) - 1
		if searchForDuplicateDescription < 0 or searchForDuplicateDescription > 2:
			searchForDuplicateDescription = 2

		# Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
		idx = {
			"none": AFTEREVENT.NONE,
			"deepstandby": AFTEREVENT.DEEPSTANDBY,
			"shutdown": AFTEREVENT.DEEPSTANDBY,
			"standby": AFTEREVENT.STANDBY,
			"auto": AFTEREVENT.AUTO
		}
		afterevent = []
		for element in timer.findall("afterevent"):
			value = element.text

			if value in idx:
				value = idx[value]
			else:
				doLog('[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition')
				continue

			start = element.get("from")
			end = element.get("to")
			if start and end:
				start = [int(x) for x in start.split(':')]
				end = [int(x) for x in end.split(':')]
				afterevent.append((value, (start, end)))
			else:
				afterevent.append((value, None))

		# Read out exclude (V*)
		idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
		excludes = ([], [], [], [])
		for exclude in timer.findall("exclude"):
			where = exclude.get("where")
			value = exclude.text
			if not (value and where):
				continue

			if where in idx:
				excludes[idx[where]].append(value.encode("UTF-8"))

		# Read out includes (use same idx) (V4+ feature, should not harm V3-)
		includes = ([], [], [], [])
		for include in timer.findall("include"):
			where = include.get("where")
			value = include.text
			if not (value and where):
				continue

			if where in idx:
				includes[idx[where]].append(value.encode("UTF-8"))

		# Read out max length (V4+)
		maxlen = timer.get("maxduration")
		if maxlen:
			maxlen = int(maxlen)*60
		# V3-
		else:
			elements = timer.findall("maxduration")
			if elements:
				maxlen = getValue(elements, None)
				if maxlen is not None:
					maxlen = int(maxlen)*60
			else:
				maxlen = None

		# Read out recording path
		destination = timer.get("destination", "").encode("UTF-8") or None

		# Read out recording tags
		tags = []
		for tag in timer.findall("tag"):
			value = tag.text
			if not value:
				continue

			tags.append(value.encode("UTF-8"))

		# Finally append timer
		list.append(preferredAutoTimerComponent(
				uniqueTimerId,
				name,
				match,
				enabled,
				timespan = timetuple,
				services = servicelist,
				offset = offset,
				afterevent = afterevent,
				exclude = excludes,
				include = includes,
				maxduration = maxlen,
				destination = destination,
				matchCount = counter,
				matchLeft = counterLeft,
				matchLimit = counterLimit,
				matchFormatString = counterFormat,
				lastBegin = lastBegin,
				justplay = justplay,
				setEndtime = setEndtime,
				avoidDuplicateDescription = avoidDuplicateDescription,
				searchForDuplicateDescription = searchForDuplicateDescription,
				bouquets = bouquets,
				tags = tags
		))

def buildConfig(defaultTimer, timers, webif = False):
	# Generate List in RAM
	list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
	append = list.append
	extend = list.extend

	# This gets deleted afterwards if we do not have set any defaults
	append(' <defaults')
	if webif:
		extend((' id="', str(defaultTimer.getId()),'"'))

	# Timespan
	if defaultTimer.hasTimespan():
		extend((' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'))

	# Duration
	if defaultTimer.hasDuration():
		extend((' maxduration="', str(defaultTimer.getDuration()), '"'))

	# Destination
	if defaultTimer.hasDestination():
		extend((' location="', stringToXML(defaultTimer.destination), '"'))

	# Offset
	if defaultTimer.hasOffset():
		if defaultTimer.isOffsetEqual():
			extend((' offset="', str(defaultTimer.getOffsetBegin()), '"'))
		else:
			extend((' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'))

	# Counter
	if defaultTimer.hasCounter():
		extend((' counter="', str(defaultTimer.getCounter()), '"'))
		if defaultTimer.hasCounterFormatString():
			extend((' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'))

	# Duplicate Description
	if defaultTimer.getAvoidDuplicateDescription():
		extend((' avoidDuplicateDescription="', str(defaultTimer.getAvoidDuplicateDescription()), '"'))

		if defaultTimer.getAvoidDuplicateDescription() > 0:
			if defaultTimer.searchForDuplicateDescription != 2:
				extend((' searchForDuplicateDescription="', str(defaultTimer.searchForDuplicateDescription), '"'))

	# Only display justplay if true
	if defaultTimer.justplay:
		extend((' justplay="', str(defaultTimer.getJustplay()), '"'))
		if not defaultTimer.setEndtime:
			append(' setEndtime="0"')

	# SearchType
	if defaultTimer.searchType != "partial":
		extend((' searchType="', str(defaultTimer.searchType), '"'))

	# Only display searchCase if sensitive
	if defaultTimer.searchCase == "sensitive":
		extend((' searchCase="', str(defaultTimer.searchCase), '"'))

	# Only add vps related entries if true
	if defaultTimer.vps_enabled:
		append(' vps_enabled="yes"')
		if defaultTimer.vps_overwrite:
			append(' vps_overwrite="yes"')

	# Only add seriesplugin related entry if true
	if defaultTimer.series_labeling:
		append(' series_labeling="yes"')
		if defaultTimer.series_save_filter:
			append(' series_save_filter="yes"')

	# Close still opened defaults tag
	append('>\n')

	if webif:
		# Services + Bouquets
		for serviceref in defaultTimer.services + defaultTimer.bouquets:
			ref = ServiceReference(str(serviceref))
			extend((
				'  <e2service>\n',
				'   <e2servicereference>', str(serviceref), '</e2servicereference>\n',
				'   <e2servicename>', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '</e2servicename>\n',
				'  </e2service>\n',
			))
	else:
		# Services
		for serviceref in defaultTimer.services:
			ref = ServiceReference(str(serviceref))
			extend(('  <serviceref>', serviceref, '</serviceref>',
						' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
			))

		# Bouquets
		for bouquet in defaultTimer.bouquets:
			ref = ServiceReference(str(bouquet))
			extend(('  <bouquet>', str(bouquet), '</bouquet>',
						' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
			))

	# AfterEvent
	if defaultTimer.hasAfterEvent():
		idx = {
			AFTEREVENT.NONE: "none",
			AFTEREVENT.STANDBY: "standby",
			AFTEREVENT.DEEPSTANDBY: "shutdown",
			AFTEREVENT.AUTO: "auto"
		}
		for afterevent in defaultTimer.afterevent:
			action, timespan = afterevent
			append('  <afterevent')
			if timespan[0] is not None:
				append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
			extend(('>', idx[action], '</afterevent>\n'))

	# Excludes
	for title in defaultTimer.getExcludedTitle():
		extend(('  <exclude where="title">', stringToXML(title), '</exclude>\n'))
	for short in defaultTimer.getExcludedShort():
		extend(('  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
	for desc in defaultTimer.getExcludedDescription():
		extend(('  <exclude where="description">', stringToXML(desc), '</exclude>\n'))
	for day in defaultTimer.getExcludedDays():
		extend(('  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))

	# Includes
	for title in defaultTimer.getIncludedTitle():
		extend(('  <include where="title">', stringToXML(title), '</include>\n'))
	for short in defaultTimer.getIncludedShort():
		extend(('  <include where="shortdescription">', stringToXML(short), '</include>\n'))
	for desc in defaultTimer.getIncludedDescription():
		extend(('  <include where="description">', stringToXML(desc), '</include>\n'))
	for day in defaultTimer.getIncludedDays():
		extend(('  <include where="dayofweek">', stringToXML(day), '</include>\n'))

	# Tags
	if webif and defaultTimer.tags:
		extend(('  <e2tags>', stringToXML(' '.join(defaultTimer.tags)), '</e2tags>\n'))
	else:
		for tag in defaultTimer.tags:
			extend(('  <tag>', stringToXML(tag), '</tag>\n'))

	# Keep the list clean
	if len(list) == 5:
		list.pop() # >
		list.pop() # <defaults
	else:
		append(' </defaults>\n\n')

	# Iterate timers
	for timer in timers:
		# Common attributes (match, enabled)
		extend((' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'))
		if webif:
			extend((' id="', str(timer.getId()),'"'))

		# Timespan
		if timer.hasTimespan():
			extend((' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'))

		# Timeframe
		if timer.hasTimeframe():
			extend((' after="', str(timer.getTimeframeBegin()), '" before="', str(timer.getTimeframeEnd()), '"'))

		# Duration
		if timer.hasDuration():
			extend((' maxduration="', str(timer.getDuration()), '"'))

		# Destination
		if timer.hasDestination():
			extend((' location="', stringToXML(timer.destination), '"'))

		# Offset
		if timer.hasOffset():
			if timer.isOffsetEqual():
				extend((' offset="', str(timer.getOffsetBegin()), '"'))
			else:
				extend((' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'))

		# Counter
		if timer.hasCounter():
			extend((' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'))
			if timer.hasCounterFormatString():
				extend((' lastActivation="', str(timer.getCounterLimit()), '"'))
				extend((' counterFormat="', str(timer.getCounterFormatString()), '"'))

		# Duplicate Description
		if timer.getAvoidDuplicateDescription():
			extend((' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'))
			if timer.searchForDuplicateDescription != 2:
				extend((' searchForDuplicateDescription="', str(timer.searchForDuplicateDescription), '"'))

		# Only display justplay if true
		if timer.justplay:
			extend((' justplay="', str(timer.getJustplay()), '"'))
			if not timer.setEndtime:
				append(' setEndtime="0"')

		# SearchType
		if timer.searchType != "partial":
			extend((' searchType="', str(timer.searchType), '"'))

		# Only display searchCase if sensitive
		if timer.searchCase == "sensitive":
			extend((' searchCase="', str(timer.searchCase), '"'))

		# Only display overrideAlternatives if true
		if timer.overrideAlternatives:
			extend((' overrideAlternatives="', str(timer.getOverrideAlternatives()), '"'))

		# Only add vps related entries if true
		if timer.vps_enabled:
			append(' vps_enabled="yes"')
			if timer.vps_overwrite:
				append(' vps_overwrite="yes"')

		# Only add seriesplugin related entry if true
		if timer.series_labeling:
			append(' series_labeling="yes"')
			if timer.series_save_filter:
				append(' series_save_filter="yes"')

		# Close still opened timer tag
		append('>\n')

		if webif:
			# Services + Bouquets
			for serviceref in timer.services + timer.bouquets:
				ref = ServiceReference(str(serviceref))
				extend((
					'  <e2service>\n',
					'   <e2servicereference>', str(serviceref), '</e2servicereference>\n',
					'   <e2servicename>', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '</e2servicename>\n',
					'  </e2service>\n',
				))
		else:
			# Services
			for serviceref in timer.services:
				ref = ServiceReference(str(serviceref))
				extend(('  <serviceref>', serviceref, '</serviceref>',
							' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
				))

			# Bouquets
			for bouquet in timer.bouquets:
				ref = ServiceReference(str(bouquet))
				extend(('  <bouquet>', str(bouquet), '</bouquet>',
							' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
				))

		# AfterEvent
		if timer.hasAfterEvent():
			idx = {
				AFTEREVENT.NONE: "none",
				AFTEREVENT.STANDBY: "standby",
				AFTEREVENT.DEEPSTANDBY: "shutdown",
				AFTEREVENT.AUTO: "auto"
			}
			for afterevent in timer.afterevent:
				action, timespan = afterevent
				append('  <afterevent')
				if timespan[0] is not None:
					append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
				extend(('>', idx[action], '</afterevent>\n'))

		# Excludes
		for title in timer.getExcludedTitle():
			extend(('  <exclude where="title">', stringToXML(title), '</exclude>\n'))
		for short in timer.getExcludedShort():
			extend(('  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
		for desc in timer.getExcludedDescription():
			extend(('  <exclude where="description">', stringToXML(desc), '</exclude>\n'))
		for day in timer.getExcludedDays():
			extend(('  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))

		# Includes
		for title in timer.getIncludedTitle():
			extend(('  <include where="title">', stringToXML(title), '</include>\n'))
		for short in timer.getIncludedShort():
			extend(('  <include where="shortdescription">', stringToXML(short), '</include>\n'))
		for desc in timer.getIncludedDescription():
			extend(('  <include where="description">', stringToXML(desc), '</include>\n'))
		for day in timer.getIncludedDays():
			extend(('  <include where="dayofweek">', stringToXML(day), '</include>\n'))

		# Tags
		if webif and timer.tags:
			extend(('  <e2tags>', stringToXML(' '.join(timer.tags)), '</e2tags>\n'))
		else:
			for tag in timer.tags:
				extend(('  <tag>', stringToXML(tag), '</tag>\n'))

		# End of Timer
		append(' </timer>\n\n')

	# End of Configuration
	append('</autotimer>\n')

	return list

