class ez5.PdfCreatorUtils extends CUI.Element

	@print: (objects = [], pdfDocumentNode) ->

		spinner = CUI.spinner(text: $$("pdf-creator.print.preparing.text"))

		if not pdfDocumentNode or pdfDocumentNode not instanceof ez5.PdfCreator.Node.Document
			return CUI.rejectedPromise("PdfCreatorUtils.print :: pdfDocument needs to be defined and it needs to be an instance of ez5.PdfCreator.Node.Document", pdfDocumentNode)

		fylrUrl = ez5.session.getBaseConfig("plugin", "pdf-creator").pdf_creator?.fylr_url

		if CUI.util.isEmpty(fylrUrl)
			if not ez5.version("6")
				spinner.destroy()
				return CUI.rejectedPromise($$("pdf-creator.print-url-not-configured"))
			else if ez5.pluginManager.getPlugin("server-pdf")
				html2pdfUrl = CUI.parseLocation(ez5.pluginManager.getPlugin("server-pdf").getPluginURL()).path
				fylrUrl = Session.addToken(html2pdfUrl+"/html2pdf")
			else
				spinner.destroy()
				return CUI.rejectedPromise($$("pdf-creator.print-url-not-configured.fylr"))

		deferred = new CUI.Deferred()
		@fetchLinkedObjects(objects).done(=>
			opts =
				objects: objects
				styles: ez5.PdfCreator.printSyles
				spinner: spinner

			customCssUrl = ez5.session.getBaseConfig("plugin", "pdf-creator").pdf_creator?.custom_css_url
			if customCssUrl
				opts.custom_css_url = customCssUrl

			html = pdfDocumentNode.renderPdf(opts).outerHTML
			# Search for all the loading="lazy" images and remove the attribute.
			# This is necessary because the images are not loaded when the pdf is generated in the server
			# but we need this attribute to improve the performance in the browser ( avoid loading images that are not visible ).
			html = html.replace(/loading="lazy"/g, "")

			# Header could be a promise, so we need to wait for it to be resolved.
			headerPromise = pdfDocumentNode.renderHeader(opts)
			header = null
			if CUI.util.isPromise(headerPromise)
				headerPromise.done((headerDOM) =>
					header = headerDOM.outerHTML.replace(/loading="lazy"/g, "")
				)
			else
				header = headerPromise.outerHTML.replace(/loading="lazy"/g, "")
				headerPromise = CUI.resolvedPromise()

			headerPromise.done =>

				footer = pdfDocumentNode.renderFooter(opts).outerHTML
				footer = footer.replace(/loading="lazy"/g, "")
				margins = pdfDocumentNode.getMargins()

				filename = pdfDocumentNode.getFilename(objects[0])
				data =
					file_name: filename
					document: html
					properties:
						displayHeaderFooter: true
						printBackground: true
						headerTemplate: header
						footerTemplate: footer
						landscape: pdfDocumentNode.isLandscape()
						paperWidth: pdfDocumentNode.getPaperWidth()
						paperHeight: pdfDocumentNode.getPaperHeight()
						marginTop: margins.top
						marginBottom: margins.bottom
						marginLeft: margins.left
						marginRight: margins.right

				spinner.updateText($$("pdf-creator.print.rendering.text"))
				spinner.autoSize()

				xhr = new CUI.XHR
					method: "POST"
					url: fylrUrl
					body: JSON.stringify(data)
					responseType: "blob"
				return xhr.start().done((response) =>
					CUI.FileReader.save(filename, response, "application/pdf")
					deferred.resolve()
				).fail(deferred.reject).always(=>spinner.destroy())
		)

		return deferred.promise()

	@initDocumentData: (data) ->
		initNodes = (children) =>
			nodes = []
			if not children
				return null

			for child in children
				plugin = ez5.PdfCreator.getPlugin(child.name)
				if not plugin
					console.warn("PdfCreator :: Load :: Skipping unknown type of plugin, name: '#{child.name}'")
					continue

				node = new plugin(
					data: child.data
					open: child.open
					children: initNodes(child.children)
				)
				nodes.push(node)
			return nodes

		children = data.children
		if children
			data.children = initNodes(children)

		return data

	# Search and fetch data of linked objects in objects.
	@fetchLinkedObjects: (objects) ->
		objectsToFetch = {}
		findLinkedObject = (object) ->
			if not CUI.util.isPlainObject(object)
				return
			globalObjectId = object._global_object_id
			format = object._format
			# If we have formatIncomplete we need to get the full object.
			formatIncomplete = object._format_incomplete
			if globalObjectId and (format != "long" or formatIncomplete)
				if not objectsToFetch[globalObjectId]
					objectsToFetch[globalObjectId] = []
				objectsToFetch[globalObjectId].push(object)
			for _, value of object
				if CUI.util.isArray(value)
					for _value in value
						findLinkedObject(_value)
				else
					findLinkedObject(value)
			return

		for object in objects
			findLinkedObject(object)

		if CUI.util.isEmptyObject(objectsToFetch)
			return CUI.resolvedPromise()

		deferred = new CUI.Deferred()
		ez5.api.search_no_limit(
			json_data:
				limit: 1000000
				search: [
					type: "in"
					bool: "must"
					fields: [ "_global_object_id" ]
					in: Object.keys(objectsToFetch)
				]
		).done((data) =>
			for _objectLongFormat in data.objects
				_objects = objectsToFetch[_objectLongFormat._global_object_id] or []
				for object in _objects
					CUI.util.mergeMap(object, _objectLongFormat)
					object._format = _objectLongFormat._format # Now it is format 'long' but mergeMap does not override it.
			return deferred.resolve()
		)
		return deferred.promise()

	@replaceTextPlaceholders: (text, object) ->
		text = text.replace(/%username%/g, ez5.session.getUser().getDisplayText())
		text = text.replace(/%date%/g, ez5.format_date(CUI.util.moment()))

		if not object
			return text

		# Object top level keys.
		for key in ["_global_object_id", "_system_object_id"]
			regexp = new RegExp("%#{key}%", "g")
			text = text.replace(regexp, object[key] or key)

		# Standard
		standardText = ez5.loca.getBestFrontendValue(object._standard["1"]?.text) or "-"
		text = text.replace(/%_standard\.1\.text%/g, standardText)

		return text