package cx.ath.hoenicke.otp.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;

import cx.ath.hoenicke.gwt.md5.client.MD5;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Client implements EntryPoint {
	public static final boolean readonly = false;
	
	String user, password, salt;
	int    seq = -1;
	int oldseq;
	int userid;
	boolean admin;
	LoginDialog loginDialog;
	AlertBox alertBox;
	
	/**
	 * Basic JSON wrapper for reply of the server.  There are subclasses
	 * for each specific reply a server may send.  This handles just
	 * the authentication layer.
	 */
	static class ServerReply extends JavaScriptObject {
		protected ServerReply() {}
		
		public final native boolean isAuth() /*-{
			return this.auth; 
		}-*/;
		public final native int authSeq() /*-{
			return this.seq; 
		}-*/;
		public final native String authSalt() /*-{
			return this.salt; 
		}-*/;
	}

	/**
	 * Handles reply for "user" queries.
	 */
	static class UserReply extends ServerReply {
		protected UserReply() {}
		
		public static native UserReply fromJSONString(String jsonString) /*-{
			return eval('('+jsonString+')');
		}-*/;
		public final native boolean isAdmin() /*-{ return this.admin; }-*/;
		public final native int getId() /*-{ return this.id; }-*/;
		public final native JSArray<User> getUsers() /*-{ return this.users; }-*/;
	}

	class UserHandler implements RequestCallback {
		public UserHandler() {
			post();
		}
		
		public void post() {
			try {
				RequestBuilder request = 
					new RequestBuilder(RequestBuilder.POST, BASE_URL+"user.php");
				request.setHeader("Content-Type", "application/x-www-form-urlencoded");
				request.sendRequest(getAuth(), this);
			} catch (RequestException ex) {
				alert(ex.toString());
			}
		}

		public void onResponseReceived(Request request, Response response) {
			if (response.getStatusCode() == 200) {
				UserReply reply = UserReply.fromJSONString(response.getText());
				if (!reply.isAuth()) {
					if (updateSalt(reply))
						post();
					return;
				}
				userid = reply.getId();
				admin = reply.isAdmin();
				/* handle user info */
				...
			} else {
				alert(""+response.getStatusCode()+":"+response.getText());
			}
		}

		public void onError(Request request, Throwable exception) {
			alert("Error while requesting UserInfo");
		}
	}

	public String getAuth() {
		return getAuth(password);
	}

	public String getAuth(String newpw) {
		String pw;
		if (salt == null)
			pw = "";
		else
			pw = "&pw="+MD5.otpjoho(password, seq, salt);
		oldseq = seq;
		if (newpw != password || (seq >= 0 && seq < 146)) {
			seq = 400;
			salt = MD5.md5(user+salt+seq+Math.random()).substring(0,16);
			pw += "&nseq="+seq+"&nsalt="+salt
			      +"&npw="+MD5.otpjoho(newpw, seq+1, salt);
		} else {
			seq--;
		}
		return "user="+user+pw;
	}
	
	public boolean updateSalt(ServerReply reply) {
		salt = reply.authSalt();
		seq = reply.authSeq();

		if (oldseq == reply.authSeq()) {
			login();
			return false;
		}
		return true;
	}

	public void setAuth(String user, String pw) {
		this.user     = URL.encodeComponent(user);
		this.password = pw;
		refresh();
	}
	
	public void refresh() {
		new UserHandler();
	}
	
	public void login() {
		loginDialog.center();
	}
	
	private void logOut() {
		user = password = salt = null;
		seq = -1;
		loginDialog.loginField.setText("");
		loginDialog.pwField.setText("");
	}
	
	public void alert(String s) {
		alertBox.alert(s);
	}
	

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		loginDialog = new LoginDialog(this);
		alertBox = new AlertBox();
		login();
	}
}

