package com.rackspace.cloud.api.docs;

import com.rackspace.papi.components.translation.xproc.Pipeline;
import com.rackspace.papi.components.translation.xproc.PipelineInput;
import com.rackspace.papi.components.translation.xproc.calabash.CalabashPipelineBuilder;
import org.apache.maven.plugin.MojoExecutionException;
import org.xml.sax.InputSource;

import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class CalabashHelper {
    private static Source run(final String pipelineURI, final InputSource inputSource, final Map<String, Object> map) throws FileNotFoundException {
        Pipeline pipeline = new CalabashPipelineBuilder(false, true).build(pipelineURI);

//        <c:param-set xmlns:c="http://www.w3.org/ns/xproc-step">
//            <c:param name="username" namespace="" value="user"/>
//            <c:param name="host" namespace="" value="http://example.com/"/>
//            <c:param name="password" namespace="" value="pass"/>
//        </c:param-set>
        
        StringBuffer strBuff = new StringBuffer("<c:param-set xmlns:c=\"http://www.w3.org/ns/xproc-step\">");
        strBuff.append("");
        
        
        if(null!=map){
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String rawValue;
                if (entry.getValue() instanceof File) {
                    rawValue = ((File)entry.getValue()).toURI().toString();
                } else if (entry.getValue() instanceof URI || entry.getValue() instanceof String) {
                    rawValue = entry.getValue().toString();
                } else if (entry.getValue() instanceof URL) {
                    rawValue = ((URL)entry.getValue()).toExternalForm();
                } else if (entry.getValue() != null) {
                    throw new UnsupportedOperationException(String.format("The map cannot contain values of type %s.", entry.getValue().getClass()));
                } else {
                    // ignore nulls
                    continue;
                }

                strBuff
                    .append("<c:param name=\"")
                    .append(entry.getKey())
                    .append("\" namespace=\"\" value=\"")
                    .append(rawValue)
                    .append("\"/>");
            }
        }

        strBuff.append("</c:param-set>");
        String params=strBuff.toString();
        final InputStream paramsStream = new ByteArrayInputStream(params.getBytes());
        
        //System.out.println("~!~!~!~!~!~!~!~!~!Sending: \n"+params+"\n");
        List<PipelineInput> pipelineInputs = new ArrayList<PipelineInput>() {{
            add(PipelineInput.port("source", inputSource));
            add(PipelineInput.port("parameters", new InputSource(paramsStream)));
        }};
        
        pipeline.run(pipelineInputs);
        List<Source> sources = pipeline.getResultPort("result"); // result of xinclude;

        return sources.get(0);
    }

    /**
     * Creates a {@link Source} for use in a Calabash pipeline.
     *
     * <p>The {@code map} values may instances of {@link String}, {@link File},
     * {@link URI}, or {@link URL}.</p>
     *
     * <ul>
     * <li>{@link String}: These are passed to the pipeline unchanged.</li>
     * <li>{@link URI}: The results of {@link URI#toString()} is passed to the pipeline.</li>
     * <li>{@link File}: These are treated as URIs by calling {@link File#toURI()}.</li>
     * <li>{@link URL}: The result of {@link URL#toExternalForm()} is passed to the pipeline.</li>
     * <li>{@code null}: This is passed to the pipeline as an empty string.</li>
     * </ul>
     *
     * @param source
     * @param pipelineURI
     * @param map
     * @return
     * @throws MojoExecutionException 
     */
    public static Source createSource(Source source, String pipelineURI, Map<String, Object> map)
            throws MojoExecutionException {

        try {
            if (!(source instanceof SAXSource)) {
                throw new MojoExecutionException("Expecting a SAXSource");
            }
            SAXSource saxSource = (SAXSource) source;

            return run(pipelineURI, saxSource.getInputSource(),map);
        } catch (FileNotFoundException e) {
            throw new MojoExecutionException("Failed to find source.", e);
        }
        
    }

}

@@ -2366,16 +2366,35 @@
 .append(
+escapeXmlAttribute(
 entry.ge
@@ -2400,16 +2400,17 @@
 etKey())
+)
 %0A       
@@ -2488,16 +2488,35 @@
 .append(
+escapeXmlAttribute(
 rawValue
@@ -2516,16 +2516,17 @@
 awValue)
+)
 %0A       
@@ -3237,24 +3237,320 @@
 (0);%0A    %7D%0A%0A
+    private static String escapeXmlAttribute(String value) %7B%0A        if (value == null) %7B%0A            return %22%22;%0A        %7D%0A%0A        return value%0A            .replace(%22&%22, %22&amp;%22)%0A            .replace(%22%5C%22%22, %22&quot;%22)%0A            .replace(%22'%22, %22&apos;%22)%0A            .replace(%22%25%22, %22&#37;%22);%0A    %7D%0A%0A
     /**%0A    

Make sure to escape parameter values since they are pass as XML attributes