package org.lp20.aikuma.storage;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.List;

import org.json.simple.*;

/**
 * Utility class for handling google authentication/authorization.
 * 
 * @author haejoong
 */
public class GoogleAuth {
	private String clientId_;
	private String clientSecret_;
	private String lastError_;
	private String accessToken_;

    public String getRefreshToken() {
        return refreshToken;
    }

    private String refreshToken;
	
	/**
	 * Construct an authentication/authorization object. Example,
	 * 
	 * - Call getAuthUrl(), and open an authentication/authorization page
	 *   pointed by the returned URL.
	 * - When submitted the form on the page, the browser displays a string
	 *   called authorization code.
	 * - Call requestAccessToken() method with the authorization code.
	 * - Call getAccessToken() method to get the access token.
	 * 
	 * @param clientId
	 * @param clientSecret
	 */
	public GoogleAuth(String clientId, String clientSecret) {
		clientId_ = clientId;
		clientSecret_ = clientSecret;
	}
	
	/**
	 * Returns a URL that the end user needs to visit to authenticate and authorize.
	 * In the page, user logs in to google and authorize APIs. Then, the page
	 * returns an authorization code.
	 * 
	 * @return A URL in string.
	 */
	public String getAuthUrl(List<String> apis) {
		String url = "https://accounts.google.com/o/oauth2/auth?"
				+ "response_type=code&"
				+ "redirect_uri=urn:ietf:wg:oauth:2.0:oob&"
				+ "client_id=" + clientId_;

		if (apis.size() > 0) {
			url += "&scope=";
			for (String api: apis) {
				url += api + " ";
			}
			url = url.trim();
		}
		
		return url;
	}

    public static boolean validateAccessToken(String accessToken) {

        try {
            URL url = new URL("https://accounts.google.com/o/oauth2/token");
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setInstanceFollowRedirects(true);
            con.setDoOutput(true);
            con.setRequestMethod("POST");
            OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream());
            writer.write("access_token="+ accessToken);
            writer.flush();
            writer.close();
            if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
                return true;
            } else if (con.getResponseCode() == HttpURLConnection.HTTP_BAD_REQUEST) {
                return false;
            } else {
                System.err.println(con.getResponseCode());
                System.err.println(con.getResponseMessage());
                throw new RuntimeException("Unexpected response code");
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Obtain an access_token given a refresh_token. Use getAccessToken() to get the obtained token.
     *
     * @param refreshToken Google supplied OATH2.0 refresh_token
     * @return true if successful, false, otherwise
     */
    public boolean refreshAccessToken(String refreshToken) {
        return handleTokenRequest("grant_type=refresh_token" +
                "&refresh_token=" + refreshToken +
                "&client_id=" + clientId_ +
                "&client_secret=" + clientSecret_);
    }

    /**
     * Obtain an access token. Use getAccessToken() to get the obtained token.
     *
     * @param authCode
     * @return true if successful, false, otherwise
     */
    public boolean requestAccessToken(String authCode) {
        return handleTokenRequest("redirect_uri=urn:ietf:wg:oauth:2.0:oob" +
                "&grant_type=authorization_code" +
                "&code=" + authCode +
                "&client_id=" + clientId_ +
                "&client_secret=" + clientSecret_);
    }

    private boolean handleTokenRequest(String body) {
        try {
            URL url = new URL("https://accounts.google.com/o/oauth2/token");
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setInstanceFollowRedirects(true);
            con.setDoOutput(true);
            con.setRequestMethod("POST");
            OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream());
            writer.write(body);
            writer.flush();
            writer.close();
            if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
                parseTokenResponse(Utils.readStream(con.getInputStream()));
                lastError_ = null;
                return true;
            } else {
                // TODO: Error logging?
                accessToken_ = null;
                lastError_ = "HTTP error: " + con.getResponseMessage();
                return false;
            }
        }
        catch (IOException e) {
            // TODO: Logging? e.printStackTrace();
            accessToken_ = null;
            lastError_ = "Exception: " + e.getMessage();
            return false;
        }
    }

    private void parseTokenResponse(String resp) {
        JSONObject obj = (JSONObject) JSONValue.parse(resp);
        accessToken_ = (String) obj.get("access_token");
        if (obj.containsKey("refresh_token"))
            refreshToken = (String) obj.get("refresh_token");
    }


	/**
	 * Return the access token obtained by requestAccessToken().
	 * Return null if there is nothing to return.
	 * 
	 * @return String token if token exists, null otherwise.
	 */
	public String getAccessToken() {
        return accessToken_;
	}
	
	/**
	 * Returns an error message produced by a previous method call that
	 * is involved in communication with google auth servers.
	 * 
	 * @return Error message.
	 */
	public String getLastError() {
		return lastError_;
	}
}

@@ -1808,16 +1808,181 @@
 rl;%0A%09%7D%0A%0A
+    /**%0A     * Check to see if an access_token is valid%0A     *%0A     * @param accessToken an OATH2 access token%0A     * @return true if valid, false otherwise%0A     */%0A
     publ
@@ -2910,232 +2910,9 @@
     
-    throw new RuntimeException(%22Unexpected response code%22);%0A            %7D%0A%0A        %7D catch (MalformedURLException e) %7B%0A            e.printStackTrace();%0A        %7D catch (ProtocolException e) %7B%0A            e.printStackTrace();
+%7D
 %0A   

Add docs, simplify exceptions