from __future__ import division
from __future__ import print_function
import NavigationInstance
from time import localtime, mktime, gmtime
from enigma import iServiceInformation, eServiceCenter, eServiceReference
from timer import TimerEntry
from six.moves import range

class TimerSanityCheck:
	def __init__(self, timerlist, newtimer=None):
		print("sanitycheck")
		self.localtimediff = 25*3600 - mktime(gmtime(25*3600))
		self.timerlist = timerlist
		self.newtimer = newtimer
		self.simultimer = []
		self.rep_eventlist = []
		self.nrep_eventlist = []
		self.bflag = -1
		self.eflag = 1

	def check(self, ext_timer=1, filter_check = 0):
		print("check")
		if ext_timer != 1:
			self.newtimer = ext_timer
		if self.newtimer is None:
			self.simultimer = []
		else:
			self.simultimer = [ self.newtimer ]
		return self.checkTimerlist(filter_check = filter_check)

	def getSimulTimerList(self):
		return self.simultimer

	def doubleCheck(self):
		if self.newtimer is not None and self.newtimer.service_ref.ref.valid():
			self.simultimer = [ self.newtimer ]
			for timer in self.timerlist:
				if timer.begin == self.newtimer.begin:
					fl1 = timer.service_ref.ref.flags & eServiceReference.isGroup
					fl2 = self.newtimer.service_ref.ref.flags & eServiceReference.isGroup
					if fl1 != fl2:
						return False
					if fl1: #is group
						return timer.service_ref.ref.getPath() == self.newtimer.service_ref.ref.getPath()
					getUnsignedDataRef1 = timer.service_ref.ref.getUnsignedData
					getUnsignedDataRef2 = self.newtimer.service_ref.ref.getUnsignedData
					for x in (1, 2, 3, 4):
						if getUnsignedDataRef1(x) != getUnsignedDataRef2(x):
							break;
					else: # same service and same start time
						# we would allow two timers with same start time on same service when one is repeated and one is non repeated
						# and the duration is not equal
						if timer.repeated == self.newtimer.repeated or timer.end == self.newtimer.end:
							return True
		return False

	def checkTimerlist(self, ext_timer=1, filter_check = 0):
		#with special service for external plugins
		# Entries in eventlist
		# timeindex
		# BeginEndFlag 1 for begin, -1 for end
		# index -1 for the new Timer, 0..n index of the existing timers
		# count of running timers

		serviceHandler = eServiceCenter.getInstance()
		print("checkTimerlist")
# create a list with all start and end times
# split it into recurring and singleshot timers

##################################################################################
# process the new timer
		self.rep_eventlist = []
		self.nrep_eventlist = []
		if ext_timer != 1:
			self.newtimer = ext_timer
		if (self.newtimer is not None) and (not self.newtimer.disabled):
			if not self.newtimer.service_ref.ref.valid():
				return False
			rflags = self.newtimer.repeated
			rflags = ((rflags & 0x7F)>> 3)|((rflags & 0x07)<<4)
			if rflags:
				begin = self.newtimer.begin % 86400 # map to first day
				if (self.localtimediff > 0) and ((begin + self.localtimediff) > 86400):
					rflags = ((rflags >> 1)& 0x3F)|((rflags << 6)& 0x40)
				elif (self.localtimediff < 0) and (begin < self.localtimediff):
					rflags = ((rflags << 1)& 0x7E)|((rflags >> 6)& 0x01)
				while rflags: # then arrange on the week
					if rflags & 1:
						self.rep_eventlist.append((begin, -1))
					begin += 86400
					rflags >>= 1
			else:
				self.nrep_eventlist.extend([(self.newtimer.begin,self.bflag,-1),(self.newtimer.end,self.eflag,-1)])

##################################################################################
# now process existing timers
		idx = 0
		for timer in self.timerlist:
			if (timer != self.newtimer) and (not timer.disabled):
				if timer.service_ref.getPath() != '': # ignore non tuner recordings (streaming)
					continue
				if timer.repeated:
					rflags = timer.repeated
					rflags = ((rflags & 0x7F)>> 3)|((rflags & 0x07)<<4)
					begin = timer.begin % 86400 # map all to first day
					if (self.localtimediff > 0) and ((begin + self.localtimediff) > 86400):
						rflags = ((rflags >> 1)& 0x3F)|((rflags << 6)& 0x40)
					elif (self.localtimediff < 0) and (begin < self.localtimediff):
						rflags = ((rflags << 1)& 0x7E)|((rflags >> 6)& 0x01)
					while rflags:
						if rflags & 1:
							self.rep_eventlist.append((begin, idx))
						begin += 86400
						rflags >>= 1
				elif timer.state < TimerEntry.StateEnded:
					self.nrep_eventlist.extend([(timer.begin,self.bflag,idx),(timer.end,self.eflag,idx)])
			idx += 1

################################################################################
# journalize timer repeations
		if self.rep_eventlist:
			if self.nrep_eventlist:
				interval_begin = min(self.nrep_eventlist)[0]
				interval_end = max(self.nrep_eventlist)[0]
				offset_0 = interval_begin - (interval_begin % 604800)
				weeks = (interval_end - offset_0) // 604800
				if ((interval_end - offset_0) % 604800):
					weeks += 1
				for cnt in range(int(weeks)):
					for event in self.rep_eventlist:
						if event[1] == -1: # -1 is the identifier of the changed timer
							event_begin = self.newtimer.begin
							event_end = self.newtimer.end
						else:
							event_begin = self.timerlist[event[1]].begin
							event_end = self.timerlist[event[1]].end
						new_event_begin = event[0] + offset_0 + (cnt * 604800)
						# summertime correction
						new_lth = localtime(new_event_begin).tm_hour
						new_event_begin += 3600 * (localtime(event_begin).tm_hour - new_lth)
						new_event_end = new_event_begin + (event_end - event_begin)
						if event[1] == -1:
							if new_event_begin >= self.newtimer.begin: # is the soap already running?
								self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]),(new_event_end, self.eflag, event[1])])
						else:
							if new_event_begin >= self.timerlist[event[1]].begin: # is the soap already running?
								self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]),(new_event_end, self.eflag, event[1])])
			else:
				offset_0 = 345600 # the Epoch begins on Thursday
				for cnt in (0, 1): # test two weeks to take care of Sunday-Monday transitions
					for event in self.rep_eventlist:
						if event[1] == -1: # -1 is the identifier of the changed timer
							event_begin = self.newtimer.begin
							event_end = self.newtimer.end
						else:
							event_begin = self.timerlist[event[1]].begin
							event_end = self.timerlist[event[1]].end
						new_event_begin = event[0] + offset_0 + (cnt * 604800)
						new_event_end = new_event_begin + (event_end - event_begin)
						self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]),(new_event_end, self.eflag, event[1])])

################################################################################
# order list chronological
		self.nrep_eventlist.sort()
		# Dr.Best --> Event available
		if filter_check:
			proceed = False
#			print "------> sanitycheck filter_check!"
			ref = self.newtimer.service_ref.ref
			refList = []
			if ref.flags & eServiceReference.isGroup: # service group ?
				serviceList = serviceHandler.list(ref) # get all alternative services
				if serviceList:
					for ref in serviceList.getContent("R"): # iterate over all group service references
						refList.append(ref.toString())
			else:
				refList.append(self.newtimer.service_ref.ref.toString())
			my_nrep_temp_eventlist = []
			bla = {}
			for event in self.nrep_eventlist:
				if event[2] == -1: # new timer
					my_nrep_temp_eventlist.append(event)
				else:
					if event[1] == self.bflag:
						bla[event[2]] = event[0]
					else:
						check = 0
						ref = self.timerlist[event[2]].service_ref.ref
						if ref.flags & eServiceReference.isGroup: # service group ?
							serviceList = serviceHandler.list(ref) # get all alternative services
							if serviceList:
								for ref in serviceList.getContent("R"): # iterate over all group service references
									if ref.toString() not in refList:
										check = 1
										break
						else:
							if ref.toString() not in refList:
								check = 1
						if check:
							begin = bla[event[2]]
							end = event[0]
							if (max(self.newtimer.begin, begin) < min(self.newtimer.end, end)):
								proceed = True
								my_nrep_temp_eventlist.extend([(begin,-1,event[2]),(end,1,event[2])])
			
			if proceed:
				self.nrep_eventlist =  my_nrep_temp_eventlist
				self.nrep_eventlist.sort()
			else:
				return True

##################################################################################
# detect overlapping timers and overlapping times
		fakeRecList = []
		ConflictTimer = None
		ConflictTunerType = None
		newTimerTunerType = None
		cnt = 0
		idx = 0
		overlaplist = []
		for event in self.nrep_eventlist:
			cnt += event[1]
			if event[2] == -1: # new timer
				timer = self.newtimer
			else:
				timer = self.timerlist[event[2]]
			if event[1] == self.bflag:
				tunerType = [ ]
				fakeRecService = NavigationInstance.instance.recordService(timer.service_ref, True)
				if fakeRecService:
					fakeRecResult = fakeRecService.start(True)
				else:
					fakeRecResult = -1
				if not fakeRecResult: # tune okay
					fakeRecService.frontendInfo() and tunerType.append(fakeRecService.frontendInfo().getFrontendData().get("tuner_type"))
				else: # tune failed.. so we must go another way to get service type (iDVBFrontend.feSatellite, iDVBFrontend.feTerrestrial, iDVBFrontend.feCable)

					def getServiceType(ref): # helper function to get a service type of a service reference
						serviceInfo = serviceHandler.info(ref)
						serviceInfo = serviceInfo and serviceInfo.getInfoObject(ref, iServiceInformation.sTransponderData)
						return -1 if serviceInfo is None else serviceInfo.get("tuner_type", -1)

					ref = timer.service_ref.ref
					if ref.flags & eServiceReference.isGroup: # service group ?
						serviceList = serviceHandler.list(ref) # get all alternative services
						if serviceList:
							for ref in serviceList.getContent("R"): # iterate over all group service references
								type = getServiceType(ref)
								if not type in tunerType: # just add single time
									tunerType.append(type) 
					else:
						tunerType.append(getServiceType(ref))

				if event[2] == -1: # new timer
					newTimerTunerType = tunerType
				overlaplist.append((fakeRecResult, timer, tunerType))
				fakeRecList.append((timer, fakeRecService))
				if fakeRecResult:
					if ConflictTimer is None: # just take care of the first conflict
						ConflictTimer = timer
						ConflictTunerType = tunerType
			elif event[1] == self.eflag:
				for fakeRec in fakeRecList:
					if timer == fakeRec[0] and fakeRec[1]:
						NavigationInstance.instance.stopRecordService(fakeRec[1])
						fakeRecList.remove(fakeRec)
				fakeRec = None
				for entry in overlaplist:
					if entry[1] == timer:
						overlaplist.remove(entry)
			else:
				print("Bug: unknown flag!")
			self.nrep_eventlist[idx] = (event[0],event[1],event[2],cnt,overlaplist[:]) # insert a duplicate into current overlaplist
			fakeRecService = None
			idx += 1

		if ConflictTimer is None: # no conflict found :)
			return True
		# Dr.Best --> Event available
		elif filter_check and ConflictTimer:
			return False

##################################################################################
# we have detected a conflict, now we must figure out the involved timers

		if self.newtimer is not None: # new timer?
			if self.newtimer is not ConflictTimer: # the new timer is not the conflicting timer?
				for event in self.nrep_eventlist:
					if len(event[4]) > 1: # entry in overlaplist of this event??
						kt = False
						nt = False
						for entry in event[4]:
							if entry[1] is ConflictTimer:
								kt = True
							if entry[1] is self.newtimer:
								nt = True
						if nt and kt:
							ConflictTimer = self.newtimer
							ConflictTunerType = newTimerTunerType
							break

		self.simultimer = [ ConflictTimer ]
		for event in self.nrep_eventlist:
			if len(event[4]) > 1: # entry in overlaplist of this event??
				for entry in event[4]:
					if entry[1] is ConflictTimer:
						break
				else:
					continue
				for entry in event[4]:
					if not entry[1] in self.simultimer:
						for x in entry[2]:
							if x in ConflictTunerType:
								self.simultimer.append(entry[1])
								break

		if len(self.simultimer) < 2:
			print("Bug: unknown Conflict!")

		return False # conflict detected!
	
	
	
	
