/*
 * ===========================================================================
 * PD4ML: HTML to PDF Converter for Java.
 * DXL-to-PDF converting agent
 * 
 * http://pd4ml.com, 2007
 * ===========================================================================
 */

import java.awt.Dimension;
import java.awt.Insets;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;

import lotus.domino.AgentBase;
import lotus.domino.AgentContext;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.DocumentCollection;
import lotus.domino.DxlExporter;
import lotus.domino.EmbeddedObject;
import lotus.domino.RichTextItem;
import lotus.domino.Session;

import org.xml.sax.SAXException;
import org.zefer.pd4ml.PD4Constants;
import org.zefer.pd4ml.PD4ML;
import org.zefer.pd4ml.PD4PageMark;

import com.lotus.xml.xml4j2dom.XML4JLiaison4dom;
import com.lotus.xsl.XSLProcessor;
import com.lotus.xsl.XSLTInputSource;
import com.lotus.xsl.XSLTResultTarget;

public class PdfAgent extends AgentBase {
	
	private final static String xslName = "dxl4pd4ml.xsl";
	
	/* configuration switch: XSL location */
	private final static boolean readXslFromJar = false;

	private final static String xslDocumentFormName = "XSL4PD4ML";
	private final static String xslDocumentFieldName = "XSLT";

	
	
	public void NotesMain() {
		try {
			Session session = getSession();
			AgentContext agentContext = session.getAgentContext();
//			System.out.println("AGENT start.");
			Database db = agentContext.getCurrentDatabase();

			String xsl = null;

			if (readXslFromJar) {
				xsl = getXsl();
				// the method uses Java ClassLoader to read XSL. Works for v6.5.4, but fails for older versions
				// (according to some reports)
			} else {
				// bulky, but a standard for Domino way to store XSL documents.
				// useful for debug and XSL refining
				DocumentCollection dcx = db.search("@Contains(Form; \"" + xslDocumentFormName + "\")");
				Document xsldoc = dcx.getFirstDocument();
				if (xsldoc != null) {
					xsl = xsldoc.getItemValueString(xslDocumentFieldName);
				} else {
					// looks like there is no form named XSL4PD4ML
					// try to read XSL from JAR file
					throw new IOException( "Can not find " );
				}
			}

			// System.out.println("XSL: " + xsl);

			// collect selected documents
			DocumentCollection dc = agentContext.getUnprocessedDocuments();

			Document doc = dc.getFirstDocument();
			while (doc != null) {

				Document tmp = db.createDocument();
				RichTextItem rti = tmp.createRichTextItem("PD4MLBody");
				doc.renderToRTItem(rti);

				DxlExporter dxl = session.createDxlExporter();
				dxl.setConvertNotesBitmapsToGIF(true);
				dxl.setOutputDOCTYPE(false);
				String xml = dxl.exportDxl(tmp);

//				System.out.println("DXL FORM: " + xml);

				String html = transformXML(xml, xsl);

//				System.out.println("HTML: " + html);

				File pdf = File.createTempFile("pd4ml", ".pdf");
				java.io.FileOutputStream fos = new java.io.FileOutputStream(pdf);
				generatePDF( html, 
						     fos, 
						     PD4Constants.A4, 
						     "java:/fonts", // the reference points to a fonts collection packed to a JAR.
						                    // an alternative would be to reference a local font directory: "c:/windows/fonts"
						     				// read more about font directory configuration: http://pd4ml.com/reference.htm#7.1
							 "<font color=green face=tahoma>DXL to PDF convesion result. $[page] of $[total]</font>");

				doc.removeItem("PdfAttachment");
				doc.save(true, true);
				RichTextItem obj = (RichTextItem) doc.getFirstItem("PdfAttachment");
				if (obj == null) {
					RichTextItem attx = doc.createRichTextItem("PdfAttachment");
					attx.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null,
							pdf.getAbsolutePath(), "pdfattachment");
					fos.flush();
					fos.close();
				}

				doc.save(true, true);
				pdf.delete();

				doc = dc.getNextDocument();
			}
			
//			System.out.println("AGENT done.");
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static final void generatePDF(String inputHTML, OutputStream fos,
			Dimension format, String fontsDir, String footerBody)
			throws Exception {

		// the method uses only basic PD4ML API calls
		// more info:
		// Reference: http://pd4ml.com/reference.htm
		// Javadoc: http://pd4ml.com/api/index.html
		
		PD4ML pd4ml = new PD4ML();
		pd4ml.setPageInsets(new Insets(20, 10, 10, 10));
		pd4ml.setHtmlWidth(1000);
		pd4ml.setPageSize(format);

		if (fontsDir != null && fontsDir.length() > 0) {
			pd4ml.useTTF(fontsDir, true);
		}

		if (footerBody != null && footerBody.length() > 0) {
			PD4PageMark footer = new PD4PageMark();
			footer.setAreaHeight(-1);
			footer.setHtmlTemplate(footerBody);
			pd4ml.setPageFooter(footer);
		}

//		pd4ml.enableDebugInfo(); //	in Lotus environment the debug info appears under File->Tools->Show Java Debug Console
		pd4ml.render(new StringReader(inputHTML), fos, new URL("file:."), "utf-8");
		fos.flush();
		fos.close();
	}

	public static final String transformXML(String xml, String xsl) throws SAXException,
			MalformedURLException, IOException {

		XSLProcessor xp = new XSLProcessor(new XML4JLiaison4dom());
		StringReader xmlSource2 = new StringReader(xml);
		StringReader xslSource2 = new StringReader(xsl);
		StringWriter sw = new StringWriter();
		XSLTInputSource xs = new XSLTInputSource(xmlSource2);
		XSLTInputSource xt = new XSLTInputSource(xslSource2);
		XSLTResultTarget tout = new XSLTResultTarget(sw);
		xp.process(xs, xt, tout);
		return sw.toString();
	}

	public String getXsl() throws IOException {

		ByteArrayOutputStream fos = new ByteArrayOutputStream();
		byte buffer[] = new byte[2048];

		InputStream is = getClass().getClassLoader().getResourceAsStream(xslName);
		BufferedInputStream bis = new BufferedInputStream(is);

		int read;
		do {
			read = bis.read(buffer, 0, buffer.length);
			if (read > 0) {
				fos.write(buffer, 0, read);
			}
		} while (read > -1);

		fos.close();
		bis.close();
		is.close();

		return new String(fos.toByteArray());
	}


	  /* 
	   * The two methods below are useful for offline debug. In order to run the Java class 
	   * offline you need in the classpath the following:
	   * 
	   * pd4ml.jar (or pd4ml_demo.jar)
	   * ss_css2.jar
	   * xml4j.jar
	   * lotusXSL.jar
	   * notes.jar
	   * 
	   * Please do not forget to pre-configure fonts dir with the command line call:
	   * java -jar pd4ml.jar -configure.fonts c:/windows/fonts 
	   * 
	   * */
	  
	public static void main( String args[] ) {

		try {
			String xml = readFile("dxl_sample.xml");
			String xsl = readFile(xslName);
			String html = transformXML(xml, xsl);
			System.out.println("HTML: " + html);
			File pdf = File.createTempFile("pd4ml", ".pdf");
			java.io.FileOutputStream fos = new java.io.FileOutputStream(pdf);
			generatePDF(
					html,
					fos,
					PD4Constants.A4,
					"c:/windows/fonts",
					"<font color=green face=tahoma>DXL to PDF convesion result. $[page] of $[total]</font>");

			Runtime.getRuntime().exec( "C:\\Program Files\\Adobe\\Acrobat 7.0\\Reader\\AcroRd32.exe " + pdf.getAbsolutePath() );
		
		} catch (Exception e) {
			e.printStackTrace();
		}		
		
	}
	
	private final static String readFile( String path ) throws Exception {

		File f = new File( path );
		FileInputStream is = new FileInputStream(f);
		BufferedInputStream bis = new BufferedInputStream(is);
		
		ByteArrayOutputStream fos = new ByteArrayOutputStream();
		byte buffer[] = new byte[2048];

		int read;
		do {
			read = is.read(buffer, 0, buffer.length);
			if (read > 0) { // something to put down
				fos.write(buffer, 0, read);
			}
		} while (read > -1);

		fos.close();
		bis.close();
		is.close();

		return fos.toString();
	}
}

