from time import time, localtime, sleep

from WebUtils.Funcs import requestURI
from AdminSecurity import AdminSecurity

def strtime(t):
	return '%4d-%02d-%02d %02d:%02d:%02d' % localtime(t)[:6]


class ThreadControl(AdminSecurity):

	def title(self):
		return 'ThreadControl'

	def writeContent(self):
		app = self.application()
		server = app.server()
		request = self.request()
		field = request.field
		myRequestID = request.requestID()
		wr = self.writeln

		try:
			threadHandler = server._threadHandler
			abortRequest = server._canAbortRequest and server.abortRequest
		except AttributeError:
			threadHandler = {}
			abortRequest = None

		maxRequestTime = server.setting('MaxRequestTime', 0) or 0

		try:
			max_duration = int(field('duration'))
		except (KeyError, ValueError):
			max_duration = int(maxRequestTime/2) or 60

		wr('<h2>Current thread status</h2>',
			'<p>Automatic cancelation of long-running requests is controlled by'
			' the <tt>AppServer.config</tt> setting <tt>MaxRequestTime</tt>.</p>')

		if maxRequestTime:
			if abortRequest:
				wr('<p>Currently, this is set to <b>%d</b> seconds.</p>'
					% maxRequestTime)
			else:
				wr('<p>Currently, this is set to %d seconds, but inactive.</p>'
					% maxRequestTime)
		else:
			wr('<p>Currently, this setting is disabled.</p>')

		wr('<form action="ThreadControl" method="post">', '<p>')
		if abortRequest:
			wr('<input name="cancel_all" type="submit"'
				' value="Cancel all requests below">')
			wr('<input name="cancel_selected" type="submit"'
				' value="Cancel all selected requests">')
		else:
			wr('<p>You need Python 2.3 with <code>ctypes</code> or a'
				' newer Python version to enable cancelation of threads.</p>')
		wr('<input name="refresh_view" type="submit"'
			' value="Refresh view">', '</p>')
		if abortRequest:
			wr('<p><input name="cancel_long" type="submit"'
				' value="Cancel all long-running requests">'
				' (longer than <input type="text" name="duration" value="%d"'
				' size="6" maxlength="12" style="text-align: right">'
				' seconds)</p>' % max_duration)
		wr('<p>You can <a href="Sleep" target="_blank">create a long-running'
			' request</a> in a separate browser window for testing.</p>'
			'<p>(Your web browser may get stuck if it is waiting for more'
			' than one of these.)</p>')

		if field('cancel_selected', None):
			killIDs = field('selectedIDs', None) or []
		elif field('cancel_all', None):
			killIDs = field('allIDs', '').split(',')
		elif field('cancel_long', None):
			killIDs = field('longIDs', None) or []
		else:
			killIDs = []
		if type(killIDs) != type([]):
			killIDs = [killIDs]
		try:
			killIDs = map(int, killIDs)
		except ValueError:
			killIDs = []
		killedIDs = []
		errorIDs = []
		activeIDs = []
		for h in threadHandler.values():
			try:
				activeIDs.append(h._requestID)
			except AttributeError:
				continue
		for requestID in killIDs:
			if (not requestID or requestID == myRequestID
					or requestID not in activeIDs):
				continue
			if abortRequest:
				try:
					killed = abortRequest(requestID) == 1
				except Exception:
					killed = 0
			else:
				killed = 0
			if killed:
				killedIDs.append(requestID)
			else:
				errorIDs.append(requestID)
		if killedIDs:
			msg = (len(killedIDs) > 1 and
				'The following requests have been canceled: %s'
					% ', '.join(map(str, killedIDs)) or
				'Request %d has been canceled.' % killedIDs[0])
			wr('<p style="color:green">%s</p>' % msg)
			tries = 100
			while tries:
				pendingIDs = []
				for h in threadHandler.values():
					try:
						requestID = h._requestID
						if requestID in killedIDs:
							pendingIDs.append(requestID)
					except AttributeError:
						continue
				if pendingIDs:
					sleep(0.125)
					tries -= 1
				else:
					pendingIDs = []
					tries = 0
			if pendingIDs:
				msg = (len(pendingIDs) > 1 and
					'The following of these are still pending: %s'
						% ', '.join(map(str, pendingIDs)) or
					'The request is still pending.')
				wr('<p>%s</p><p>You can'
					' <a href="ThreadControl">refresh the view<a>'
					' to verify cancelation.</p>' % msg)
		if errorIDs:
			msg = (len(errorIDs) > 1 and
				'The following requests could not be canceled: %s'
					% ', '.join(map(str, errorIDs)) or
				'Request %d could not be canceled.' % errorIDs[0])
			wr('<p style="color:red">%s</p>' % msg)

		curTime = time()
		activeThreads = []
		try:
			threadCount = server._threadCount
		except AttributeError:
			threadCount = 0
		for t, h in threadHandler.items():
			try:
				name = t.getName()
				requestID = h._requestID
				requestDict = h._requestDict
				if requestID != requestDict['requestID']:
					raise AttributeError
				startTime = requestDict.get('time')
				env = requestDict.get('environ')
				client = env and (env.get('REMOTE_NAME')
					or env.get('REMOTE_ADDR')) or None
				uri = env and requestURI(env) or None
				activeThreads.append((name, requestID,
					startTime, curTime - startTime, client, uri))
			except AttributeError:
				continue

		if activeThreads:
			headings = ('Thread name', 'Request ID', 'Start time',
				'Duration', 'Client', 'Request URI')
			sort = field('sort', None)
			wr('<table class="NiceTable"><tr>')
			column = 0
			sort_column = 1
			sort = field('sort', None)
			for heading in headings:
				sort_key = heading.lower().replace(' ', '_')
				if sort_key == sort:
					sort_column = column
				wr('<th><a href="ThreadControl?sort=%s">%s</a></th>'
					% (sort_key, heading))
				column += 1
			if abortRequest:
				wr('<th>Cancel</th>')
			wr('</tr>')
			def sort_threads(t1, t2, i=sort_column):
				v1, v2 = t1[i], t2[i]
				if v1 == v2:
					v1, v2 = t1[1], t2[1]
				elif i == 0:
					try:
						return cmp(int(v1.split('-')[-1]),
							int(v2.split('-')[-1]))
					except Exception:
						pass
				return cmp(v1, v2)
			activeThreads.sort(sort_threads)
		else:
			wr('<p>Could not determine the active threads.</p>')
		longIDs = []
		for (name, requestID, startTime, duration,
				client, uri) in activeThreads:
			if startTime:
				startTime = strtime(startTime)
				duration = int(duration + 0.5)
				if duration > 0:
					if duration > max_duration:
						duration = '<b>%s</b>' % duration
						if requestID:
							longIDs.append(requestID)
					duration = '%s&nbsp;s' % duration
				else:
					duration = int(1000*(duration) + 0.5)
					duration = '%s&nbsp;ms' % duration
			else:
				duration = startTime = '-'
			if abortRequest and requestID and requestID != myRequestID:
				checkbox = ('<input type="hidden" name="allIDs" value="%d">'
					'<input type="checkbox" name="selectedIDs" value="%d">'
					% (requestID, requestID))
			else:
				checkbox = '&nbsp;'
			if not requestID:
				requestID = '-'
			elif requestID == myRequestID:
				requestID = '<b>%s</b>' % requestID
			if not client:
				client = '-'
			if uri:
				uri = uri.replace('/', '/' + '<wbr>')
			else:
				uri = '-'
			wr('<tr><td align="right">', name,
				'</td><td align="right">', requestID,
				'</td><td>', startTime, '</td><td align="right">', duration,
				'</td><td>', client, '</td><td>', uri, '</td>')
			if abortRequest:
				wr('<td align="center">', checkbox, '</td>')
			wr('</tr>')
		if activeThreads:
			wr('</table>')
		if abortRequest:
			longIDs = ','.join(map(str, longIDs))
			wr('<input type="hidden" name="longIDs" value="%s">' % longIDs)
		wr('</form>')

		if threadCount > len(activeThreads):
			wr('<p>Idle threads waiting for requests: <b>%d</b></p>' %
				(threadCount - len(activeThreads)))
		wr('<p>Current time: %s</p>' % strtime(curTime))
