"""
Code dealing with product (i.e., fits file) delivery.
"""

#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.


import base64

from twisted.web import resource
from twisted.web import server

from gavo import base
from gavo.protocols import products
from gavo.svcs import streaming
from gavo.svcs import weberrors
from gavo.web import grend
from gavo.web import producttar


class MissingPreview(resource.Resource):
	"""A twisted resource returning a 404 image.
	"""
	noPreviewImg = base64.b64decode(
		'iVBORw0KGgoAAAANSUhEUgAAAMgAAABkAgMAAAC+6JeYAAAADFBMV'
		'EUQEg9OUE2Qko/8//texVsLAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAA'
		'Ad0SU1FB+QIDAwjFnjAPBkAAAK0SURBVFjD7dQ/aBNRHAfwbh0ydCoiBzlUcEpz4CBC/zzo0'
		'EVQqKOgg0MRioKDZLiagmKHgsF20amUCiJCtVAhyl0eWNpAb2jTGwp1ae8NRRya97zAS3t3P'
		'393bcy1TaEddOlluPfL5T653yXv+2uDM7/aEpKQhCQkIQk5f6Su/D0RkLWA4FprXiNaEWeyS'
		'bTvfh5X3nifb02s2BnthUdjxCetSRncxxtP3/rhteobz1TAv+Qr4E3DgEyBGKsrUz8qFF7TJ'
		'qnQdWtxbtgI69vDRc6A85oATx/RTQLVuT1WWEkTKJEmkalxe6Hq5cK60POFCXC44yLJmb0EO'
		'TjWK6Lhc8Z+ZLvAyjy4HpbLvU+2BpEwCZ6RC5AIHkys3+vU4GeciAKzBFwOy6Vn1xgAO0SEw'
		'7LYVDZGdsS4jeRO9FylKw5+R9SYETUmOHOcPuyrL0aWnpfHkYztYr1J2/ku8C3uINFzpe6Qr'
		'1WJ+inoihFXdTUklot1FW76KfCv4sEz8/2ggKjJIUlXKbzMH9tjK2ffllNnJjKfBPnfE20/u'
		'h3R4tEonbXTEBkdtw8IBnq7MQRObMzdT1AEImI3hkCcbKgbIxhvWvlar8MmGfDfw6gxHQTtO'
		'APqymhpQKqVYurwuBhbnMN4a+kb1Rq4vm7cxbtk9OIehniP2YFkO+new3ex7AUmrEKWZOYFu'
		'NLsIUh0vYgTDnNvg5wJSNcRwspbg+v3s52ZDzNIgp48EkM3FCTBhA3vPj/qPEYsBkzLEv/hg'
		'4jQiNBJnAHCsb1i6RbxjzZmOeCQrBakL0SNHRAR5d6Wki5rRwgmn+9WiabOdqTA9XIGBWYY+'
		'rffOAOcNVac/7iiznb/ipNFzfJTkmqrVO2AOvTjf8INUzfDGSCHeOmiix8p9klj/JR77P+QJ'
		'MgJSUhCEnJ+yB+3JLSzGAuzmQAAAABJRU5ErkJggg==')

	def render(self, request):
		request.setResponseCode(404)
		request.setHeader("content-type", "image/png")
		request.write(self.noPreviewImg)
		request.finish()

_MISSING_PREVIEW = MissingPreview()


class ProductRenderer(grend.ServiceBasedPage):
	"""The renderer used for delivering products.

	This will only work with a ProductCore since the resulting
	data set has to contain products.Resources.  Thus, you probably
	will not use this in user RDs.
	"""
	name = "get"
	pathFromSegments = ""

	def render(self, request):
		if len(request.strargs.get("accref", []))>1:
			# more than one accref passed in: produce a tar
			assert not self.pathFromSegments
			data = request.strargs
		else:
			data = {"accref":
					products.RAccref.fromRequest(self.pathFromSegments, request)}

		self.runAsyncWithFormalData(data, request
			).addCallback(self._deliver, request
			).addErrback(weberrors.renderDCErrorPage, request)
		return server.NOT_DONE_YET
	
	def _deliver(self, result, request):
		if len(result)>1:
			# TODO: presumably we should have the option of producing
			# tars with 0 and 1 item(s); we should probably have
			# a special renderer for that.
			return producttar.getTarMaker()._streamOutTar(
				result, request, self.queryMeta, "products.tar")

		else:
			return self._deliverSingle(result, request)

	def _deliverSingle(self, result, request):
		doPreview = self.queryMeta.ctxArgs.get("preview")
		product = result[0]

		# This catches cases where people want previews of processed
		# products (in particular cutouts).  I doubt this is a good
		# idea, but getting out of this hack again is hard.
		try:
			if doPreview and not "Preview" in product.__class__.__name__:
				return products.PreviewCacheManager.getPreviewFor(product
					).addCallback(self._deliverPreview, product, request
					).addErrback(self._deliverPreviewFailure, request)
		except base.DataError:
			return _MISSING_PREVIEW.render(request)

		else:
			if hasattr(product, "render"):
				res = product.render(request)
				if res not in (None, b"", server.NOT_DONE_YET):
					# we don't really want products' render methods to
					# immediately return data; in particular not large amounts
					# of data, as they will block the event loop.  But there is
					# a bit of error-generating code below here that returns
					# bytes, and we don't want to refactor that.
					request.write(res)
					request.finish()
					return server.NOT_DONE_YET
			else:
				base.ui.notifyWarning("Non-product result in product renderer:"
					" %s"%repr(product))
				return streaming.streamOut(lambda w, q: w.write(product), request)

	def _deliverPreview(self, content, product, request):
		previewMime = product.pr["preview_mime"] or "image/jpeg"
		request.setHeader("content-type", str(previewMime))
		request.setHeader("content-length", str(len(content)))
		request.write(content)
		request.finish()
	
	def _deliverPreviewFailure(self, failure, request):
		failure.printTraceback()
		data = b"Not an image (preview generation failed, please report)"
		request.setResponseCode(500)
		request.setHeader("content-type", "text/plain")
		request.setHeader("content-length", str(len(data)))
		request.write(data)
		request.finish()

	def getChild(self, name, request):
		self.pathFromSegments = "/".join(request.popSegments(name))
		return self
