package example.basic;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.apache.openjpa.persistence.OpenJPAEntityManager;

import test.common.AbstractSDOTest;

import commonj.sdo.DataObject;
import commonj.sdo.helper.DataFactory;

/**
 * Tests basic create/read/update/delete operations on DataObject that is 
 * related to other DataObjects.
 * 
 * @author Pinaki Poddar
 *
 */
public class TestRelation extends AbstractSDOTest {
	/**
	 * Persist a DataObject. Persisting a root DataObject also persists its
	 * closure. Verifies that related USAddress instances are persisted also.
	 */
	public void testPersist() {
		int poCountBefore = count("PurchaseOrderType");
		int addressCountBefore = count("USAddress");
		
		DataObject data = createPurchaseOrder();
		EntityManager em = emf.createEntityManager();
		em.getTransaction().begin();
		em.persist(data);
		em.getTransaction().commit();
		
		int poCountAfter = count("PurchaseOrderType");
		int addressCountAfter = count("USAddress");
		
		assertEquals(poCountBefore+1, poCountAfter);
		assertEquals(addressCountBefore+2, addressCountAfter);
	}
	

	/**
	 * Query using JPQL. The query results should be DataObjects. The related
	 * DataObjects should also be fetched.
	 */
	public void testQuery() {
		// START SNIPPET:createQuery
		EntityManager em = emf.createEntityManager();
		String jpql = "SELECT o FROM PurchaseOrderType o WHERE o.shipTo.name=:name";
		Query query = em.createQuery(jpql);
		query.setParameter("name", "Alice Smith");
		List<DataObject> result = query.getResultList();
		// END SNIPPET:createQuery
		assertFalse(result.isEmpty());
		for (DataObject dataObject : result) {
			assertEquals("PurchaseOrderType", dataObject.getType().getName());
			DataObject shipTo = dataObject.getDataObject("shipTo");
			String name = shipTo.getString("name");
			assertEquals("Alice Smith", name);
			DataObject billTo = dataObject.getDataObject("billTo");
			String state = billTo.getString("state");
			assertEquals("PA", state);
		}
	}
	
	/**
	 * Modifies part of an existing DataGraph, merges the changes and verifies
	 * that merged ojects reflect the changes. Also verifies that no new
	 * instances are created as a result of merging.
	 *
	 */
	public void testUpdate() {
		// START SNIPPET:merge 
		OpenJPAEntityManager em = emf.createEntityManager();
		String jpql = "SELECT o FROM PurchaseOrderType o WHERE o.shipTo.name=:name";
		Query query = em.createQuery(jpql);
		query.setParameter("name", "Alice Smith");
		List<DataObject> result = query.getResultList();
		assertFalse(result.isEmpty());
		for (DataObject dataObject : result) {
			int before = count("USAddress");
			dataObject.getDataObject("billTo").setString("name", "John Doe");
			em.getTransaction().begin();
			DataObject merged = em.merge(dataObject);
			em.getTransaction().commit();
			String changedName = merged.getDataObject("billTo").getString("name");
			assertEquals("John Doe", changedName);
			int after = count("USAddress");
			assertEquals(before, after);
		}
		// END SNIPPET:merge 
	}
	
	/**
	 * Tests that related DataObjects are fetched.
	 *
	 */
	public void testEagerRelationFetchedInDefaultFetch() {
		DataObject data = createPurchaseOrder();
		EntityManager em = emf.createEntityManager();
		em.getTransaction().begin();
		em.persist(data);
		em.getTransaction().commit();
		em.clear();
		
		em = emf.createEntityManager();
		String jpql = "SELECT o FROM PurchaseOrderType o";
		Query query = em.createQuery(jpql);
		List result = query.getResultList();
		assertFalse(result.isEmpty());
		for (Object o : result) {
			assertTrue(o.getClass() + " is not DataObject", o instanceof DataObject);
			DataObject dataObject = (DataObject) o;
			List items = dataObject.getDataObject("items").getList(0);
			assertNotNull(items);
			for (Object item:items)
				assertNotNull(item);
		}
	}
	
	/**
	 * Deletes a DataGraph. Verifies that all realted instances are deleted 
	 * also.
	 *
	 */
	public void testDelete() {
		OpenJPAEntityManager em = emf.createEntityManager();
		String jpql = "SELECT o FROM PurchaseOrderType o WHERE o.shipTo.name=:name";
		Query query = em.createQuery(jpql);
		query.setParameter("name", "Alice Smith");
		List result = query.getResultList();
		assertFalse(result.isEmpty());
		for (Object o : result) {
			int before = count("USAddress");
			DataObject dataObject = (DataObject) o;
			em.getTransaction().begin();
			em.remove(dataObject);
			em.getTransaction().commit();
			int after = count("USAddress");
			assertEquals(before-2, after); // 2 address /PurchaseOrder
		}
	}
	
	/**
	 * Create a SDO DataGraph and returns the root DataObject.
	 * 
	 */
	DataObject createPurchaseOrder() {
		DataObject purchaseOrder = DataFactory.INSTANCE.create(
				getType("PurchaseOrderType"));

		purchaseOrder.setString("orderDate", "1999-10-20");

		DataObject shipTo = purchaseOrder.createDataObject("shipTo");
		purchaseOrder.set("shipTo", shipTo);
		shipTo.set("country", "US");
		shipTo.set("name", "Alice Smith");
		shipTo.set("street", "123 Maple Street");
		shipTo.set("city", "Mill Valley");
		shipTo.set("state", "CA");
		shipTo.setString("zip", "90952");

		 DataObject billTo = purchaseOrder.createDataObject("billTo");
		 purchaseOrder.set("billTo", billTo);
		 billTo.set("country", "US");
		 billTo.set("name", "Robert Smith");
		 billTo.set("street", "8 Oak Avenue");
		 billTo.set("city", "Mill Valley");
		 billTo.set("state", "PA");
		 billTo.setString("zip", "95819");

		 purchaseOrder.set("comment", "Hurry, my lawn is going wild!");

		 DataObject items = purchaseOrder.createDataObject("items");
		
		 DataObject item1 = items.createDataObject("item");
		 item1.set("partNum", "872-AA");
		 item1.set("productName", "Lawnmower");
		 item1.setInt("quantity", 1);
		 item1.setString("USPrice", "148.95");
		 item1.set("comment", "Confirm this is electric");
		
		 DataObject item2 = items.createDataObject("item");
		 item2.set("partNum", "926-AA");
		 item2.set("productName", "Baby Monitor");
		 item2.setInt("quantity", 1);
		 item2.setString("USPrice", "39.98");
		 item2.setString("shipDate", "1999-05-21");

		return purchaseOrder;
	}
}
