package appengine.util;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import com.google.appengine.api.datastore.dev.LocalDatastoreService;
import com.google.appengine.tools.development.ApiProxyLocal;
import com.google.appengine.tools.development.ApiProxyLocalImpl;
import com.google.apphosting.api.ApiProxy;

/**
 * AppEngineの単体テストを行うためのユーティリティクラス。
 * @author shin1ogawa
 */
public class TestUtil {

	static final Logger logger = Logger.getLogger(TestUtil.class.getName());


	private TestUtil() {
	}

	/**
	 * AppEngineの単体テスト環境を準備する。
	 * @param appId 
	 * @param versionId 
	 * @param folderName
	 * @param noStrage {@code true}ならファイルへの書き込みは行わない。
	 * @throws IOException 
	 */
	public static void setUpAppEngine(String appId, String versionId, String folderName,
			boolean noStrage) throws IOException {
		setUpAppEngineWithTaskQueueService(appId, versionId, folderName, noStrage, null);
	}

	/**
	 * AppEngineの単体テスト環境を準備する。
	 * @param environment 
	 * @param folderName
	 * @param noStrage {@code true}ならファイルへの書き込みは行わない。
	 * @throws IOException 
	 */
	public static void setUpAppEngine(ApiProxy.Environment environment, String folderName,
			boolean noStrage) throws IOException {
		setUpAppEngineWithTaskQueueService(environment, folderName, noStrage, null);
	}

	/**
	 * queue.xmlを有効にして、AppEngineの単体テスト環境を準備する。
	 * @param appId
	 * @param versionId
	 * @param folderName
	 * @param noStrage {@code true}ならファイルへの書き込みは行わない。
	 * @param srcQueueXmlPath テスト環境にコピーする{@literal "queue.xml"}のパス
	 * @throws IOException
	 */
	public static void setUpAppEngineWithTaskQueueService(String appId, String versionId,
			String folderName, boolean noStrage, String srcQueueXmlPath) throws IOException {
		setUpAppEngineWithTaskQueueService(new LocalEnvironment(appId, versionId), folderName,
				noStrage, srcQueueXmlPath);
	}

	/**
	 * queue.xmlを有効にして、AppEngineの単体テスト環境を準備する。
	 * @param environment 
	 * @param folderName
	 * @param noStrage {@code true}ならファイルへの書き込みは行わない。
	 * @param srcQueueXmlPath テスト環境にコピーする{@literal "queue.xml"}のパス
	 * @throws IOException
	 */
	public static void setUpAppEngineWithTaskQueueService(ApiProxy.Environment environment,
			String folderName, boolean noStrage, String srcQueueXmlPath) throws IOException {
		if (StringUtils.isNotEmpty(srcQueueXmlPath)) {
			File dstQueueXmlFile =
					new File(folderName
							+ (folderName.endsWith(File.separator) ? "" : File.separator)
							+ "WEB-INF" + File.separator + "queue.xml");
			FileUtils.copyFile(new File(srcQueueXmlPath), dstQueueXmlFile);
		}
		ApiProxy.setEnvironmentForCurrentThread(environment);
		ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(folderName)) {
		});
		if (noStrage) {
			((ApiProxyLocalImpl) ApiProxy.getDelegate()).setProperty(
					LocalDatastoreService.NO_STORAGE_PROPERTY, Boolean.TRUE.toString());
		}
	}

	/**
	 * AppEngineの単体テスト環境を終了する。
	 */
	public static void tearDownAppEngine() {
		if (ApiProxy.getDelegate() != null) {
			((ApiProxyLocal) ApiProxy.getDelegate()).stop();
			ApiProxy.setDelegate(null);
		}
		ApiProxy.setEnvironmentForCurrentThread(null);
	}


	static class LocalEnvironment implements ApiProxy.Environment {

		final String appId;

		final String versionId;


		/**
		 * the constructor.
		 * @param appId
		 * @param versionId
		 * @category constructor
		 */
		LocalEnvironment(String appId, String versionId) {
			this.appId = appId;
			this.versionId = versionId;
		}

		/**
		 * sdkで起動した時に{@code ApiProxy.getCurrentEnvironment().getAppId()}で取得される値
		 */
		public String getAppId() {
			return appId;
		}

		/**
		 * sdkで起動した時に{@code ApiProxy.getCurrentEnvironment().getVersionId()}
		 * で取得される値
		 */
		public String getVersionId() {
			return versionId;
		}

		public String getRequestNamespace() {
			return "";
		}

		public String getAuthDomain() {
			return "gmail.com";
		}

		public boolean isLoggedIn() {
			return true;
		}

		public String getEmail() {
			return "unittest@gmail.com";
		}

		public boolean isAdmin() {
			return true;
		}

		public Map<String, Object> getAttributes() {
			Map<String, Object> map = new HashMap<String, Object>();
			return map;
		}
	}
}
