package net.sourceforge.rtf;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.text.Format;

import net.sourceforge.rtf.context.DigesterRTFContextFields;
import net.sourceforge.rtf.context.fields.RTFContextFields;
import net.sourceforge.rtf.document.RTFDocument;
import net.sourceforge.rtf.document.transformer.config.DigesterTransformerConfig;
import net.sourceforge.rtf.document.transformer.config.TransformerConfig;
import net.sourceforge.rtf.template.IContext;

import org.xml.sax.SAXException;

public class RTFTemplate {

    private InputStream rtfSourceInputStream; // (reader or InpuStream)
    private Reader rtfSourceReader; // (reader or InpuStream)

    private static TransformerConfig defaultTransformerConfig;
    static {       
        try {
            defaultTransformerConfig = DigesterTransformerConfig.getTransformerConfig();
        }
        catch(Exception e) {}
    }
    
    private RTFDocument transformedDocument;
    private IRTFDocumentParser parser;    
    private ITemplateEngine templateEngine;
    
    private InputStream xmlFields;
      
    private IRTFDocumentTransformer transformer;    
    private TransformerConfig transformerConfig;

    
    public void initializeContext() {
        templateEngine.initializeContext();
    }

    public IContext getContext() {
        return templateEngine.getContext();
    }
    
    /**
     * Use a pre-populated Context.
     * This method treats any previously populated keys in the context kindly,
     * so it won't act unpredictably if this is called late
     *
     * @param innerContext  a pre-populated Context object
     */
    public void setGlobalContext( IContext globalContext) {
        templateEngine.setGlobalContext(globalContext);
    }
    
    /**
     * Set the template to use
     * 
     * @param template
     */
    public void setTemplate(File rtfSourceFile) throws FileNotFoundException {
        this.rtfSourceInputStream = null;
        this.transformedDocument = null;
        this.rtfSourceReader = new FileReader(rtfSourceFile);
    }        
    
    /**
     * Set the template to use
     * 
     * @param template
     */
    public void setTemplate(InputStream template) {
        this.rtfSourceInputStream = template;
        this.transformedDocument = null;
        this.rtfSourceReader = null;
    }    
    
    /**
     * Set the template to use
     * 
     * @param template
     */
    public void setTemplate(Reader template) {        
        this.rtfSourceReader = template;
        this.transformedDocument = null;
        this.rtfSourceInputStream = null;
    }
    
    /**
     * Put a value for the given key.
     *
     * @param key
     * @param value
     */
    public void put(String key, Object value) {
        templateEngine.put(key, value);
    }
    
    /**
     * Merge.
     *
     * @param file name of file to merge into
     */
    public RTFDocument merge(String file) throws Exception {
        transformAndSetTemplate();
        templateEngine.merge(file);
        return this.transformedDocument;
    }
    
    /**
     * Merge.
     *
     * @param file    file to merge into
     */
    public RTFDocument merge(File file) throws Exception {
        transformAndSetTemplate();
        templateEngine.merge(file);
        return this.transformedDocument;
    }
    
    /**
     * Merge.
     *
     * @param writer    writer to merge into
     */
    public RTFDocument merge(Writer writer) throws Exception {
        transformAndSetTemplate();
        templateEngine.merge(writer);
        return this.transformedDocument;
    }
    
    /**
     * Set the default format for a given class.
     *
     * @param clazz   class to set format for
     * @param format  format to use for instances of the given class
     */
    public void setDefaultFormat(Class clazz, Format format) {
        templateEngine.setDefaultFormat(clazz, format);
    }
    
    public RTFDocument transform() throws IOException, SAXException {
    	return transform(false);
    }
    
    public RTFDocument transform(boolean convertBracketsLowLevelVelocityCode) throws IOException, SAXException {
        if (parser == null) 
            throw new IOException("RTFDocumentParser must be defined. Use setParser method to set it.");
        if (transformer == null) 
            throw new IOException("RTFDocumentTransformer must be defined. Use setTransformer method to set it.");
        
        /**
         * 1. Parse RTF reader to transform it with macoro of template engine.
         */ 
        RTFDocument document = null;
        if (rtfSourceInputStream != null) {
        	if (convertBracketsLowLevelVelocityCode) {
            	ByteArrayOutputStream baos = new ByteArrayOutputStream();
            	RTFConverter.convertStream(this.rtfSourceInputStream, baos);
            	this.rtfSourceInputStream = new ByteArrayInputStream(baos.toByteArray());
        	}
            parser.parse(this.rtfSourceInputStream);
        } else {
        	if (convertBracketsLowLevelVelocityCode) {
            	ByteArrayOutputStream baos = new ByteArrayOutputStream();
            	RTFConverter.convertStream(this.rtfSourceReader, baos);
            	this.rtfSourceReader = new InputStreamReader(new ByteArrayInputStream(baos.toByteArray()));
        	}
            parser.parse(this.rtfSourceReader);
        }
        document = parser.getRTFDocument();
        if (document == null)
            throw new IOException(
                    "Error : impossible to get RTF document of source file RTF ");
        /**
         * 
         */ 
        transformer.setTransformerConfig(transformerConfig);        
        
        if (xmlFields == null)
            this.transformedDocument =  transformer.transform(document, templateEngine.getContext());        
        else {
            RTFContextFields rtfContextFields = DigesterRTFContextFields
            .getRTFContextFields(xmlFields);
            this.transformedDocument = transformer.transform(document, rtfContextFields);       
        }     
        return this.transformedDocument;
    }
    
    private void transformAndSetTemplate() throws IOException, SAXException {
        if (this.transformedDocument == null) {
            this.transformedDocument = transform();
        }
        Reader transformedReader = transformedDocument.getReader();
        templateEngine.setTemplate(transformedReader);       
    }
    
    public ITemplateEngine getTemplateEngine() {
        return templateEngine;
    }
    public void setTemplateEngine(ITemplateEngine templateEngine) {
        this.templateEngine = templateEngine;
    }
    public IRTFDocumentTransformer getTransformer() {
        return transformer;
    }
    public void setTransformer(IRTFDocumentTransformer transformer) {
        this.transformer = transformer;
    }

    public void setTransformerConfig(TransformerConfig transformerConfig) {
        this.transformerConfig = transformerConfig;
    }

    public TransformerConfig getTransformerConfig() {
        if (transformerConfig == null) {
            transformerConfig = defaultTransformerConfig;
        }
        return transformerConfig;
    }

    public InputStream getXmlFields() {
        return xmlFields;
    }

    public void setXmlFields(InputStream xmlFields) {
        this.xmlFields = xmlFields;
    }

    public void setTransformedDocument(RTFDocument transformedDocument) {
        this.transformedDocument = transformedDocument;
    }

    public RTFDocument getTransformedDocument() {
        return transformedDocument;
    }

    public int getGroupByPerPageBreak() {
        if (this.transformer != null)
            return this.transformer.getGroupByPerPageBreak();
        return -1;
    }

    public void setGroupByPerPageBreak(int groupByPerPageBreak) {
        if (this.transformer != null)
            this.transformer.setGroupByPerPageBreak(groupByPerPageBreak);
    }

    public IRTFDocumentParser getParser() {
        return parser;
    }

    public void setParser(IRTFDocumentParser parser) {
        this.parser = parser;
    }

    public boolean isCircularReferences() {
        if (this.transformer != null)
            return this.transformer.isCircularReferences();
        return false;
    }

    public void setCircularReferences(boolean circularReferences) {
        if (this.transformer != null)
            this.transformer.setCircularReferences(circularReferences);
    }
    
    
    
}
