package org.openhds.task;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;

import org.apache.commons.codec.digest.DigestUtils;
import org.openhds.domain.util.CalendarAdapter;
import org.openhds.task.service.AsyncTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional;

/**
 * Template for writing entities to an XML file
 * 
 * @param <T>
 *            The type of entities to write to the file
 */
public abstract class XmlWriterTemplate<T> implements XmlWriterTask {
    private static final int PAGE_SIZE = 100;

    @Autowired
    private CalendarAdapter calendarAdapter;
    private AsyncTaskService asyncTaskService;
    private String taskName;

    public XmlWriterTemplate(AsyncTaskService asyncTaskService, String taskName) {
        this.asyncTaskService = asyncTaskService;
        this.taskName = taskName;
    }

    @Async
    @Transactional
    public void writeXml(TaskContext taskContext) {
        // service methods require authorization, which in turn depend on a user
        // this will use the security context of the user who starts the task
        SecurityContextHolder.setContext(taskContext.getSecurityContext());
//        if (!taskName.startsWith("Form")){
//        	asyncTaskService.beginNewTaskSession();
//        }

        try {
            OutputStream outputStream = new FileOutputStream(taskContext.getDestinationFile());

//            if (!taskName.startsWith("Form")){
//            	asyncTaskService.startTask(taskName);
//            }

            long itemsWritten = 0L;
            int totalCount = getTotalEntityCount(taskContext);
            int totalPages = (int) Math.ceil((double) totalCount / PAGE_SIZE);

            XMLStreamWriter xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStream);
            xmlStreamWriter.writeStartDocument();
            xmlStreamWriter.writeStartElement(getStartElementName());
            JAXBContext context = JAXBContext.newInstance(getBoundClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
            marshaller.setAdapter(calendarAdapter);

            for (int i = 0; i < totalPages; i++) {
                List<T> entities = getEntitiesInRange(taskContext, (i * PAGE_SIZE), PAGE_SIZE);
                for (T original : entities) {
                    T copy = makeCopyOf(original);
                    marshaller.marshal(copy, xmlStreamWriter);
                    itemsWritten += 1;
                }
                // clear the session so hibernate empties the cache
                // this is necessary so that it doesn't consume lots and lots of
                // memory
                // as the size of the database grows
                // TODO: Is there a better way of handling this? Stateless
                // Sessions?
                if (!taskName.startsWith("Form")){
                asyncTaskService.clearSession();
                asyncTaskService.updateTaskProgress(taskName, itemsWritten);
                }
            }

            xmlStreamWriter.writeEndElement();
            xmlStreamWriter.close();
            outputStream.close();

            InputStream inputStream = new FileInputStream(taskContext.getDestinationFile());
            String md5 = DigestUtils.md5Hex(inputStream);
            inputStream.close();
            if (!taskName.startsWith("Form")){
            asyncTaskService.finishTask(taskName, itemsWritten, md5);
            }
        } catch (Exception e) {
            System.out.println(e);
        } 
//        finally {
//        	 if (!taskName.startsWith("Form")){
//                asyncTaskService.closeTaskSession();
//        	 }
//        }
    }

    protected abstract T makeCopyOf(T original);

    protected abstract List<T> getEntitiesInRange(TaskContext taskContext, int i, int pageSize);

    protected abstract Class<?> getBoundClass();

    protected abstract String getStartElementName();

    protected abstract int getTotalEntityCount(TaskContext taskContext);

}

@@ -1605,116 +1605,9 @@
 ));%0A
-//        if (!taskName.startsWith(%22Form%22))%7B%0A//        %09asyncTaskService.beginNewTaskSession();%0A//        %7D
 %0A
+
 %0A   
@@ -1714,18 +1714,17 @@
 le());%0A%0A
-//
+ 
         
@@ -1754,34 +1754,33 @@
 sWith(%22Form%22))%7B%0A
-//
+ 
             %09asy
@@ -1814,18 +1814,17 @@
 kName);%0A
-//
+ 
         
@@ -3259,32 +3259,33 @@
                 
+%09
 asyncTaskService
@@ -3309,32 +3309,33 @@
                 
+%09
 asyncTaskService
@@ -3928,155 +3928,8 @@
  %7D %0A
-//        finally %7B%0A//        %09 if (!taskName.startsWith(%22Form%22))%7B%0A//                asyncTaskService.closeTaskSession();%0A//        %09 %7D%0A//        %7D
 %0A   

the task was not initialised