package it.gotoandplay.smartfoxclient.http;

import it.gotoandplay.smartfoxclient.SFSEventDispatcher;
import it.gotoandplay.smartfoxclient.SmartFoxClient;
import it.gotoandplay.smartfoxclient.data.SFSObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.logging.Level;
import java.util.logging.Logger;

public class HttpConnection extends SFSEventDispatcher {
	private static final String CONN_LOST = "ERR#01";
	private static final String DISCONNECT = "disconnect";
	private static final String HANDSHAKE = "connect";

	public static final char HANDSHAKE_TOKEN = '#';

	private static final String paramName = "sfsHttp";
	private static final String servletUrl = "BlueBox/HttpBox.do";

	private URL blueboxURL;
	private final int buffersize = 150000;
	private final IHttpProtocolCodec codec;
	private boolean connected;
	private String ipAddr;
	private int port;

	private String sessionId;
	private String webUrl;

	public HttpConnection () {
		connected = false;
		codec = new RawProtocolCodec ();
	}

	public void close () {
		send (HttpConnection.DISCONNECT);
	}

	public void connect (final String addr) {
		connect (addr, 8080);
	}

	public void connect (final String addr, final int portNumber) {
		try {
			ipAddr = addr;
			port = portNumber;
			webUrl = "http://" + ipAddr + ":" + port + "/"
					+ HttpConnection.servletUrl;
			sessionId = null;
			blueboxURL = new URL (webUrl);

			send (HttpConnection.HANDSHAKE);
		} catch (final MalformedURLException ex) {
			Logger.getLogger (SmartFoxClient.class.getName ()).log (
					Level.SEVERE,
					"Malformed BlueBox URL: " + ex.getMessage (), ex);
		}
	}

	public String getSessionId () {
		return sessionId;
	}

	public boolean isConnected () {
		return connected;
	}

	public void send (final String message) {
		if (connected || !connected
				&& message.equals (HttpConnection.HANDSHAKE)
				|| !connected && message.equals ("poll")) {
			try {
				final String data = HttpConnection.paramName
						+ "="
						+ URLEncoder.encode (codec.encode (sessionId,
								message), "UTF-8");

				if (!message.equals ("poll")) {
					Logger.getLogger (SmartFoxClient.class.getName ())
							.log (Level.INFO, "[ Send ]: " + data);
				}

				final HttpURLConnection connection = (HttpURLConnection) blueboxURL
						.openConnection ();
				connection.setRequestMethod ("POST");
				connection
						.setRequestProperty ("Content-Type",
								"application/x-www-form-urlencoded; charset=UTF-8");
				connection.setDoOutput (true);
				final OutputStreamWriter out = new OutputStreamWriter (
						connection.getOutputStream ());
				out.write (data);
				out.flush ();
				out.close ();

				final BufferedReader in = new BufferedReader (
						new InputStreamReader (connection
								.getInputStream ()), buffersize);
				final char [] cbuf = new char [1];
				final StringBuilder sb = new StringBuilder ();
				while (in.read (cbuf) != -1) {
					sb.append (cbuf);
				}
				final String response = sb.toString ();

				if (response.length () > 0) {
					HttpEvent event;
					final SFSObject params = new SFSObject ();

					// handle handshake
					if (response.charAt (0) == HttpConnection.HANDSHAKE_TOKEN) {
						// Init the sessionId
						if (sessionId == null) {
							sessionId = codec.decode (response);
							connected = true;

							params.put ("sessionId", sessionId);
							params.put ("success", true);

							event = new HttpEvent (
									HttpEvent.onHttpConnect, params);
							dispatchEvent (event);
						} else {
							// Error, session already exist and cannot
							// be redefined!
							Logger
									.getLogger (
											SmartFoxClient.class
													.getName ())
									.log (Level.SEVERE,
											"**ERROR** SessionId is being rewritten");
						}
					} // handle data
					else {
						// fire disconnection
						if (response.indexOf (HttpConnection.CONN_LOST) == 0) {
							params.put ("data", "");
							event = new HttpEvent (
									HttpEvent.onHttpClose, params);
						} // fire onHttpData
						else {
							params.put ("data", response);
							event = new HttpEvent (
									HttpEvent.onHttpData, params);
						}
						dispatchEvent (event);
					}
				}
			} catch (final IOException ex) {
				final SFSObject params = new SFSObject ();
				params.put ("message", ex.getMessage ());

				final HttpEvent event = new HttpEvent (
						HttpEvent.onHttpError, params);

				dispatchEvent (event);
			}
		}
	}
}
