package be.iminds.ilabt.jfed.highlevel.stitcher;

import be.iminds.ilabt.jfed.TestAuthoritiesModule;
import be.iminds.ilabt.jfed.lowlevel.TestCertHelper;
import be.iminds.ilabt.jfed.util.RFC3339Util;
import org.apache.commons.lang.StringEscapeUtils;

import java.util.Date;
import java.util.List;

/**
 * TestStitchingRspecHelper
 *
 * this test class is used for creating test RSpecs. (both requests and manifests)
 */
public class TestStitchingXmlRpcHelper {
        private final TestCertHelper testCertHelper;

        public final static String node1Name = "w1node";
        public final static String node2Name = "w2node";
        public final static String node3Name = "w3node";
        public final static String fakeNodeName = "fake";

        public final static String link1Name = "link";
        public final static String link2Name = "link2";
        public final static String fakeLinkName = "linkfake";

        public final static String fakeAmUrl = "https://www."+TestCertHelper.fakeHostname+":12369/protogeni/xmlrpc/am";
        public final static String fakeHopUrn = "urn:publicid:IDN+"+TestCertHelper.fakeHostname+"+interface+switch1:0.1";

        public final static String stitchIfaceAm1 = "stitchIface1";
        public final static String stitchIfaceFakeAm1 = "stitchIfaceFake";
        public final static String stitchIfaceAm2 = "stitchIface2";
        public final static String stitchIfaceAm3 = "stitchIface3";

        private final String stitchIfaceAm1Urn = "urn:publicid:IDN+"+TestCertHelper.authority1Hostname+"+interface+"+stitchIfaceAm1;
        private final String stitchIfaceAm2Urn = "urn:publicid:IDN+"+TestCertHelper.authority2Hostname+"+interface+"+stitchIfaceAm2;
        private final String stitchIfaceFakeAm1Urn = "urn:publicid:IDN+"+TestCertHelper.authority1Hostname+"+interface+"+stitchIfaceFakeAm1;

        public TestStitchingXmlRpcHelper(TestCertHelper testCertHelper) {
                this.testCertHelper = testCertHelper;
        }

        private static String createRspecTag(String type) {
                return "<rspec xmlns=\"http://www.geni.net/resources/rspec/3\" " +
                        "       type=\""+type+"\" " +
                        "       xmlns:emulab=\"http://www.protogeni.net/resources/rspec/ext/emulab/1\" " +
                        "       xmlns:delay=\"http://www.protogeni.net/resources/rspec/ext/delay/1\" " +
                        "       xmlns:jfed-command=\"http://jfed.iminds.be/rspec/ext/jfed-command/1\" " +
                        "       xmlns:client=\"http://www.protogeni.net/resources/rspec/ext/client/1\" " +
                        "       xmlns:jfed-ssh-keys=\"http://jfed.iminds.be/rspec/ext/jfed-ssh-keys/1\" " +
                        "       xmlns:jfed=\"http://jfed.iminds.be/rspec/ext/jfed/1\" " +
                        "       xmlns:sharedvlan=\"http://www.protogeni.net/resources/rspec/ext/shared-vlan/1\" " +
                        "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                        "       xsi:schemaLocation=\"http://www.geni.net/resources/rspec/3 http://www.geni.net/resources/rspec/3/request.xsd \">\n";
        }

        public String createStitchRequest() {
                return createStitchRequest(false, false, "");
        }

        private String createNodeRspec(String nodeName, String cmUrn, int index, int ifaceCount) {
                String res = "  <node client_id=\""+nodeName+"\" exclusive=\"true\" " +
                        "   component_manager_id=\""+cmUrn+"\">\n" +
                        "    <sliver_type name=\"raw-pc\"/>\n";
                for (int ifaceIndex = 0; ifaceIndex < ifaceCount; ifaceIndex++)
                        res += "    <interface client_id=\""+nodeName+":if"+ifaceIndex+"\">\n" +
                                "      <ip address=\"192.168."+ifaceIndex+"."+(index+1)+"\" netmask=\"255.255.255.0\" type=\"ipv4\"/>\n" + //note that IPs don't matter for any of the tests
                                "    </interface>\n";
                res += "  </node>\n" ;
                return res;
        }

        private String createLinkRspec(String linkName, String nodeName1, String nodeName2, String cmUrn1, String cmUrn2, int node1IfaceIndex, int node2IfaceIndex) {
                String res = "  <link client_id=\""+linkName+"\">\n" +
                        "    <component_manager name=\""+cmUrn1+"\"/>\n" +
                        "    <component_manager name=\""+cmUrn2+"\"/>\n";
                res += "    <interface_ref client_id=\""+nodeName1+":if"+node1IfaceIndex+"\"/>\n";
                res += "    <interface_ref client_id=\""+nodeName2+":if"+node2IfaceIndex+"\"/>\n";
                res += "    <property source_id=\""+nodeName2+":if"+node2IfaceIndex+"\" dest_id=\""+nodeName1+":if"+node1IfaceIndex+"\" capacity=\"10000\"/>\n";
                res += "    <property source_id=\""+nodeName1+":if"+node1IfaceIndex+"\" dest_id=\""+nodeName2+":if"+node2IfaceIndex+"\" capacity=\"10000\"/>\n";
            res += "  </link>\n" ;
                return res;
        }

        public String createStitchRequest(boolean hasFakeAm3, boolean hasNonInvolvedAm3, String extraXmlBeforeRspecClose) {
                return "<?xml version='1.0'?>\n" +
                        createRspecTag("request")+"\n"+
                        createNodeRspec(node1Name, TestCertHelper.am1Urn, 0, hasFakeAm3?2:1)+"\n"+
                        createNodeRspec(node2Name, TestCertHelper.am2Urn, 1, 1)+"\n"+
                        (hasFakeAm3 ? createNodeRspec(fakeNodeName, TestCertHelper.fakeAmUrn, 1, 1) + "\n" : "") +
                        (hasNonInvolvedAm3 ? createNodeRspec(node3Name, TestCertHelper.am3Urn, 1, 0) + "\n" : "") +
                        createLinkRspec(fakeLinkName, node1Name, node2Name, TestCertHelper.am1Urn, TestCertHelper.am2Urn, 0, 0)+"\n"+
                        (hasFakeAm3 ? createLinkRspec(fakeLinkName, node1Name, fakeNodeName, TestCertHelper.am1Urn, TestCertHelper.fakeAmUrn, 1, 0)+"\n" : "")+
                        extraXmlBeforeRspecClose+
                        "</rspec>";
        }

        public String create2IndepAmScsHttpReply(String hop1Available, String hop2Available,
                                                 String hop1Suggested, String hop2Suggested,
                                                 boolean hasFakeAm3, boolean hasNonInvolvedAm3) {
                return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                        "<methodResponse>\n" +
                        "<params>\n" +
                        "<param><value><struct>\n" +
                        "<member><name>code</name>\n" +
                        "<value><struct>\n" +
                        "<member><name>geni_code</name>\n" +
                        "<value><i4>0</i4></value></member>\n" +
                        "</struct></value></member>\n" +
                        "<member><name>value</name>\n" +
                        "<value><struct>\n" +
                        "<member><name>service_rspec</name>\n" +
                        "<value><string>"+
                        encodeXmlRpcString(createStitchRequest(hasFakeAm3, hasNonInvolvedAm3,
                                createScsStitchingTag(link1Name, stitchIfaceAm1Urn, stitchIfaceAm2Urn,
                                        hop1Available, hop2Available, hop1Suggested, hop2Suggested, hasFakeAm3)
                        )) + "\n" +
                        "</string></value></member>\n" +
                        "<member><name>workflow_data</name>\n" +
                        "<value><struct>\n" +
                        "<member><name>"+link1Name+"</name>\n" +
                        "<value><struct>\n" +
                        "<member><name>dependencies</name>\n" +
                        "<value><array><data>\n" +
                        "<value><struct>\n" +
                        "<member><name>aggregate_url</name>\n" +
                        "<value><string>"+TestAuthoritiesModule.urlA_am3+"</string></value></member>\n" +
                        "<member><name>aggregate_urn</name>\n" +
                        "<value><string>"+ TestCertHelper.am1Urn +"</string></value></member>\n" +
                        "<member><name>hop_urn</name>\n" +
                        "<value><string>"+stitchIfaceAm1Urn+"</string></value></member>\n" +
                        "<member><name>import_vlans</name>\n" +
                        "<value><boolean>0</boolean></value></member>\n" +
                        "</struct></value>\n" +
                        "<value><struct>\n" +
                        "<member><name>aggregate_url</name>\n" +
                        "<value><string>"+TestAuthoritiesModule.urlB_am2+"</string></value></member>\n" +
                        "<member><name>aggregate_urn</name>\n" +
                        "<value><string>"+ TestCertHelper.am2Urn +"</string></value></member>\n" +
                        "<member><name>hop_urn</name>\n" +
                        "<value><string>"+stitchIfaceAm2Urn+"</string></value></member>\n" +
                        "<member><name>import_vlans</name>\n" +
                        "<value><boolean>0</boolean></value></member>\n" +
                        "</struct></value>\n" +
                        "</data></array></value></member>\n" +
                        "</struct></value></member>\n" +
                        "</struct></value></member>\n" +
                        "</struct></value></member>\n" +
                        "</struct></value></param>\n" +
                        "</params>\n" +
                        "</methodResponse>\n";
        }

        public String create3DepAmScsHttpReply(String hop1Available, String hop2Available, String hop3Available,
                                               String hop1Suggested, String hop2Suggested, String hop3Suggested) {
                throw new RuntimeException("Not yet implemented");
        }

        public String create2DepAmScsHttpReply(String hop1Available, String hop2Available,
                                               String hop1Suggested, String hop2Suggested,
                                               boolean hasFakeAm3, boolean hasNonInvolvedAm3) {
                String res = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                        "<methodResponse>\n" +
                        "<params>\n" +
                        "<param><value><struct>\n" +
                        "<member><name>code</name>\n" +
                        "<value><struct>\n" +
                        "<member><name>geni_code</name>\n" +
                        "<value><i4>0</i4></value></member>\n" +
                        "</struct></value></member>\n" +
                        "<member><name>value</name>\n" +
                        "<value><struct>\n" +
                        "<member><name>service_rspec</name>\n" +
                        "<value><string>"+
                        encodeXmlRpcString(createStitchRequest(hasFakeAm3, hasNonInvolvedAm3,
                                createScsStitchingTag(link1Name, stitchIfaceAm1Urn, stitchIfaceAm2Urn,
                                        hop1Available, hop2Available, hop1Suggested, hop2Suggested, hasFakeAm3)
                        ))+"\n" +
                        "</string></value></member>\n" +
                        "<member><name>workflow_data</name>\n" +
                        "<value>\n"+
                        "                    <struct>\n";

                if (hasFakeAm3)
                        res += "<member><name>"+fakeLinkName+"</name>\n" +
                                "<value><struct>\n" +
                                "<member><name>dependencies</name>\n" +
                                "<value><array><data>\n" +
                                "<value><struct>\n" +
                                "<member><name>aggregate_url</name>\n" +
                                "<value><string>"+TestAuthoritiesModule.urlA_am2+"</string></value></member>\n" +
                                "<member><name>aggregate_urn</name>\n" +
                                "<value><string>"+ TestCertHelper.am1Urn +"</string></value></member>\n" +
                                "<member><name>dependencies</name>\n" +
                                "<value><array><data>\n" +
                                "<value><struct>\n" +
                                "<member><name>aggregate_url</name>\n" +
                                "<value><string>"+fakeAmUrl+"</string></value></member>\n" +
                                "<member><name>aggregate_urn</name>\n" +
                                "<value><string>"+TestCertHelper.fakeAmUrn +"</string></value></member>\n" +
                                "<member><name>hop_urn</name>\n" +
                                "<value><string>"+fakeHopUrn+"</string></value></member>\n" +
                                "<member><name>import_vlans</name>\n" +
                                "<value><boolean>0</boolean></value></member>\n" +
                                "</struct></value>\n" +
                                "</data></array></value></member>\n" +
                                "<member><name>hop_urn</name>\n" +
                                "<value><string>"+stitchIfaceFakeAm1Urn+"</string></value></member>\n" +
                                "<member><name>import_vlans</name>\n" +
                                "<value><boolean>1</boolean></value></member>\n" +
                                "</struct></value>\n" +
                                "</data></array></value></member>\n" +
                                "</struct></value></member>\n";

                res +=  "                      <member>\n" +
                        "                        <name>"+link1Name+"</name>\n" +
                        "                        <value>\n" +
                        "                          <struct>\n" +
                        "                            <member>\n" +
                        "                              <name>dependencies</name>\n" +
                        "                              <value>\n" +
                        "                                <array>\n" +
                        "                                  <data>\n" +
                        "                                    <value>\n" +
                        "                                      <struct>\n" +
                        "                                        <member>\n" +
                        "                                          <name>aggregate_url</name>\n" +
                        "                                          <value>\n" +
                        "                                            <string>"+TestAuthoritiesModule.urlB_am3+"</string>\n" +
                        "                                          </value>\n" +
                        "                                        </member>\n" +
                        "                                        <member>\n" +
                        "                                          <name>aggregate_urn</name>\n" +
                        "                                          <value>\n" +
                        "                                            <string>"+ TestCertHelper.am2Urn +"</string>\n" +
                        "                                          </value>\n" +
                        "                                        </member>\n" +
                        "                                        <member>\n" +
                        "                                          <name>dependencies</name>\n" +
                        "                                          <value>\n" +
                        "                                            <array>\n" +
                        "                                              <data>\n" +
                        "                                                <value>\n" +
                        "                                                  <struct>\n" +
                        "                                                    <member>\n" +
                        "                                                      <name>aggregate_url</name>\n" +
                        "                                                      <value>\n" +
                        "                                                        <string>"+TestAuthoritiesModule.urlA_am3+"</string>\n" +
                        "                                                      </value>\n" +
                        "                                                    </member>\n" +
                        "                                                    <member>\n" +
                        "                                                      <name>aggregate_urn</name>\n" +
                        "                                                      <value>\n" +
                        "                                                        <string>"+ TestCertHelper.am1Urn +"</string>\n" +
                        "                                                      </value>\n" +
                        "                                                    </member>\n" +
                        "                                                    <member>\n" +
                        "                                                      <name>hop_urn</name>\n" +
                        "                                                      <value>\n" +
                        "                                                        <string>"+stitchIfaceAm1Urn+"</string>\n" +
                        "                                                      </value>\n" +
                        "                                                    </member>\n" +
                        "                                                    <member>\n" +
                        "                                                      <name>import_vlans</name>\n" +
                        "                                                      <value>\n" +
                        "                                                        <boolean>0</boolean>\n" +
                        "                                                      </value>\n" +
                        "                                                    </member>\n" +
                        "                                                  </struct>\n" +
                        "                                                </value>\n" +
                        "                                              </data>\n" +
                        "                                            </array>\n" +
                        "                                          </value>\n" +
                        "                                        </member>\n" +
                        "                                        <member>\n" +
                        "                                          <name>hop_urn</name>\n" +
                        "                                          <value>\n" +
                        "                                            <string>"+stitchIfaceAm2Urn+"</string>\n" +
                        "                                          </value>\n" +
                        "                                        </member>\n" +
                        "                                        <member>\n" +
                        "                                          <name>import_vlans</name>\n" +
                        "                                          <value>\n" +
                        "                                            <boolean>1</boolean>\n" +
                        "                                          </value>\n" +
                        "                                        </member>\n" +
                        "                                      </struct>\n" +
                        "                                    </value>\n" +
                        "                                  </data>\n" +
                        "                                </array>\n" +
                        "                              </value>\n" +
                        "                            </member>\n" +
                        "                          </struct>\n" +
                        "                        </value>\n" +
                        "                      </member>\n" +
                        "                    </struct>\n" +
                        "                  </value>\n" +
                        "                </member>\n" +
                        "              </struct>\n" +
                        "            </value>\n" +
                        "          </member>\n" +
                        "        </struct>\n" +
                        "      </value>\n" +
                        "    </param>\n" +
                        "  </params>\n" +
                        "</methodResponse>\n";
                return res;
        }

        private String createScsStitchingTag(String linkName, String stitchIfaceHop1Urn, String stitchIfaceHop2Urn,
                                             String hop1Available, String hop2Available,
                                             String hop1Suggested, String hop2Suggested,
                                             boolean hasFakeAm3) {
                String res = "<stitching xmlns=\"http://hpn.east.isi.edu/rspec/ext/stitch/0.1/\" lastUpdateTime=\"20140719:18:11:43\">\n" +
                        "  <path id=\""+linkName+"\">\n" +
                        "    <hop id=\"1\">\n" +
                        "      <link id=\""+stitchIfaceHop1Urn+"\">\n" +
                        "        <trafficEngineeringMetric>10</trafficEngineeringMetric>\n" +
                        "        <capacity>10000</capacity>\n" +
                        "        <switchingCapabilityDescriptor>\n" +
                        "          <switchingcapType>l2sc</switchingcapType>\n" +
                        "          <encodingType>ethernet</encodingType>\n" +
                        "          <switchingCapabilitySpecificInfo>\n" +
                        "            <switchingCapabilitySpecificInfo_L2sc>\n" +
                        "              <interfaceMTU>1500</interfaceMTU>\n" +
                        "              <vlanRangeAvailability>"+hop1Available+"</vlanRangeAvailability>\n" +
                        "              <suggestedVLANRange>"+hop1Suggested+"</suggestedVLANRange>\n" +
                        "              <vlanTranslation>false</vlanTranslation>\n" +
                        "            </switchingCapabilitySpecificInfo_L2sc>\n" +
                        "          </switchingCapabilitySpecificInfo>\n" +
                        "        </switchingCapabilityDescriptor>\n" +
                        "      </link>\n" +
                        "      <nextHop>2</nextHop>\n" +
                        "    </hop>\n" +
                        "    <hop id=\"2\">\n" +
                        "      <link id=\""+stitchIfaceHop2Urn+"\">\n" +
                        "        <trafficEngineeringMetric>10</trafficEngineeringMetric>\n" +
                        "        <capacity>10000</capacity>\n" +
                        "        <switchingCapabilityDescriptor>\n" +
                        "          <switchingcapType>l2sc</switchingcapType>\n" +
                        "          <encodingType>ethernet</encodingType>\n" +
                        "          <switchingCapabilitySpecificInfo>\n" +
                        "            <switchingCapabilitySpecificInfo_L2sc>\n" +
                        "              <interfaceMTU>1500</interfaceMTU>\n" +
                        "              <vlanRangeAvailability>"+hop2Available+"</vlanRangeAvailability>\n" +
                        "              <suggestedVLANRange>"+hop2Suggested+"</suggestedVLANRange>\n" +
                        "              <vlanTranslation>false</vlanTranslation>\n" +
                        "            </switchingCapabilitySpecificInfo_L2sc>\n" +
                        "          </switchingCapabilitySpecificInfo>\n" +
                        "        </switchingCapabilityDescriptor>\n" +
                        "      </link>\n" +
                        "      <nextHop>null</nextHop>\n" +
                        "    </hop>\n" +
                        "  </path>\n";

                String fakeHopAvailable = "2000";
                String fakeHopSuggested = "2000";

                if (hasFakeAm3)
                        res += "<path id=\""+fakeLinkName+"\">\n" +
                                "    <hop id=\"1\">\n" +
                                "      <link id=\""+stitchIfaceFakeAm1Urn+"\">\n" +
                                "        <trafficEngineeringMetric>10</trafficEngineeringMetric>\n" +
                                "        <capacity>10000</capacity>\n" +
                                "        <switchingCapabilityDescriptor>\n" +
                                "          <switchingcapType>l2sc</switchingcapType>\n" +
                                "          <encodingType>ethernet</encodingType>\n" +
                                "          <switchingCapabilitySpecificInfo>\n" +
                                "            <switchingCapabilitySpecificInfo_L2sc>\n" +
                                "              <interfaceMTU>1500</interfaceMTU>\n" +
                                "              <vlanRangeAvailability>"+fakeHopAvailable+"</vlanRangeAvailability>\n" +
                                "              <suggestedVLANRange>"+fakeHopSuggested+"</suggestedVLANRange>\n" +
                                "              <vlanTranslation>false</vlanTranslation>\n" +
                                "            </switchingCapabilitySpecificInfo_L2sc>\n" +
                                "          </switchingCapabilitySpecificInfo>\n" +
                                "        </switchingCapabilityDescriptor>\n" +
                                "      </link>\n" +
                                "      <nextHop>2</nextHop>\n" +
                                "    </hop>\n" +
                                "    <hop id=\"2\">\n" +
                                "      <link id=\""+fakeHopUrn+"\">\n" +
                                "        <trafficEngineeringMetric>10</trafficEngineeringMetric>\n" +
                                "        <capacity>10000</capacity>\n" +
                                "        <switchingCapabilityDescriptor>\n" +
                                "          <switchingcapType>l2sc</switchingcapType>\n" +
                                "          <encodingType>ethernet</encodingType>\n" +
                                "          <switchingCapabilitySpecificInfo>\n" +
                                "            <switchingCapabilitySpecificInfo_L2sc>\n" +
                                "              <interfaceMTU>1500</interfaceMTU>\n" +
                                "              <vlanRangeAvailability>"+fakeHopAvailable+"</vlanRangeAvailability>\n" +
                                "              <suggestedVLANRange>"+fakeHopSuggested+"</suggestedVLANRange>\n" +
                                "              <vlanTranslation>false</vlanTranslation>\n" +
                                "            </switchingCapabilitySpecificInfo_L2sc>\n" +
                                "          </switchingCapabilitySpecificInfo>\n" +
                                "        </switchingCapabilityDescriptor>\n" +
                                "      </link>\n" +
                                "      <nextHop>null</nextHop>\n" +
                                "    </hop>\n" +
                                "  </path>";

                res += "</stitching>";
                return res;
        }

        public static String wrapSuccessStringReply(String s) {
                return wrapSuccessXmlRpcAMReply("<string>" + encodeXmlRpcString(s) + "</string>");
        }
        public static String wrapStructMember(String key, String value) {
                return "<member>\n" +
                        "  <name>"+key+"</name>\n" +
                        "  <value>"+value+"</value>\n" +
                        "</member>\n";
        }

        public static String wrapSuccessXmlRpcAMReply(String internal) {
                return "<?xml version=\"1.0\"?>\n" +
                        "<methodResponse>\n" +
                        "<params>\n" +
                        "<param><value>" +
                        "<struct>\n" +
                        "<member><name>value</name><value>"+
                        internal +
                        "</value>\n" +
                        "</member>\n" +
                        "<member><name>output</name><value><string></string></value></member>\n" +
                        "<member><name>code</name><value><struct>\n" +
                        "<member><name>geni_code</name><value><i4>0</i4></value></member>\n" +
                        "</struct></value>\n" +
                        "</member>\n" +
                        "</struct></value>\n" +
                        "</param>\n" +
                        "</params>\n" +
                        "</methodResponse>\n";
        }

        public static String wrapSuccessXmlRpcSAReply(String internal) {
                return "<?xml version=\"1.0\"?>\n" +
                        "<methodResponse>\n" +
                        "<params>\n" +
                        "<param><value>" +
                        "<struct>\n" +
                        "<member><name>value</name><value>"+
                        internal +
                        "</value>\n" +
                        "</member>\n" +
                        "<member><name>output</name><value><string></string></value></member>\n" +
                        "<member><name>code</name><value><i4>0</i4></value></member>\n" +
                        "</struct></value>\n" +
                        "</param>\n" +
                        "</params>\n" +
                        "</methodResponse>\n";
        }

        public static String wrapErrorReply(int code, String output) {
                return "<?xml version=\"1.0\"?>\n" +
                        "<methodResponse>\n" +
                        "<params>\n" +
                        "<param><value>" +
                        "<struct>\n" +
                        "<member><name>output</name><value><string>"+output+"</string></value></member>\n" +
                        "<member><name>code</name><value><struct>\n" +
                        "<member><name>geni_code</name><value><i4>"+code+"</i4></value></member>\n" +
                        "</struct></value>\n" +
                        "</member>\n" +
                        "</struct></value>\n" +
                        "</param>\n" +
                        "</params>\n" +
                        "</methodResponse>\n";
        }

        public static String wrapTestErrorReply(String output) {
                int code = 7;
                return "<?xml version=\"1.0\"?>\n" +
                        "<methodResponse>\n" +
                        "<params>\n" +
                        "<param><value>" +
                        "<struct>\n" +
                        "<member><name>output</name><value><string>"+output+"</string></value></member>\n" +
                        "<member><name>code</name><value><struct>\n" +
                        "<member><name>geni_code</name><value><i4>"+code+"</i4></value></member>\n" +
                        "</struct></value>\n" +
                        "</member>\n" +
                        "</struct></value>\n" +
                        "</param>\n" +
                        "</params>\n" +
                        "</methodResponse>\n";
        }

        public static class SliverInfo {
                private final String operationalStatus;
                private final String allocationStatus;
                private final String testInternalStatus;
                private final Date expires;
                private final String sliverUrn;
                private final String error;

                public SliverInfo(String allocationStatus, String operationalStatus, String testInternalStatus, Date expires, String sliverUrn, String error) {
                        this.operationalStatus = operationalStatus;
                        this.allocationStatus = allocationStatus;
                        this.testInternalStatus = testInternalStatus;
                        this.expires = expires;
                        this.sliverUrn = sliverUrn;
                        this.error = error;
                        //geni_resource_status
                }

                public String wrapStruct() {
                        String res = "";
                        if (operationalStatus != null)
                                res += "    "+wrapXmlRpcMember("geni_operational_status", "<string>"+encodeXmlRpcString(operationalStatus)+"</string>")+"\n";
                        if (testInternalStatus != null)
                                res += "    "+wrapXmlRpcMember("test_internal_status", "<string>"+encodeXmlRpcString(testInternalStatus)+"</string>")+"\n";
                        if (allocationStatus != null)
                                res += "    "+wrapXmlRpcMember("geni_allocation_status", "<string>"+encodeXmlRpcString(allocationStatus)+"</string>")+"\n";
                        if (expires != null)
                                res += "    "+wrapXmlRpcMember("geni_expires", "<string>"+encodeXmlRpcString(RFC3339Util.dateToRFC3339String(expires))+"</string>")+"\n";
                        if (sliverUrn != null)
                                res += "    "+wrapXmlRpcMember("geni_sliver_urn", "<string>"+encodeXmlRpcString(sliverUrn)+"</string>")+"\n";
                        if (error != null)
                                res += "    "+wrapXmlRpcMember("geni_error", "<string>"+encodeXmlRpcString(error)+"</string>")+"\n";
                        //geni_resource_status
                        return "<struct>\n"+
                                res+
                                "</struct>\n";
                }
        }

        private static String wrapXmlRpcMember(String membername, String internal) {
                return  "<member>\n" +
                        "  <name>"+membername+"</name>\n" +
                        "  <value>\n" +
                        "    "+internal+"\n" +
                        "  </value>\n" +
                        "</member>\n";
        }

        public static String wrapSuccessAmv3Reply(String rspec, String sliceUrn, List<SliverInfo> sliverInfos) {
                String internal = "<struct>";
                if (rspec != null) {
                        internal += "                <member>\n" +
                                "                  <name>geni_rspec</name>\n" +
                                "                  <value>\n" +
                                "                    <string>"+encodeXmlRpcString(rspec)+"</string>\n" +
                                "                  </value>\n" +
                                "                </member>\n";
                }
                if (sliceUrn != null) {
                        internal += "                <member>\n" +
                                "                  <name>geni_urn</name>\n" +
                                "                  <value>\n" +
                                "                    <string>"+encodeXmlRpcString(sliceUrn)+"</string>\n" +
                                "                  </value>\n" +
                                "                </member>\n";
                }
                //see example stitching at https://flsmonitor.fed4fire.eu/testresults.html?testdefinitionname=stitch
                //https://flsmonitor.fed4fire.eu/results/stitch/392/2015/1/26/10:5:4.721/result.html
                if (sliverInfos != null) {
                        internal += "                <member>\n" +
                                "                  <name>geni_slivers</name>\n" +
                                "                  <value>\n" +
                                "                    <array>\n" +
                                "                      <data>\n";

                        for (SliverInfo sliverInfo : sliverInfos) {
                                internal +=   "                        <value>\n" +
                                        "                        "+sliverInfo.wrapStruct()+
                                        "                        </value>\n";
                        }

                        internal +=  "                      </data>\n" +
                                "                    </array>\n" +
                                "                  </value>\n" +
                                "                </member>\n";
                }

                internal += "</struct>";

                return wrapSuccessXmlRpcAMReply(internal);
        }
        public static String wrapSuccessAmv3SliverInfoReply(List<SliverInfo> sliverInfos) {
                String internal = "<array>";
                if (sliverInfos != null && !sliverInfos.isEmpty()) {
                        internal += "<value><data>";
                        for (SliverInfo sliverInfo : sliverInfos) {
                                internal +=  sliverInfo.wrapStruct()+"\n";
                        }
                        internal += "</data></value>";
                }

                internal += "</array>";

                return wrapSuccessXmlRpcAMReply(internal);
        }

        public static String encodeXmlRpcString(String in) {
                return StringEscapeUtils.escapeXml(in);
        }
}
