package gwtupload.client;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestBuilder.Method;
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.user.client.Cookies;
import com.google.gwt.user.client.Window;
import com.google.gwt.xml.client.XMLParser;

import java.util.List;
import java.util.Map.Entry;

import static gwtupload.shared.UConsts.PARAM_SESSION;
import static gwtupload.shared.UConsts.TAG_SESSION_ID;

public interface ISession {

  public static class CORSSession extends Session {
    @Override
    protected void setSessionId(String s) {
      super.setSessionId(s);
      s = s == null? "" : (";jsessionid=" + s);
      servletPath = servletPath.replaceFirst("^(.+)(/[^/\\?;]*)(;[^/\\?]*|)(\\?|/$|$)(.*)", "$1$2" + s + "$4$5");
      System.err.println("CORS Session: " + servletPath);
    }
    
    @Override
    protected RequestBuilder createRequest(Method method, int timeout, String... params) {
      RequestBuilder req =  super.createRequest(method, timeout, params);
      req.setIncludeCredentials(true);
      return req;
    }
  }

  public static class Session implements ISession {
    String sessionId;
    String servletPath = "servlet.gupld";

    public static ISession createSession(String path, RequestCallback callback) {
      Session ret = path.startsWith("http") ? new CORSSession() : new Session();
      ret.servletPath = path;
      ret.getSession(callback);
      return ret;
    }

    /**
     * Sends a request to the server in order to get the session cookie,
     * when the response with the session comes, it submits the form.
     *
     * This is needed because this client application usually is part of
     * static files, and the server doesn't set the session until dynamic pages
     * are requested.
     *
     * If we submit the form without a session, the server creates a new
     * one and send a cookie in the response, but the response with the
     * cookie comes to the client at the end of the request, and in the
     * meanwhile the client needs to know the session in order to ask
     * the server for the upload status.
     */
    public void getSession(final RequestCallback callback) {
      sendRequest("session", new RequestCallback() {
        public void onResponseReceived(Request request, Response response) {
          String s  = Cookies.getCookie("JSESSIONID");
          if (s == null) {
            s = Utils.getXmlNodeValue(XMLParser.parse(response.getText()), TAG_SESSION_ID);
          }
          setSessionId(s);
          callback.onResponseReceived(request, response);
        }
        public void onError(Request request, Throwable exception) {
          setSessionId(null);
          callback.onError(request, exception);
        }
      }, PARAM_SESSION + "=true");
    }

    protected void setSessionId(String s) {
      sessionId = s;
    }

    public String getServletPath() {
      return servletPath;
    }

    public void sendRequest(String payload, RequestCallback callback, String... params) {
      // Using a reusable builder makes IE fail
      RequestBuilder reqBuilder = createRequest(RequestBuilder.GET, DEFAULT_AJAX_TIMEOUT, params);
      try {
        reqBuilder.sendRequest(payload, callback);
      } catch (RequestException e) {
        callback.onError(null, e);
      }
    }

    protected RequestBuilder createRequest(Method method, int timeout, String...params) {
      RequestBuilder reqBuilder = new RequestBuilder(RequestBuilder.GET, composeURL(params));
      reqBuilder.setTimeoutMillis(timeout);
      return reqBuilder;
    }

    public String composeURL(String... params) {
      String ret = servletPath;
      ret = ret.replaceAll("[\\?&]+$", "");
      String sep = ret.contains("?") ? "&" : "?";
      for (String par : params) {
        ret += sep + par;
        sep = "&";
      }
      for (Entry<String, List<String>> e : Window.Location.getParameterMap().entrySet()) {
        ret += sep + e.getKey() + "=" + e.getValue().get(0);
      }
      ret += sep + "random=" + Math.random();
      return ret;
    }
  }

  static final int DEFAULT_AJAX_TIMEOUT = 10000;

  public void getSession(RequestCallback callback);

  public String composeURL(String... params);

  public void sendRequest(String name, RequestCallback callback, String... params);

  public String getServletPath();
}

@@ -20,16 +20,55 @@
 lient;%0A%0A
+import com.google.gwt.core.client.GWT;%0A
 import c
@@ -98,24 +98,24 @@
 nt.Request;%0A
-
 import com.g
@@ -710,16 +710,25 @@
 SSession
+Parameter
  extends
@@ -1053,18 +1053,70 @@
     %7D%0A  
-  
+%7D%0A%0A  public static class CORSSession extends Session %7B
 %0A    @Ov
@@ -1571,19 +1571,28 @@
 sion ret
- = 
+;%0A      if (
 path.sta
@@ -1610,45 +1610,120 @@
 tp%22)
- ? new CORSSession() : new Session();
+) %7B%0A        ret = GWT.create(CORSSession.class);%0A      %7D else %7B%0A        ret = GWT.create(Session.class);%0A      %7D
 %0A   

Use credentials instead of parameter in CORS