package org.w3c.dbwg.wsdl.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class RunExamplesHttpPost {

	/**
	 * Call web service for each example
	 */

	private static String EXAMPLEURI = "http://www.w3.org/2002/ws/databinding/examples/6/09/";
	private static String EXAMPLENS = "http://www.w3.org/2002/ws/databinding/examples/6/09/";
	private static String TOOLKITS = "http://www.w3.org/2002/ws/databinding/edcopy/toolkits/";
	private Vector<String> basictests;
	private Vector<String> advancedtests;
	private Vector<String> pendingtests;
	private Vector<String> excludetests;
	private String exampleuri = null;
	private Hashtable<String, String> extable = null;
	private Hashtable<String, String> instex = null;
	private int totalbasic, totaladvanced, totalpending, excludedbasic, excludedadvanced, excludedpending;
	private static final String logname = "<log toolkit=\"axis_13\""
			+ " examples=\"http://www.w3.org/2002/ws/databinding/examples/6/09/\""
			+ " xmlns=\"http://www.w3.org/2002/ws/databinding/log/6/09/\""
			+ " xmlns:ex=\"*invalid*\">";
	private static final String close_logname = "</log>";
	private File resultsFile = new File("output.xml");
	private static BufferedWriter results = null;

	/***************************************************************************
	 * Reads all the examples and classifies as advanced or basic. Build a list
	 * of all the examples relative to basic or advanced
	 **************************************************************************/

	private void initTests(String toolkit) {
		InputStream is = null;
		URL tests = null;
		URL test = null;
		URL excludes = null;
		String examplefile, testfile, id, classification;
		int i;

		System.err.println("*** remove excludes ");
		String excludesFile = TOOLKITS + toolkit + "/toolkit.xml";  
		try {
			excludes = new URL(excludesFile);
		} catch (MalformedURLException muex) {
			muex.printStackTrace();
			System.exit(1);
		}
		try {
			is = excludes.openStream();
		} catch (IOException ioex) {
			ioex.printStackTrace();
			System.exit(1);
		}
		DOMParser parser = new DOMParser();
		BufferedReader bf = new BufferedReader(new InputStreamReader(is));
		InputSource ins = new InputSource(bf);
		try {
			parser.parse(ins);
		} catch (org.xml.sax.SAXException saxex) {
			saxex.printStackTrace();
			System.exit(1);
		} catch (IOException ioex) {
			ioex.printStackTrace();
			System.exit(1);
		}
		Document exdoc = parser.getDocument();
		NodeList excludesNL = exdoc.getElementsByTagName("exclude");
		int totalExcludes = excludesNL.getLength();
		System.err.println("total excludes for toolkit " + toolkit + "="+ totalExcludes);
		excludetests = new Vector<String>();
		for (i = 0; i < totalExcludes; i++) {
			Element exclude = (Element) excludesNL.item(i);
			String excludeExample = exclude.getFirstChild().getNodeValue();
			excludetests.add(excludeExample);
		}
		
		// get examples 
		if (exampleuri == null) {
			examplefile = EXAMPLEURI + "examples.xml";
		} else {
			examplefile = exampleuri + "examples.xml";
		}
		System.err
				.println("*** fetching examples.xml to extract the number of tests");
		try {
			tests = new URL(examplefile);
		} catch (MalformedURLException muex) {
			muex.printStackTrace();
			System.exit(1);
		}
		try {
			is = tests.openStream();
		} catch (IOException ioex) {
			ioex.printStackTrace();
			System.exit(1);
		}
		parser = new DOMParser();
		bf = new BufferedReader(new InputStreamReader(is));
		ins = new InputSource(bf);
		try {
			parser.parse(ins);
		} catch (org.xml.sax.SAXException saxex) {
			saxex.printStackTrace();
			System.exit(1);
		} catch (IOException ioex) {
			ioex.printStackTrace();
			System.exit(1);
		}
		exdoc = parser.getDocument();
		NodeList inl = exdoc.getElementsByTagNameNS(EXAMPLENS, "instance");
		int totalTestsCount = inl.getLength();

		System.err.println("*** got " + totalTestsCount + " tests total");
		NodeList nl = exdoc.getElementsByTagNameNS(EXAMPLENS, "example");
		extable = new Hashtable<String, String>();
		for (i = 0; i < nl.getLength(); i++) {
			String exname = ((Element) nl.item(i)).getAttribute("xml:id");
			// now go to exampleuri/exname/exname-patterns.xml
			// and fetch <detected... status>
			if (exampleuri == null) {
				testfile = EXAMPLEURI + exname + "/" + exname + "-patterns.xml";
			} else {
				testfile = exampleuri + exname + "/" + exname + "-patterns.xml";
			}
			try {
				test = new URL(testfile);
			} catch (MalformedURLException muex) {
				muex.printStackTrace();
				System.exit(1);
			}
			try {
				parser = new DOMParser();
				bf = new BufferedReader(
						new InputStreamReader(test.openStream()));
				ins = new InputSource(bf);
				parser.parse(ins);
			} catch (org.xml.sax.SAXException saxex) {
				saxex.printStackTrace();
				System.exit(1);
			} catch (IOException ioex) {
				ioex.printStackTrace();
				System.exit(1);
			}
			Document testdoc = parser.getDocument();
			NodeList tnl = testdoc.getElementsByTagName("detected");
			if (tnl.getLength() == 0) {
				System.err.println("Error, no info on pattern " + exname);
				System.exit(3);
			}
			classification = ((Element) tnl.item(0)).getAttribute("status");
			extable.put(exname, classification);
		}
		// now check the breakdown of all instances
		totalbasic = 0;
		excludedbasic = 0;
		totaladvanced = 0;
		excludedadvanced = 0;
		totalpending = 0;
		excludedpending = 0;
		basictests = new Vector<String>();
		advancedtests = new Vector<String>();
		pendingtests = new Vector<String>();

		instex = new Hashtable<String, String>();

		for (i = 0; i < inl.getLength(); i++) {
			Element testinstance = (Element) inl.item(i);
			Node parent = testinstance.getParentNode();
			// should be an example...
			id = ((Element) parent).getAttribute("xml:id");
			String instname = testinstance.getAttribute("xml:id");
			instex.put(instname, id);
			classification = extable.get(id);
			if (classification.equals("basic")) {
				if (!excluded(id)) {
					basictests.add(instname);
				} else {
					excludedbasic++;
				}
				totalbasic++;
			} else if (classification.equals("advanced")) {
				if (!excluded(id)) {
					advancedtests.add(instname);
				} else {
					excludedadvanced++;
				}
				totaladvanced++;
			} else if (classification.equals("pending")) {
				if (!excluded(id)) {
					pendingtests.add(instname);
				} else {
					excludedpending++;
				}
				totalpending++;
			} else {
				System.err.println("Wrong classification for " + id + ": "
						+ classification);
				totalpending++;
			}
		}
		System.err.println("Totalbasic=" + totalbasic);
		System.err.println("Excludedbasic=" + excludedbasic);
		System.err.println("Totaladvanced=" + totaladvanced);
		System.err.println("Excludedadvanced=" + excludedadvanced);
		System.err.println("Totalpending=" + totalpending);
		System.err.println("Excludedpending=" + excludedpending);

	}
	/*
	 * check to see if an example is excluded in toolkits.xml
	 */
	private boolean excluded(String id) {
		for (int i = 0; i < excludetests.size(); i++) {
			String ex = (String)excludetests.get(i);
			if (ex.equalsIgnoreCase(id)) {
				return true;
			}
		} 
		return false;
	}

	/*
	 * Perform the test for each web service
	 */
	private void performTests(String mode, String endpoint) {
		int i = 0;
		Enumeration enumer = null;
		if (mode.equalsIgnoreCase("basic")) {
			enumer = basictests.elements();
			System.err.println("++++BASIC EXAMPLES++++");
		}
		if (mode.equalsIgnoreCase("advanced")) {
			enumer = advancedtests.elements();
			System.err.println("++++ADVANCED EXAMPLES++++");
		}
		while (enumer.hasMoreElements()) {
			i++;
			String inst = (String) enumer.nextElement();
			String example = instex.get(inst);
			String fullURL = EXAMPLEURI + example + "/echo" + example + "-"
					+ inst + ".xml";
			System.err.println("item " + i + " " + inst);
			ServiceCall sc = callService(fullURL, example, inst, endpoint);
			logCall(sc);
		}
	}

	private ServiceCall callService(String fullURL, String example,
			String inst, String endpoint) {

		// This is the neatest way to call a web service but 
		// unfortunately javax.xml.soap.* classes add an empty
		// namespace binding to all elements which Axis does not like!
/*
		MessageFactory messageFactory = MessageFactory.newInstance();
		SOAPMessage message = messageFactory.createMessage();
		SOAPPart soapPart = message.getSOAPPart();
		SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
		SOAPBody soapBody = soapEnvelope.getBody();
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
//		Document doc = db.parse("http://www.w3.org/2002/ws/databinding/examples/6/09/StringElement/echoStringElement-StringElement01.xml");
		Document doc = db.parse("http://www.w3.org/2002/ws/databinding/examples/6/09/SequenceElementList/echoSequenceElementList-SequenceElementList01.xml");
		soapBody.addDocument(doc);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		message.writeTo(baos);
		String xmlRequest = new String(baos.toByteArray());
		String endpoint = "http://localhost:8181/databinding_axis13/services/Port";
		SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
		SOAPConnection con = factory.createConnection();
		SOAPMessage response = con.call(message, endpoint);			
		ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
		response.writeTo(baos2);
		String xmlResponse = new String(baos2.toByteArray());
		System.out.println("xmlRequest");
		System.out.println(xmlRequest);
		System.out.println("xmlResponse");
		System.out.println(xmlResponse);
*/			
		// revert back to constructing a SOAP meesage from scratch
		ServiceCall sc = new ServiceCall();
		sc.setExample(example);
		sc.setInstance(inst);
		try {
			URL exampleXml = new URL(fullURL);
			System.err.println(fullURL);
			InputStream is = exampleXml.openStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			String str = null;
			StringBuffer sb = new StringBuffer();
			while (null != ((str = br.readLine()))) {
				sb.append(str);
			}
			String message = sb.toString();
			// remove XML PI from message
			message = message.substring(message.indexOf(">")+1);
			String soapRequest = "<env:Envelope xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\""
					+ " xmlns:enc=\"http://schemas.xmlsoap.org/soap/encoding/\""
					+ "	xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
					+ "	xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
					+ " <env:Body>"
					+ message
					+ "</env:Body></env:Envelope>";
			// store request message
			sc.setRequest(soapRequest);
			// store request headers TO DO
			// sc.setRequestHeader(something);
			URL webservice = new URL(endpoint);
			HttpURLConnection httpCon = (HttpURLConnection) webservice
					.openConnection();
			httpCon.setAllowUserInteraction(false);
			httpCon.setDoOutput(true);
			httpCon.setDoInput(true);
			httpCon.setRequestProperty("Content-Type",
					"text/xml; charset=utf-8");
			httpCon.setRequestProperty("Content-Language", "en");
			httpCon.setRequestProperty("SOAPAction", "");
			httpCon.setRequestMethod("POST");
			OutputStream postStream = httpCon.getOutputStream();
			PrintWriter prnWriter = new PrintWriter(postStream);
			prnWriter.println(soapRequest);
			prnWriter.flush();
			prnWriter.close();
			int responseCode = httpCon.getResponseCode();
			String responseText = httpCon.getResponseMessage();
			// just in case an exception is thrown before we can store result
			sc.setResponse(responseText);
			System.err.println("Post response: " + responseText + " RC=" + responseCode + " for instance " + inst);
			InputStreamReader inputStreamReader = new InputStreamReader(httpCon
					.getInputStream());
			BufferedReader reader = new BufferedReader(inputStreamReader);
			String inputLine;
			StringBuffer responseMessage = new StringBuffer("");
			while ((inputLine = reader.readLine()) != null)
				responseMessage.append(inputLine);
			inputStreamReader.close();
			reader.close();
			String soapResponse = responseMessage.toString();
			// store response message
			soapResponse = soapResponse.replace("<?xml", "<?_xml");
			sc.setResponse(soapResponse);

			// store headers
			Map headers = httpCon.getHeaderFields();
			Iterator it = headers.keySet().iterator();
			StringBuffer buff = new StringBuffer();
			while (it.hasNext()) {
				String key = (String) it.next();
				buff.append(key + ": " + headers.get(key) + "\r\n");
			}
			sc.setResponseHeader(buff.toString());
		} catch (Exception e) {
			sc.setResponse(createSoapFault(sc.getResponse()));
			System.err.println("Error retrieving " + e.getMessage());
		}
		return sc;
	}

	/*
	 * Init the output log
	 */
	private void initLog() {
		try {
			results = new BufferedWriter(new FileWriter(resultsFile));
			results.write(logname);
			results.newLine();
			results.flush();
		} catch (Exception e) {
			System.err.println("Error writing output -" + e.getMessage());
			System.exit(1);
		}
	}

	/*
	 * End the output log
	 */
	private void endLog() {
		try {
			results.write(close_logname);
			results.newLine();
			results.flush();
			results.close();
		} catch (Exception e) {
			System.err.println("Error writing output -" + e.getMessage());
			System.exit(1);
		}
	}

	/*
	 * log the call details to output
	 */
	private void logCall(ServiceCall sc) {
		try {
			results.write("<call example=\"" + sc.getExample()
					+ "\" instance=\"" + sc.getInstance() + "\">");
			results.newLine();
			results.write("\t<request>");
			results.newLine();
			results.write("\t\t<head><![CDATA[");
			results.newLine();
			results.write(sc.getRequestHeader());
			results.write("\t\t]]>");
			results.newLine();
			results.write("\t\t</head>");
			results.newLine();
			results.write("\t\t<body>");
			results.newLine();
			results.write(sc.getRequest());
			results.newLine();
			results.write("\t\t</body>");
			results.newLine();
			results.write("\t</request>");
			results.newLine();
			results.write("\t<response>");
			results.newLine();
			results.write("\t\t<head><![CDATA[");
			results.newLine();
			results.write(sc.getResponseHeader());
			results.write("\t\t]]>");
			results.newLine();
			results.write("\t\t</head>");
			results.newLine();
			results.write("\t\t<body>");
			results.newLine();
			results.write(sc.getResponse());
			results.newLine();
			results.write("\t\t</body>");
			results.newLine();
			results.write("\t</response>");
			results.newLine();
			results.write("</call>");
			results.newLine();
			results.flush();

		} catch (Exception e) {
			System.err.println("Error writing output -" + e.getMessage());
			System.exit(1);
		}

	}
	// create soap fault from 500 server error
	private String createSoapFault(String error) {
		String soapFault = "<env:Envelope xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\""
				+ " xmlns:enc=\"http://schemas.xmlsoap.org/soap/encoding/\""
				+ "	xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
				+ "	xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
				+ " <env:Body>"
				+ " <env:Fault xmlns:n1=\"http://schemas.xmlsoap.org/soap/encoding/\""
		        + "  env:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
		        + " <faultcode>env:Server</faultcode>" 
		        + " <faultstring xsi:type=\"xsd:string\">"
		        + error
		        + " </faultstring>"
		        + " <detail>"
		        + error
		        + " </detail></env:Fault>"
				+ "</env:Body></env:Envelope>";
		return soapFault;
	}

	public static void main(String[] args) {
		RunExamplesHttpPost runner = new RunExamplesHttpPost();
		if (args.length!=3) {
			System.err.println("Please supply 3 parameters 1=WS endpoint, 2=basic/advanced 3=toolset");
			System.exit(1);
		}
		String endpoint = args[0];
		String mode = args[1];
		String toolkit = args[2];
		System.err.println("Run " + mode + " toolkit test for " + toolkit + " using endpoint " + endpoint);
		System.err.println("Any examples specified in toolkit.xml will be excluded from the test");
		runner.initTests(toolkit);
		runner.initLog();
		runner.performTests(mode, endpoint);
		runner.endLog();
	}
}
