package appengine.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.util.logging.Logger;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;

import com.google.apphosting.utils.remoteapi.RemoteApiPb;
import com.google.apphosting.utils.remoteapi.RemoteApiServlet;

/**
 * RemoteApiを使用するための認証機能とRemoteApiへのアクセス機能を提供するユーティリティクラス。
 * @author shin1ogawa
 * @see RemoteApiServlet
 * @see RemoteApiPb
 */
public class RemoteApiUtil {

	static final Logger LOGGER = Logger.getLogger(RemoteApiUtil.class.getName());


	/**
	 * Google Accounts API で認証を行い、auth tokenを返す。
	 * 
	 * @param httpClient
	 * @param email
	 * @param password
	 * @param applicationName
	 *            適当で良いっぽい？よくわからない。
	 * @return auth token
	 * @throws HttpException
	 * @throws IOException
	 */
	public static String auth(HttpClient httpClient, String email, String password,
			String applicationName) throws HttpException, IOException {
		PostMethod method = new PostMethod("https://google.com/accounts/ClientLogin");
		method.setParameter("Email", email);
		method.setParameter("Passwd", password);
		method.setParameter("service", "ah");
		method.setParameter("source", applicationName);
		method.setParameter("accountType", "HOSTED_OR_GOOGLE");
		int statusCode = httpClient.executeMethod(method);
		if (statusCode != HttpStatus.SC_OK) {
			method.releaseConnection();
			throw new IllegalStateException("HttpStatus != SC_OK: " + statusCode);
		}
		Header[] responseHeaders = method.getResponseHeaders();
		for (Header header : responseHeaders) {
			LOGGER.finest(header.getName() + "=" + header.getValue().trim());
		}
		LineNumberReader reader =
				new LineNumberReader(new StringReader(method.getResponseBodyAsString()));
		method.releaseConnection();
		String line = null;
		while ((line = reader.readLine()) != null) {
			line = line.trim();
			if (line.startsWith("Auth=")) {
				return line.substring("Auth=".length());
			}
		}
		throw new IllegalStateException("Con't find Auth field.");
	}

	/**
	 * RemoteApiServletへGETメソッドをリクエストする。
	 * 
	 * @param httpClient
	 * @param authToken
	 * @param applicationUri
	 *            例. {@literal http://shin1ogawa.appspot.com/}
	 * @param remoteApiServletName
	 *            例. {@litera _ah/remoteapi}
	 * @return リクエストに使用した{@link HttpMethod}
	 * @throws HttpException
	 * @throws IOException
	 */
	public static HttpMethod getFromRemoteApi(HttpClient httpClient, String authToken,
			String applicationUri, String remoteApiServletName) throws HttpException, IOException {
		StringBuilder uri = new StringBuilder();
		uri.append("continue=").append(applicationUri).append(remoteApiServletName)
			.append("&auth=").append(authToken);
		GetMethod method = new GetMethod(applicationUri + "_ah/login?" + uri.toString());
		method.addRequestHeader(new Header("X-appcfg-api-version", "dummy"));
		int statusCode = httpClient.executeMethod(method);
		if (statusCode != HttpStatus.SC_OK) {
			method.releaseConnection();
			throw new IllegalStateException("HttpStatus != SC_OK: " + statusCode);
		}
		Header[] responseHeaders = method.getResponseHeaders();
		for (Header header : responseHeaders) {
			LOGGER.finest(header.getName() + "=" + header.getValue().trim());
		}
		return method;
	}

	/**
	 * RemteApiServletへバイナリをPostする。
	 * 
	 * @param httpClient
	 * @param authToken
	 * @param applicationUri
	 * @param remoteApiServletName
	 * @param requestBody
	 * @return リクエストに使用した{@link HttpMethod}
	 * @throws HttpException
	 * @throws IOException
	 */
	@SuppressWarnings("deprecation")
	public static HttpMethod postToRemoteApi(HttpClient httpClient, String authToken,
			String applicationUri, String remoteApiServletName, byte[] requestBody)
			throws HttpException, IOException {
		StringBuilder uri =
				new StringBuilder().append(applicationUri).append(remoteApiServletName).append(
						"&auth=").append(authToken);
		PostMethod method = new PostMethod(uri.toString());
		method.addRequestHeader(new Header("Content-Type", "application/octet-stream"));
		method.addRequestHeader(new Header("X-appcfg-api-version", "dummy"));
		ByteArrayInputStream in = new ByteArrayInputStream(requestBody);
		method.setRequestBody(in);
		int statusCode = httpClient.executeMethod(method);
		if (statusCode != HttpStatus.SC_OK) {
			method.releaseConnection();
			throw new IllegalStateException("HttpStatus != SC_OK: " + statusCode);
		}
		Header[] responseHeaders = method.getResponseHeaders();
		for (Header header : responseHeaders) {
			LOGGER.finest(header.getName() + "=" + header.getValue().trim());
		}
		return method;
	}
}
