package ca.ucalgary.services.util;

import org.w3c.dom.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.*;
import javax.xml.parsers.*;

public class IOUtils{

    private static DocumentBuilder docBuilder;
    private static Logger logger = Logger.getLogger(IOUtils.class.getName());    

    private static DocumentBuilder getDocBuilder(){
	if(docBuilder == null){
	    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	    dbf.setNamespaceAware(true);
	    try{
		docBuilder = dbf.newDocumentBuilder();
	    } catch(Exception e){
		e.printStackTrace();
	    }
	}
	return docBuilder;
    }

    /**
     * Creates a SOAP envelope around the request payload
     */
    public static void writeToConnection(URLConnection conn, byte[] payload, String soapAction) throws Exception {
        conn.setDoInput(true); // should be the default, but set it just in case
        conn.setDoOutput(true);
	((HttpURLConnection)conn).setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "text/xml");
	//disable caching and keep-alive as these make no sense for Web Services
        conn.setRequestProperty("Connection", "close"); 
	conn.setRequestProperty("Cache-Control", "max-age=2");
	if(soapAction != null && soapAction.length() != 0){
            conn.setRequestProperty("SOAPAction", "\""+soapAction+"\"");
        }
        OutputStream out = new BufferedOutputStream(conn.getOutputStream(), 8192);
	out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><soapenv:Body>".getBytes());
        out.write(payload);
	out.write("</soapenv:Body></soapenv:Envelope>\n\n".getBytes());

        out.flush();
    }

    /**
     * Returns the response payload from a SOAP request
     */
    public static Node readFromConnection(HttpURLConnection conn) throws Exception {
	byte[] byteBufferChunk = new byte[100000];
	ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
	InputStream in = conn.getErrorStream();
	if (in == null) {
	    in = conn.getInputStream();
	}
	try{
	    for(int r = in.read(byteBufferChunk, 0, 100000);
		r != -1; 
		r = in.read(byteBufferChunk, 0, 100000)){
		byteBuffer.write(byteBufferChunk, 0, r);
	    }
	}catch(Exception e){
 	    System.err.println("Exception while reading from URL, will try to parse anyway: " + e.getMessage()); 
 	}
	try{
	    if(conn.getResponseCode() >= 400){
		throw new IOException("HTTP Error ("+conn.getResponseCode()+"): "+conn.getResponseMessage());
	    }
	} catch(Exception e){
	    if(e instanceof IOException){
		throw e;
	    }
	    else{
		logger.log(Level.SEVERE, "Could not get response code, but attempting to retrieve response", e);
	    }
	}
	String sb = byteBuffer.toString();

// 	java.io.LineNumberReader sr = new java.io.LineNumberReader(new java.io.InputStreamReader(in));
// 	StringBuilder sb = new StringBuilder();
// 	    for(String line = sr.readLine(); line != null; line = sr.readLine()){
// 		sb.append(line+"\n");
// 	    }
// 	} catch(Exception e){
// 	    System.err.println("Exception while reading from URL, will try to parse anyway: " + e.getMessage()); 
// 	}

	if(sb.length() < 10000){
	    logger.log(Level.INFO, sb);
	}
	Document respDoc = getDocBuilder().parse(new ByteArrayInputStream(byteBuffer.toByteArray()));
	NodeList bodyList = respDoc.getElementsByTagNameNS("http://schemas.xmlsoap.org/soap/envelope/", 
							   "Body");
	if(bodyList == null || bodyList.getLength() != 1){
	    throw new Exception("Could not find the Body element in the response:\n"+sb);
	}
	NodeList children = ((Element) bodyList.item(0)).getChildNodes();
        Node payLoadNode = null;
	if(children == null || children.getLength() == 0){
	    throw new Exception("Could not find the payload in the Body element of the response:\n" + sb);
	}
	else{
	    for(int i = 0; i < children.getLength(); i++){
		Node child = children.item(i);
		if(!(child instanceof Element)){
		    continue;
		}
		// check for Fault
		if("Fault".equals(child.getLocalName())){
		    throw new Exception("SOAP Fault at remote service ("+conn.getURL()+"):\n"+sb);
		}
		else{
		    return child; // there should be only one child
		}
	    }
	}
	throw new Exception("Could not find the payload element in the Body element of the response:\n" + sb);
    }

}
