package org.slc.sli.test.utils;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Iterator;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter;
import org.slc.sli.test.xmlgen.StateEdFiXmlGenerator;

/**
 * Interchange writer
 *
 * @author bsuzuki
 *
 */
public class InterchangeWriter<T> {

    private static final int XML_WRITER_BUFFER_SIZE = 104857600; // 100 MB
    private static final boolean FORMAT_INTERCHANGE_XML = true;

    private String interchangeName = null;
    private String xmlFilePath = null;

    private XMLStreamWriter writer = null;
    private Marshaller streamMarshaller = null;
    
    private long interchangeStartTime;
    
    public InterchangeWriter(Class<T> interchange) {
        
        interchangeStartTime = System.currentTimeMillis();
        interchangeName = interchange.getSimpleName();
        xmlFilePath = StateEdFiXmlGenerator.rootOutputPath + "/" + interchangeName + ".xml";

        System.out.println("Creating interchange " + interchangeName);
        try {
            JAXBContext context = JAXBContext.newInstance(interchange);
            streamMarshaller = context.createMarshaller();
            // Doesn't work for XMLStreamWriter
//            streamMarshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
            streamMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        } catch (JAXBException e1) {
            e1.printStackTrace();
            System.exit(1);  // fail fast for now
        }

        try {
            FileOutputStream xmlFos = new FileOutputStream(xmlFilePath);
            OutputStream xmlBos = new BufferedOutputStream(xmlFos, XML_WRITER_BUFFER_SIZE);
            writer = XMLOutputFactory.newFactory().createXMLStreamWriter(xmlBos, "UTF-8");
            if (FORMAT_INTERCHANGE_XML) {
                IndentingXMLStreamWriter indentingWriter = new IndentingXMLStreamWriter(writer);
                indentingWriter.setIndentStep("    ");
                writer = indentingWriter;
            }
            writer.writeStartDocument("UTF-8", "1.0");

            // remove unwanted population of namespace attributes
            writer.setNamespaceContext(new NamespaceContext() {
                public Iterator<String> getPrefixes(String namespaceURI) {
                    return null;
                }

                public String getPrefix(String namespaceURI) {
                    return "";
                }

                public String getNamespaceURI(String prefix) {
                    return null;
                }
            });
            
            writer.writeStartElement(interchangeName);
            writer.writeNamespace(null, "http://ed-fi.org/0100");

        } catch (XMLStreamException e) {
            e.printStackTrace();
            System.exit(1);  // fail fast for now
        } catch (FactoryConfigurationError e) {
            e.printStackTrace();
            System.exit(1);  // fail fast for now
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.exit(1);  // fail fast for now
        }
    }
    
    public void close() {
        
      System.out.println("generated and marshaled in: "
      + (System.currentTimeMillis() - interchangeStartTime));

        try {

//            writer.writeEndElement();
            writer.writeEndDocument();
            writer.close();
        } catch (XMLStreamException e) {
            e.printStackTrace();
            System.exit(1);  // fail fast for now
        }

        streamMarshaller = null;
    }

    /**
     * Marshal the provided object using the XMLStreamWriter specified
     *
     * @param objectToMarshal
     * @param outputStream
     * @throws JAXBException
     */
    public void marshal(Object objectToMarshal) {
        if (objectToMarshal != null) {

            try {                
                streamMarshaller.marshal(objectToMarshal, writer);
            } catch (JAXBException e) {
                e.printStackTrace();
                System.exit(1);  // fail fast for now
            }

        } else {
            throw new IllegalArgumentException("Cannot marshal null object");
        }
    }

    public String getXmlFilePath() {
        return xmlFilePath;
    }

}

@@ -871,16 +871,81 @@
  = true;
+%0A    private static final boolean SINGLE_LINE_MARSHALLING = true;
 %0A%0A    pr
@@ -1056,32 +1056,148 @@
  writer = null;%0A
+    private XMLStreamWriter defaultWriter = null;%0A    private IndentingXMLStreamWriter indentingWriter = null;%0A    %0A
     private Mars
@@ -2291,33 +2291,40 @@
 E);%0A            
-w
+defaultW
 riter = XMLOutpu
@@ -2447,33 +2447,8 @@
     
-IndentingXMLStreamWriter 
 inde
@@ -2490,17 +2490,24 @@
 mWriter(
-w
+defaultW
 riter);%0A
@@ -2603,16 +2603,77 @@
 Writer;%0A
+            %7D else %7B%0A                writer = defaultWriter;%0A
         
@@ -3364,24 +3364,36 @@
 org/0100%22);%0A
+            
 %0A        %7D c
@@ -3952,16 +3952,151 @@
   try %7B%0A
+            if (FORMAT_INTERCHANGE_XML && SINGLE_LINE_MARSHALLING) %7B%0A                defaultWriter.writeCharacters(%22%5Cn%22);%0A            %7D
 %0A//     
@@ -4550,16 +4550,48 @@
      */%0A
+    @SuppressWarnings(%22unused%22)%0A
     publ
@@ -4726,84 +4726,475 @@
     
-streamMarshaller.marshal(objectToMarshal, writer);%0A            %7D catch (JAXB
+if (FORMAT_INTERCHANGE_XML && SINGLE_LINE_MARSHALLING) %7B%0A                    defaultWriter.writeCharacters(%22%5Cn%22);%0A                    streamMarshaller.marshal(objectToMarshal, defaultWriter);%0A                %7D else %7B%0A                    streamMarshaller.marshal(objectToMarshal, writer);%0A                %7D%0A            %7D catch (JAXBException e) %7B%0A                e.printStackTrace();%0A                System.exit(1);  // fail fast for now%0A            %7D catch (XMLStream
 Exce

Write one entity per line for easier grepping by default.