package com.shin1ogawa.service;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

/**
 * Serviceの実装のための抽象クラス。
 * 
 * @author shin1ogawa
 * @param <T>
 *            Entityクラス
 */
public class AbstractService<T> {

	PersistenceManager pm;
	Class<T> entityClass;

	@SuppressWarnings("unchecked")
	public AbstractService(T... args) {
		try {
			entityClass = (Class<T>) args.getClass().getComponentType();
			pm = PMF.get().getPersistenceManager();
		} catch (Throwable th) {
			th.printStackTrace();
		}
	}

	public Query newQuery() {
		return pm.newQuery(entityClass);
	}

	public T getByPrimaryKey(Key key) {
		Query query = newQuery();
		query.setFilter("key == pKey");
		query.declareParameters("java.lang.String pKey");
		@SuppressWarnings("unchecked")
		List<T> list = (List<T>) query.execute(KeyFactory.keyToString(key));
		return list.isEmpty() ? null : list.get(0);
		// TODO pm.getObjectById()したいが、datanucleus内部のキャッシュから取得してしまう。
		// 例えばBoardにMessageを追加してコミット→対象のBoardをgetObjectById()すると、
		// Messageを追加する前に取得したBoardが帰って来てしまう。
		// datanucleusが内部で使用しているキャッシュを操作するにはどーすれば良いのだろうか？
		// return pm.getObjectById(entityClass, key);
	}

	public T getById(Long id) {
		Key key = KeyFactory.createKey(entityClass.getSimpleName(), id);
		return getByPrimaryKey(key);
	}

	public List<T> list(Query query) {
		try {
			@SuppressWarnings("unchecked")
			List<T> list = (List<T>) query.execute();
			return new ArrayList<T>(list);
		} finally {
			query.closeAll();
		}
	}

	public T delete(T entity, Object key) {
		Transaction transaction = pm.currentTransaction();
		transaction.begin();
		try {
			T b = pm.getObjectById(entityClass, key);
			pm.deletePersistent(b);
			transaction.commit();
			return b;
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
		}
	}

	public T save(T entity) {
		Transaction transaction = pm.currentTransaction();
		transaction.begin();
		try {
			T b = pm.makePersistent(entity);
			transaction.commit();
			return b;
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
		}
	}
}
