package kr.co.kihyun.beans.entity.util;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.jdo.Extent;
import javax.jdo.FetchGroup;
import javax.jdo.FetchPlan;
import javax.jdo.JDOException;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.JDOUserException;
import javax.jdo.ObjectState;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.datastore.JDOConnection;
import javax.jdo.datastore.Sequence;
import javax.jdo.listener.InstanceLifecycleListener;
import kr.co.kihyun.beans.entity.MDept;
import kr.co.kihyun.beans.entity.MoumiEntity;
import kr.co.kihyun.beans.entity.ecross.CRUD;
import kr.co.kihyun.beans.entity.ecross.IRemotePersistable;
import kr.co.kihyun.moumi.MoumiConfig;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MPersistenceManager implements PersistenceManager {

	private static final Logger LOG = LoggerFactory.getLogger(MPersistenceManager.class);
	private PersistenceManager pm;

	public MPersistenceManager(PersistenceManager pm) {
		this.pm = pm;
	}

	public boolean isClosed() {
		return pm.isClosed();
	}

	public void close() {
		pm.close();
	}

	public Transaction currentTransaction() {
		return pm.currentTransaction();
	}

	public void evict(Object pc) {
		pm.evict(pc);
	}

	public void evictAll(Object... pcs) {
		pm.evictAll(pcs);
	}

	public void evictAll(Collection pcs) {
		pm.evictAll(pcs);
	}

	public void evictAll(boolean subclasses, Class pcClass) {
		pm.evictAll(subclasses, pcClass);
	}

	public void evictAll() {
		pm.evictAll();
	}

	public void refresh(Object pc) {
		pm.refresh(pc);
	}

	public void refreshAll(Object... pcs) {
		pm.refreshAll(pcs);
	}

	public void refreshAll(Collection pcs) {
		pm.refreshAll(pcs);
	}

	public void refreshAll() {
		pm.refreshAll();
	}

	public void refreshAll(JDOException jdoe) {
		pm.refreshAll(jdoe);
	}

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

	public Query newQuery(Object compiled) {
		return pm.newQuery(compiled);
	}

	public Query newQuery(String query) {
		return pm.newQuery(query);
	}

	public Query newQuery(String language, Object query) {
		return pm.newQuery(language, query);
	}

	public Query newQuery(Class cls) {
		return pm.newQuery(cls);
	}

	public Query newQuery(Extent cln) {
		return pm.newQuery(cln);
	}

	public Query newQuery(Class cls, Collection cln) {
		return pm.newQuery(cls, cln);
	}

	public Query newQuery(Class cls, String filter) {
		return pm.newQuery(cls, filter);
	}

	public Query newQuery(Class cls, Collection cln, String filter) {
		return pm.newQuery(cls, cln, filter);
	}

	public Query newQuery(Extent cln, String filter) {
		return pm.newQuery(cln, filter);
	}

	public Query newNamedQuery(Class cls, String queryName) {
		return pm.newNamedQuery(cls, queryName);
	}

	public <T> Extent<T> getExtent(Class<T> persistenceCapableClass, boolean subclasses) {
		return pm.getExtent(persistenceCapableClass, subclasses);
	}

	public <T> Extent<T> getExtent(Class<T> persistenceCapableClass) {
		return pm.getExtent(persistenceCapableClass);
	}

	public Object getObjectById(Object oid, boolean validate) {
		return pm.getObjectById(oid, validate);
	}

	public <T> T getObjectById(Class<T> cls, Object key) {
		PersistenceCapable pc = cls.getAnnotation(PersistenceCapable.class);
		//LOG.debug("{}@{}.identityType: {}", new Object[]{cls, key, pc.identityType()});
		if (pc.identityType() == IdentityType.NONDURABLE) {
			try {
				Method m = cls.getMethod("getId");
				String typeName = m.getReturnType().getCanonicalName();
				//LOG.debug("cls/key : {}@({}){}", new Object[]{cls, typeName, key});

				QueryImpl q = new QueryImpl(pm, cls);
				q.setFilter("id == key");
				q.declareParameters(typeName + " key");
                                //v2. 6.SQL 삽입 : DO형식이므로 해결책에 따른 prepare SQL 문으로 변경할 수 없음
				Iterator<T> objIter = ((List<T>) q.execute(key)).iterator();
                                //================
				if (objIter.hasNext()) {
					return objIter.next();
				}
				throw new JDOObjectNotFoundException("Class: " + cls, " key: " + key);
			} catch (SecurityException e) {
				throw new RuntimeException(e);
			} catch (NoSuchMethodException e) {
				throw new JDOUserException(cls + " doesn't have identity.");
			}
		}
		return pm.getObjectById(cls, key);
	}

	public Object getObjectById(Object oid) {
		return pm.getObjectById(oid);
	}

	public Object getObjectId(Object pc) {
		return pm.getObjectId(pc);
	}

	public Object getTransactionalObjectId(Object pc) {
		return pm.getTransactionalObjectId(pc);
	}

	public Object newObjectIdInstance(Class pcClass, Object key) {
		return pm.newObjectIdInstance(pcClass, key);
	}

	public Collection getObjectsById(Collection oids, boolean validate) {
		return pm.getObjectsById(oids, validate);
	}

	public Collection getObjectsById(Collection oids) {
		return pm.getObjectsById(oids);
	}

	public Object[] getObjectsById(Object[] oids, boolean validate) {
		return pm.getObjectsById(oids, validate);
	}

	public Object[] getObjectsById(boolean validate, Object... oids) {
		return pm.getObjectsById(validate, oids);
	}

	public Object[] getObjectsById(Object... oids) {
		return pm.getObjectsById(oids);
	}

	public <T> T makePersistent(T pc) {
		try {
			return pm.makePersistent(pc);
		} catch (Exception e) {
                    
                        //42.비어있는 if문 : Delete by YOUNGJUN,CHO
                        /*
			if (pc instanceof kr.co.kihyun.beans.entity.Board) {
				//LOG.debug("attachements: {}", ((kr.co.kihyun.beans.entity.Board) pc).getAttachments());
			}
                        */
                        //------------------------------------------------
			throw new RuntimeException(e);
		}
	}

	public <T> boolean isRemoteObject(T pc) {
		if (pc instanceof IRemotePersistable) {
			IRemotePersistable rp = (IRemotePersistable) pc;

			MDept currentParty = this.getObjectById(MDept.class, MoumiConfig.getInitParameter("moumi.partyDeptId"));
			MDept ownerParty = rp.getOwnerParty(this);
			//LOG.debug("currentParty: {}, ownerParty: {}", currentParty, rp.getOwnerParty(this));

			if (!ownerParty.getId().equals(currentParty.getId())) {
				return true;
			}
		}
		return false;
	}

	public <T> CRUD getCrud(T pc) {
		CRUD crud = CRUD.U;
		MoumiEntity me = (MoumiEntity) pc;
		if (me.getId() == null) {
			crud = CRUD.C;
		} else {
			try {
				pm.getObjectById(pc.getClass(), me.getId());
			} catch (JDOObjectNotFoundException jonfe) {
				crud = CRUD.C;
			}
		}
		return crud;
	}

	public <T> T[] makePersistentAll(T... pcs) {
		return pm.makePersistentAll(pcs);
	}

	public <T> Collection<T> makePersistentAll(Collection<T> pcs) {
		return pm.makePersistentAll(pcs);
	}

	public void deletePersistent(Object pc) {
		pm.deletePersistent(pc);
	}

	public void deletePersistentAll(Object... pcs) {
		pm.deletePersistentAll(pcs);
	}

	public void deletePersistentAll(Collection pcs) {
		pm.deletePersistentAll(pcs);
	}

	public void makeTransient(Object pc) {
		pm.makeTransient(pc);
	}

	public void makeTransientAll(Object... pcs) {
		pm.makeTransactionalAll(pcs);
	}

	public void makeTransientAll(Collection pcs) {
		pm.makeNontransactionalAll(pcs);
	}

	public void makeTransient(Object pc, boolean useFetchPlan) {
		pm.makeTransient(pc, useFetchPlan);
	}

	public void makeTransientAll(Object[] pcs, boolean useFetchPlan) {
		pm.makeTransientAll(pcs, useFetchPlan);
	}

	public void makeTransientAll(boolean useFetchPlan, Object... pcs) {
		pm.makeTransientAll(useFetchPlan, pcs);
	}

	public void makeTransientAll(Collection pcs, boolean useFetchPlan) {
		pm.makeTransientAll(pcs, useFetchPlan);
	}

	public void makeTransactional(Object pc) {
		pm.makeNontransactional(pc);
	}

	public void makeTransactionalAll(Object... pcs) {
		pm.makeNontransactionalAll(pcs);
	}

	public void makeTransactionalAll(Collection pcs) {
		pm.makeNontransactionalAll(pcs);
	}

	public void makeNontransactional(Object pc) {
		pm.makeNontransactional(pc);
	}

	public void makeNontransactionalAll(Object... pcs) {
		pm.makeNontransactionalAll(pcs);
	}

	public void makeNontransactionalAll(Collection pcs) {
		pm.makeNontransactionalAll(pcs);
	}

	public void retrieve(Object pc) {
		pm.refresh(pc);
	}

	public void retrieve(Object pc, boolean useFetchPlan) {
		pm.retrieve(pc, useFetchPlan);
	}

	public void retrieveAll(Collection pcs) {
		pm.retrieveAll(pcs);
	}

	public void retrieveAll(Collection pcs, boolean useFetchPlan) {
		pm.retrieveAll(pcs, useFetchPlan);
	}

	public void retrieveAll(Object... pcs) {
		pm.retrieveAll(pcs);
	}

	public void retrieveAll(Object[] pcs, boolean useFetchPlan) {
		pm.retrieveAll(pcs, useFetchPlan);
	}

	public void retrieveAll(boolean useFetchPlan, Object... pcs) {
		pm.retrieveAll(useFetchPlan, pcs);
	}

	public void setUserObject(Object o) {
		pm.setUserObject(o);
	}

	public Object getUserObject() {
		return pm.getUserObject();
	}

	public PersistenceManagerFactory getPersistenceManagerFactory() {
		return pm.getPersistenceManagerFactory();
	}

	public Class getObjectIdClass(Class cls) {
		return pm.getObjectIdClass(cls);
	}

	public void setMultithreaded(boolean flag) {
		pm.setMultithreaded(flag);
	}

	public boolean getMultithreaded() {
		return pm.getMultithreaded();
	}

	public void setIgnoreCache(boolean flag) {
		pm.setIgnoreCache(flag);
	}

	public boolean getIgnoreCache() {
		return pm.getIgnoreCache();
	}

	public void setDatastoreReadTimeoutMillis(Integer interval) {
		pm.setDatastoreReadTimeoutMillis(interval);
	}

	public Integer getDatastoreReadTimeoutMillis() {
		return pm.getDatastoreReadTimeoutMillis();
	}

	public void setDatastoreWriteTimeoutMillis(Integer interval) {
		pm.setDatastoreWriteTimeoutMillis(interval);
	}

	public Integer getDatastoreWriteTimeoutMillis() {
		return pm.getDatastoreWriteTimeoutMillis();
	}

	public boolean getDetachAllOnCommit() {
		return pm.getDetachAllOnCommit();
	}

	public void setDetachAllOnCommit(boolean flag) {
		pm.setDetachAllOnCommit(flag);
	}

	public boolean getCopyOnAttach() {
		return pm.getCopyOnAttach();
	}

	public void setCopyOnAttach(boolean flag) {
		pm.setCopyOnAttach(flag);
	}

	public <T> T detachCopy(T pc) {
		return pm.detachCopy(pc);
	}

	public <T> Collection<T> detachCopyAll(Collection<T> pcs) {
		return pm.detachCopyAll(pcs);
	}

	public <T> T[] detachCopyAll(T... pcs) {
		return pm.detachCopyAll(pcs);
	}

	public Object putUserObject(Object key, Object val) {
		return pm.putUserObject(key, val);
	}

	public Object getUserObject(Object key) {
		return pm.getUserObject(key);
	}

	public Object removeUserObject(Object key) {
		return pm.removeUserObject(key);
	}

	public void flush() {
		pm.flush();
	}

	public void checkConsistency() {
		pm.checkConsistency();
	}

	public FetchPlan getFetchPlan() {
		return pm.getFetchPlan();
	}

	public <T> T newInstance(Class<T> pcClass) {
		return pm.newInstance(pcClass);
	}

	public Sequence getSequence(String name) {
		return pm.getSequence(name);
	}

	public JDOConnection getDataStoreConnection() {
		return pm.getDataStoreConnection();
	}

	public void addInstanceLifecycleListener(InstanceLifecycleListener listener, Class... classes) {
		pm.addInstanceLifecycleListener(listener, classes);
	}

	public void removeInstanceLifecycleListener(InstanceLifecycleListener listener) {
		pm.removeInstanceLifecycleListener(listener);
	}

	public Date getServerDate() {
		return pm.getServerDate();
	}

	public Set getManagedObjects() {
		return pm.getManagedObjects();
	}

	public Set getManagedObjects(EnumSet<ObjectState> states) {
		return pm.getManagedObjects(states);
	}

	public Set getManagedObjects(Class... classes) {
		return pm.getManagedObjects(classes);
	}

	public Set getManagedObjects(EnumSet<ObjectState> states, Class... classes) {
		return pm.getManagedObjects(states, classes);
	}

	public FetchGroup getFetchGroup(Class cls, String name) {
		return pm.getFetchGroup(cls, name);
	}
}
