package push;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.*;

/**
 * <code>PushInitiator</code> class implements a simple push initiator. It can be used to create and 
 * send messages to push proxy gateway (ppg)
 *
 */
public final class PushInitiator {

    /* xml version */
    private final static String XML_VERSION = "<?xml version=\"1.0\"?>"; 
    /* Push Access Protocol (PAP) document type */
    private final static String PAP_DOCTYPE = 
	"<!DOCTYPE pap PUBLIC \"-//WAPFORUM//DTD PAP 1.0//EN\" \"http://www.wapforum.org/DTD/pap_1.0.dtd\" >";
    /* content type of the push request multipart body */
    private final static String CONTENT_TYPE = 
	"multipart/related; boundary=multipart-boundary; type=\"application/xml\"";

    /* Service Indication (SI) document type */
    private static final String SI_DOCTYPE = "<!DOCTYPE si PUBLIC \"-//WAPFORUM//DTD SI 1.0//EN\" " +
	"\"http://www.wapforum.org/DTD/si.dtd\">";

    private static int pushId = 0;

    /* PPG url where the pap message is to be sent  */
    private final URL ppgUrl;

    /* Some push proxy gateways may also require the authentication of the push initiator. 
       For instance, the push initiator imay be required to send an HTTP "authorization" header 
       in each request */
    private final String authorization;

    /**
     * Creates a new <code>PushInitiator</code> instance.
     *
     * @param ppgUrl an <code>URL</code> value
     */
    public PushInitiator(URL ppgUrl) {
	this.ppgUrl = ppgUrl;
	authorization = null;
    }

    /**
     * Creates a new <code>PushInitiator</code> instance.
     *
     * @param ppgUrl an <code>URL</code> value
     * @param authorization a <code>String</code> value
     */
    public PushInitiator(URL ppgUrl, String authorization) {
	this.ppgUrl = ppgUrl;
	this.authorization = authorization;
	System.out.println("Push Initiator created: ppgUrl = " + ppgUrl);
    }

    /**
     * <code>sendPushMessage</code> sends push message to ppg
     *
     * @param clientAddress a <code>String</code> value
     * @param addressType a <code>String</code> value
     * @param message a <code>String</code> value
     * @param mimeType a <code>String</code> value
     * @exception Exception if an error occurs
     */
    public void sendPushMessage(String clientAddress, String addressType, 
				String message, String mimeType) throws Exception {
	HttpURLConnection http = (HttpURLConnection)ppgUrl.openConnection();
	String ppgServer = ppgUrl.getHost();
	http.setDoInput(true);
	http.setRequestProperty("Content-Type", CONTENT_TYPE);
	if(authorization != null) {
	    if(!authorization.trim().equals(""))
		http.setRequestProperty("Authorization", authorization);
	}
	http.setDoOutput(true);
	PrintWriter out = new PrintWriter(new BufferedOutputStream(http.getOutputStream()));
	out.println("--multipart-boundary");
	out.println("Content-type: application/xml");
	out.println();
	out.println(createPapMessage(clientAddress, addressType, ppgServer, genPushId(), null));
	out.println();
        out.println("--multipart-boundary");
        out.println("Content-type: " + mimeType);
        out.println();
	out.println(message);
	out.println();
	out.println("--multipart-boundary--");
	out.flush();
	out.close();
	http.connect();

	/* finally, read and debug the response received from PPG */
	try {
	    debugResponse(http);
	}
	catch (Exception e) { 
	    System.err.println("Error in receiving the response!"); 
	}
    }

    /**
     * <code>createPapMessage</code> creates a Push Access Protocol type message
     *
     * @param address a <code>String</code> value
     * @param addressType a <code>String</code> value
     * @param ppgServer a <code>String</code> value
     * @param pushId a <code>String</code> value
     * @param notificationTo a <code>String</code> value
     * @return a <code>String</code> value
     */
    public static String createPapMessage(String address, String addressType, 
					  String ppgServer, String pushId, String notificationTo) {
	return XML_VERSION + "\n" + PAP_DOCTYPE + "\n" +
	    "<pap product-name=\"Mobile Zoo Push Initiator\">\n" +
	    "<push-message push-id=\"" + pushId + 
	    (notificationTo != null ? " request-notification-to=\"" + notificationTo : "") + "\">\n" +
	    "<address address-value=\"WAPPUSH=" + address + "/TYPE=" + addressType + 
		 "@" + ppgServer + "\"/>\n"  + 
	    "</push-message>\n</pap>";
    }

    /**
     * <code>createSiMessage</code> creates a service indication type message
     *
     * @param url a <code>String</code> value
     * @param created a <code>String</code> value
     * @param expires a <code>String</code> value
     * @param message a <code>String</code> value
     * @return a <code>String</code> value
     */
    public static String createSiMessage(String url, String sid, String created, String expires, 
					 String action, String message) {
	return XML_VERSION + SI_DOCTYPE +
	    "<si>\n" +
	    "<indication href=\"" + url + "\"" +
	    (sid != null ? " si-id=\"" + sid + "\"" : "") +
	    (created != null ? " created=\"" + created + "\"" : "") +
	    (action != null ? " action=\"" + action + "\"" : "") + 
	    (expires != null ? " expires=\"" + expires + "\"" : "") + ">" + 
	    message + 
	    "</indication>\n</si>";
    }

    /**
     * Debug response
     *
     * @param http a <code>HttpURLConnection</code> value
     * @exception Exception if an error occurs
     */
    private void debugResponse(HttpURLConnection http) throws Exception {
	System.out.println(http.getResponseMessage());
	String headerName;
	int i=0;
	while((headerName = http.getHeaderFieldKey(i++)) != null) {
	    System.out.println("headerName" + ": " + http.getHeaderField(headerName));
	}
	System.out.println();
	InputStreamReader in = new InputStreamReader(new BufferedInputStream(http.getInputStream()));
	int c;
	while((c = in.read()) != -1) System.out.print((char)c);
	System.out.println();
    }

    /**
     * <code>genPushId</code> generates an unique push id
     *
     * @return a <code>String</code> value
     */
    private String genPushId() {
	pushId %= 100000;
	return System.currentTimeMillis() + ":" +  pushId++;
    }
}



