/*
 * Delivered to the U.S. Government in Accordance with Contract FA8650-10-C-7080.
 *
 * EXPORT CONTROLLED
 * ECCN#   EAR99
 * Warning: This document contains technical data which is controlled under the
 * Bureau of Industry & Security's Export Administration Regulations and may not be
 * exported or re-exported without authorization by the Bureau of Industry & Security.
 * Diversion contrary to US law is prohibited.
 *
 * (c) 2011 United Technologies Corp.
 * Developed with the sponsorship of the Defense Advanced Research Projects Agency (DARPA).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this data,
 * including any software or models in source or binary form, as well as any drawings, 
 * specifications, and documentation (collectively "the Data"), to deal in the Data without 
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Data, and to permit persons to whom the Data
 * is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Data.
 *
 * THE DATA IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, SPONSORS, DEVELOPERS, CONTRIBUTORS, OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE DATA OR THE
 * USE OR OTHER DEALINGS IN THE DATA.
 */
package meta.soa;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.rmi.Naming;
import java.util.Date;

import meta.rmi.Service;

public class SOAHandler implements Runnable {
	private static final String CONTENT_LENGTH = "Content-Length: ";
	// The value for content length begins at position 15
	private static final int CONTENT_LENGTH_BEGINS = 15;
	private static final int SC_OK = 200;
	private static final int SC_BAD_REQUEST = 400;
	private static final int SC_INTERNAL_SERVER_ERROR = 500;
	private static final int SC_NOT_IMPLEMENTED = 501;
	private Socket socket;
	private BufferedReader in;
	private PrintWriter out;
	private int contentLengthOfRequest;
	private int status;
	private String serviceURI;
	private String serviceRUN;
	private String request;

	public SOAHandler(Socket socket) {
		this.socket = socket;
		contentLengthOfRequest = -1;
	}

	private void reply(String s, int code) throws IOException {
		String x = "<result success=\"true\"></result>";
		out.print(x);
		out.flush();
	}
	private void reply2(String s, int code) throws IOException {
		String msg = "HTTP/1.0 200 OK";
		if (code == 500)
			msg = "HTTP/1.0 500 Server Error";
		out.println(msg);
		out.println("Server: SOAServer/1.0");
		out.println("Content-Type: text/html");
		String contentLength = String
				.format("Content-Length: %d\n", s.length());
		out.println(contentLength);
		out.print(s);
		out.flush();
	}
	private void log() {
	   String m = String.format("%s : %s \n %s ",new Date(),serviceURI,request);
	   System.out.println(m);
	}
	/**
	 *  rmi://localhost/repository
	 */
	private void invoke() throws Exception  {
		  Service srv = (Service) Naming.lookup(serviceURI);
		  String xml = srv.invoke(request);
		  out.print(xml);
		  out.flush();
	}
	/**
	 *  run://host:port
	 *  POST run://10.30.68.101:9000
	 */
	private void invokeRun() throws Exception {
		  String [] tokens = serviceRUN.split(":");
		  String host = tokens[1].substring(2);
		  String p = tokens[2].trim();
		  int port = Integer.parseInt(p);
		  Socket sock = new Socket(host,port);
		  try {
			    BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
			    wr.write(request);
			    wr.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
	}
	/**
	 * 
	 */
	private void parseRequestHeader(String header) {
		System.out.println(header);
		if (header.startsWith("POST")) {
			String [] tokens = header.split("\\s");
			serviceURI = null;
			serviceRUN = null;
			if ( tokens[1].startsWith("rmi:")) {
				serviceURI = tokens[1];
			}
			if ( tokens[1].startsWith("run:")) {
				serviceRUN = tokens[1];
			}
		}
		if (header.startsWith(CONTENT_LENGTH)) {
			String s = header.substring(CONTENT_LENGTH_BEGINS);
			s = s.trim();
			try {
				contentLengthOfRequest = Integer.parseInt(s);
			} catch (NumberFormatException e) {
				// content length stays as -1 and we error out
				// after reading in the rest of the headers
			}
		}
	}
    private int getRequestBody() {
    	if ( contentLengthOfRequest == -1 ) return SC_BAD_REQUEST;
    	StringBuilder sb = new StringBuilder();
    	int count = 0;
    	while ( count < contentLengthOfRequest) {
    		try {
				int c = in.read();
				if ( c == -1)  {
					System.out.println("Debug:End of Stream?");
					break;
				}
				char ch = (char) c;
				sb.append(ch);
			} catch (IOException e) {
				return SC_BAD_REQUEST;
			}
    		count++;
    	}
    	request = sb.toString();
    	System.out.println(request);
    	return SC_OK;
    }
	public void run() {
		try {
			  in = new BufferedReader(new InputStreamReader(socket
					.getInputStream()));
			out = new PrintWriter(socket.getOutputStream(), true);
			boolean done = false;
			while (!done) {
				String str = in.readLine();
				parseRequestHeader(str);
				if (str == null)
					done = true;
				if (str.equals("")) {
					done = true;
				}
			}
            status = getRequestBody();
            if ( serviceURI != null) {
            	invoke();
            }
            if ( serviceRUN !=null) {
            	invokeRun();
            }
            log();
			if ( serviceURI == null) reply("<html></html>", status);
			socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
