"""
A renderer that queries a single field in a service.
"""

#c Copyright 2008-2025, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


from twisted.web import resource
from twisted.web import server
from twisted.web.template import tags as T

from gavo import base
from gavo import formal
from gavo import svcs
from gavo.svcs import streaming
from gavo.web import common
from gavo.web import grend
from gavo.svcs import weberrors


NT = formal.addNevowAttributes


class VOTableResource(resource.Resource):
# A quick hack to support VOTable responses.
# Kill this in favour of serviceresults.
	def __init__(self, result, queryMeta):
		resource.Resource.__init__(self)
		self.result, self.queryMeta = result, queryMeta
	
	def render(self, request):
		if base.getMetaText(self.result.getPrimaryTable(), "_queryStatus"
				)=="OVERFLOW":
			fName = "truncated_votable.xml"
		else:
			fName = "votable.xml"
		request.setHeader("content-type", base.votableType)
		request.setHeader('content-disposition',
			'attachment; filename=%s'%fName)
		return streaming.streamVOTable(request, self.result, self.queryMeta)


class QPRenderer(grend.HTMLResultRenderMixin,
		grend.CustomTemplateMixin,
		grend.ServiceBasedPage):
	"""The Query Path renderer extracts a query argument from the query path.

	Basically, whatever segments are left after the path to the renderer
	are taken and fed into the service.  The service must cooperate by
	setting a queryField property which is the key the parameter is assigned
	to.

	QPRenderers cannot do forms, of course, but they can nicely share a
	service with the form renderer.

	To adjust the results' appreance, you can override resultline (for when
	there's just one result row) and resulttable (for when there is more
	than one result row) templates.  In the templates, you can retrieve
	the input parameter's value as the inPar data, for instance, like this::

	  <n:invisible n:data="inPar" n:render="string"/>
	"""
	name = "qp"
	queryValue = None
	defaultQueryFormat = "HTML"

	@classmethod
	def isCacheable(self, segments, request):
		return False  # That's the default, but let's be sure here...

	def render(self, request):
		if not self.queryValue:
			raise svcs.UnknownURI("This page is a root page for a"
				" query-based service.  You have to give a valid value in the"
				" path.")

		args = request.strargs.copy()
		args[self.service.getProperty("queryField")] = [self.queryValue]
		self.runAsync(args
			).addCallback(self._formatOutput, request
			).addErrback(self._handleError, request
			).addErrback(weberrors.renderDCErrorPage, request)
		return server.NOT_DONE_YET
	
	def _formatOutput(self, res, request):
		self.result = res

		# Hm... can we get rid of this hack?  Just support proper RESPONSEFORMAT,
		# perhaps?
		if "vot" in self.queryMeta.get("format", "").lower():
			return VOTableResource(self.result, self.queryMeta).render(request)

		# Hm... I suppose this and the corresponding stuff in
		# UnifiedDALRenderer should be merged.
		if isinstance(self.result, tuple):
			# core returned a complete document (mime and string)
			mime, payload = self.result
			request.setHeader("content-type", mime)
			return streaming.streamOut(
				lambda f: f.write(payload), request, self.queryMeta)

		nMatched = self.queryMeta.get("Matched")
		if nMatched==0:
			raise svcs.UnknownURI("No record matching %s."%(
				self.queryValue))
		elif nMatched==1:
			self.customTemplate = self.getTemplate("resultline")
		else:
			self.customTemplate = self.getTemplate("resulttable")
		return super(QPRenderer, self).render(request)

	def _handleError(self, flr, request):
		# all errors except svcs.Errors (which are handled by weberrors)
		# are translated to 404s (and logged).
		if isinstance(flr.value, (base.ExecutiveAction, svcs.Error)):
			return flr

		base.ui.notifyFailure(flr, "A core raised '%s' into a qp renderer."
			"  Translating into a 404."%(flr.value))
		raise svcs.UnknownURI("The query initiated by your URL failed,"
			" yielding a message '%s'."%flr.getErrorMessage())

	def data_inPar(self, request, tag):
		return self.queryValue

	def getChild(self, name, request):
		# if we're here, we are the responsible resource and just stuff
		# the remaining segments into the query value
		self.queryValue = "/".join(request.popSegments(name))
		return self

	def getTemplate(self, resultFormat):
		if resultFormat in self.service.templates:
			return self.service.getTemplate(resultFormat)
		return common.doctypedStan(
			T.html[
				T.head(render="commonhead")[
					T.title(render="meta")['title'],
					T.style(type="text/css", render="servicestyle"),],
				T.body(render="withsidebar")[
					T.h1(render="meta")['title'],
					NT(T.div, data="result")(class_="result") [
						T.transparent(render=resultFormat)]]])
