package com.rttsweb.stels_xml_extension;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.FileUtils;

public class MergeXMLTasks {

	public MergeXMLTasks(){
		
	}
	
	public static void merge_xml_files(String dirPath, String mergedFilePath) throws Throwable{
		mergeXmlFiles(dirPath, mergedFilePath, " ");
	}
	
	public static void merge_xml_files(String dirPath, String mergedFilePath, String replacement) throws Throwable{
		mergeXmlFiles(dirPath, mergedFilePath, replacement);
	}
	
	
	@SuppressWarnings("restriction")
	private static void mergeXmlFiles(String dirPath, String mergedFilePath, String replacement) throws Throwable{
		
		//if the merged file exists, delete it
        File mergedFile = new File(mergedFilePath);
        if(mergedFile.exists()){
        	FileUtils.forceDelete(mergedFile);
        }
		       
        File dir = new File(dirPath);
        File[] rootFiles = dir.listFiles();
        
        //Writer outputWriter = new FileWriter(mergedFilePath);
        FileOutputStream outputWriter = new FileOutputStream(mergedFilePath);
        XMLOutputFactory xmlOutFactory = XMLOutputFactory.newFactory();
        XMLEventWriter xmlEventWriter = xmlOutFactory.createXMLEventWriter(outputWriter, "UTF-8");
        XMLEventFactory xmlEventFactory = XMLEventFactory.newFactory();

        xmlEventWriter.add(xmlEventFactory.createStartDocument("UTF-8", "1.0"));
        xmlEventWriter.add(xmlEventFactory.createStartElement("", null, "rootSet"));

        XMLInputFactory xmlInFactory = XMLInputFactory.newFactory();
        for (File rootFile : rootFiles) {
        	
        	File cleanRootFile = getXMLReadyFile(rootFile, replacement);
        	
            XMLEventReader xmlEventReader = xmlInFactory.createXMLEventReader(new StreamSource(cleanRootFile));
            XMLEvent event = xmlEventReader.nextEvent();
            // Skip ahead in the input to the opening document element
            while (event.getEventType() != XMLEvent.START_ELEMENT) {
                event = xmlEventReader.nextEvent();
            }

            do {
                xmlEventWriter.add(event);
                event = xmlEventReader.nextEvent();
            } while (event.getEventType() != XMLEvent.END_DOCUMENT);
            xmlEventReader.close();
            
            if(cleanRootFile.exists()){
            	FileUtils.forceDelete(cleanRootFile);
            }
        }

        xmlEventWriter.add(xmlEventFactory.createEndElement("", null, "rootSet"));
        xmlEventWriter.add(xmlEventFactory.createEndDocument());
		
        xmlEventWriter.flush();
		
        xmlEventWriter.close();
        outputWriter.close();
	}
	
	/**
	 * Will move the source File to the destination File.
	 * The Method will backup the dest File, copy source to
	 * dest, and then will delete the source and the backup.
	 * 
	 * @param source
	 *            File to be moved
	 * @param dest
	 *            File to be overwritten (does not matter if
	 *            non existent)
	 * @throws IOException
	 */
	private static void moveAndOverwrite(File source, File dest) throws IOException {
	    // Backup the src
	    File backup = getNonExistingTempFile(dest);
	    FileUtils.copyFile(dest, backup);
	    FileUtils.copyFile(source, dest);
	    if (!source.delete()) {
	        throw new IOException("Failed to delete " + source.getName());
	    }
	    if (!backup.delete()) {
	        throw new IOException("Failed to delete " + backup.getName());
	    }
	}

	/**
	 * Recursive Method to generate a FileName in the same
	 * Folder as the {@code inputFile}, that is not existing
	 * and ends with {@code _temp}.
	 * 
	 * @param inputFile
	 *            The FileBase to generate a Tempfile
	 * @return A non existing File
	 */
	private static File getNonExistingTempFile(File inputFile) {
	    File tempFile = new File(inputFile.getParentFile(), inputFile.getName() + "_temp");
	    if (tempFile.exists()) {
	        return getNonExistingTempFile(tempFile);
	    } else {
	        return tempFile;
	    }
	}
	
	private static File getXMLReadyFile(File inputFile, String replacement){
		File tempFile = getNonExistingTempFile(inputFile);
		
		// Replace invalid XML characters
		try{
			FileWriter fw = new FileWriter(tempFile,true);
			BufferedWriter bw = new BufferedWriter(fw);
			PrintWriter pw = new PrintWriter(bw);
			
			BufferedReader br = new BufferedReader(new FileReader(inputFile));
			String line;
			String cleanLine;
			while ((line = br.readLine()) != null) {
				// Clean the line
				cleanLine = cleanInvalidXmlChars(line, replacement);

				// Write the clean line to the temp file
				pw.println(cleanLine);
			}
			
			pw.close();
			br.close();
			
		}catch(IOException ioe){
			System.out.println("Exception occurred:");
	    	ioe.printStackTrace();
		}
		
		return tempFile;
	}
	
	/// <summary>
    /// This removes characters that are invalid for xml encoding
    /// </summary>
    /// <param name="text">Text to be encoded.</param>
    /// <returns>Text with invalid xml characters removed.</returns>
    private static String cleanInvalidXmlChars(String text, String replacement)
    {
        // From xml spec valid chars:
        // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]    
        // any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
        //String re = @"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]";
        String xml10pattern = "[^"
                + "\u0009\r\n"
                + "\u0020-\uD7FF"
                + "\uE000-\uFFFD"
                + "\ud800\udc00-\udbff\udfff"
                + "]";
        return text.replaceAll(xml10pattern, replacement);
    }
	
}
