package example.basic;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.sdo.ImplHelper;

import commonj.sdo.DataObject;
import commonj.sdo.Type;
import commonj.sdo.helper.DataFactory;
import test.common.AbstractSDOTest;

/**
 * Test basic CRUD operation with a simpleton Type that has no relation but
 * primitive properties.
 *  
 * @author Pinaki Poddar
 *
 */
public class TestCRUD extends AbstractSDOTest {
	/**
	 * Persist a DataObject. 
	 * Verifies that the instance count is increased.
	 * Also verifies that the DataObject is cached.
	 * <br>
	 * Shows persist() and find() call with DataObjects.
	 */
	public void testPersist() {
		int before = count("Person");
		
		String ssn = ""+System.currentTimeMillis();
		// START SNIPPET:persist
		DataObject data = createPerson(ssn, "John", "Doe", 42);
		EntityManager em = emf.createEntityManager();
		em.getTransaction().begin();
		em.persist(data);
		em.getTransaction().commit();
		// END SNIPPET:persist
		int after = count("Person");
		
		assertEquals(before+1, after);
		
		// Shows how to find DataObject via em.find()
		// Notice how the identity is prefixed with Type name
		// START SNIPPET:find
		String id = "Person:"+ssn;
		DataObject result = em.find(DataObject.class, id);
		// END SNIPPET:find
		assertNotNull(result);
		assertEquals(data,result);
	}
	

	/**
	 * Query using JPQL. The query results should be DataObjects. The related
	 * DataObjects should also be fetched.
	 */
	public void testQuery() {
		EntityManager em = emf.createEntityManager();
		String jpql = "SELECT o FROM Person o WHERE o.firstName=:first AND o.age=:age";
		Query query = em.createQuery(jpql);
		query.setParameter("first", "John");
		query.setParameter("age", 42);
		List result = query.getResultList();
		assertFalse(result.isEmpty());
		for (Object o : result) {
			assertTrue(o instanceof DataObject);
			DataObject dataObject = (DataObject) o;
			assertEquals("Person", dataObject.getType().getName());
			String firstName = dataObject.getString("firstName");
			assertEquals("John", firstName);
			String lastName = dataObject.getString("lastName");
			assertEquals("Doe", lastName);
		}
	}
	
	/**
	 * Tests that DataObject can be modified and merged. The changes are 
	 * persisted and the merged DataObjects returned reflect the modifications
	 * as well as the version updates.
	 */
	public void testUpdate() {
		OpenJPAEntityManager em = emf.createEntityManager();
		String jpql = "SELECT o FROM Person o WHERE o.firstName=:first AND o.age=:age";
		Query query = em.createQuery(jpql);
		query.setParameter("first", "John");
		query.setParameter("age", 42);
		List result = query.getResultList();
		assertFalse(result.isEmpty());
		for (Object o : result) {
			int before = count("Person");
			DataObject dataObject = (DataObject) o;
			int v0 = ImplHelper.getVersion(dataObject);
			assertTrue(v0>0);
			assertNotNull(ImplHelper.getIdValue(dataObject));
			
			dataObject.setString("firstName", "Ron");
			em.getTransaction().begin();
			DataObject merged = em.merge(dataObject);
			int v1 = ImplHelper.getVersion(merged);
			assertTrue(v1>v0);
			em.getTransaction().commit();
			assertEquals("Ron", merged.getString("firstName"));
			int after = count("Person");
			assertEquals(before, after);
		}
	}
	
	/**
	 * Tests that DataObjects can be deleted from persistent store via remove()
	 * method.
	 *
	 */
	public void testDelete() {
		OpenJPAEntityManager em = emf.createEntityManager();
		String jpql = "SELECT o FROM Person o WHERE o.lastName=:last AND o.age=:age";
		Query query = em.createQuery(jpql);
		query.setParameter("last", "Doe");
		query.setParameter("age", 42);
		List result = query.getResultList();
		assertFalse(result.isEmpty());
		for (Object o : result) {
			int before = count("Person");
			DataObject dataObject = (DataObject) o;
			em.getTransaction().begin();
			em.remove(dataObject);
			em.getTransaction().commit();
			int after = count("Person");
			assertEquals(before-1, after);
		}
	}
	
	
	/**
	 * Helper method to create and populate a Person DataObject.
	 */
	// START SNIPPET:createPerson
	DataObject createPerson(String ssn, String first, String last, int age) {
		Type type = getType("Person");
		DataObject data = DataFactory.INSTANCE.create(type);
		data.set("ssn", ssn);
		data.setString("firstName", first);
		data.setString("lastName", last);
		data.setInt("age", age);
		return data;
	}
	// END SNIPPET:createPerson
	
}
