/***
 * Excerpted from "Domain-Driven Design Using Naked Objects",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/dhnako for more book information.
***/
package com.pragprog.dhnako.carserv.wicket.app;

import java.util.List;

import org.apache.wicket.Session;
import org.nakedobjects.applib.query.Query;
import org.nakedobjects.metamodel.authentication.AuthenticationSession;
import org.nakedobjects.metamodel.commons.exceptions.NotYetImplementedException;
import org.nakedobjects.metamodel.facets.object.value.ValueFacet;
import org.nakedobjects.metamodel.services.container.query.QueryFindAllInstances;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.specloader.SpecificationLoader;
import org.nakedobjects.plugins.headless.embedded.EmbeddedContext;
import org.nakedobjects.plugins.headless.embedded.internal.PersistenceState;

import com.google.common.collect.Lists;
import com.pragprog.dhnako.carserv.wicket.persistence.ObjectStore;

public class EmbeddedContextWicket implements EmbeddedContext {

	private final ObjectStore objectStore;
	private final EffectiveSessionHolder effectiveSessionHolder;
	private SpecificationLoader specificationLoader;
	

	public EmbeddedContextWicket(final ObjectStore objectStore, EffectiveSessionHolder effectiveSessionHolder) {
		this.objectStore = objectStore;
		this.effectiveSessionHolder = effectiveSessionHolder;
	}

	///////////////////////////////////////////////////////////
	// AuthenticationSession
	///////////////////////////////////////////////////////////

	public AuthenticationSession getAuthenticationSession() {
		return effectiveSessionHolder.getSession();
	}

	///////////////////////////////////////////////////////////
	// instantiate
	///////////////////////////////////////////////////////////

	public Object instantiate(Class<?> type) {
		try {
			return type.newInstance();
		} catch (InstantiationException ex) {
			throw new RuntimeException(ex);
		} catch (IllegalAccessException ex) {
			throw new RuntimeException(ex);
		}
	}

	///////////////////////////////////////////////////////////
	// allInstances, allMatchingPattern, allMatching
	///////////////////////////////////////////////////////////
	
	public List<?> allInstances(String fullyQualifiedTypeName) {
		try {
			Class<?> type = loadClass(fullyQualifiedTypeName);
			return instancesOf(type);
		} catch (ClassNotFoundException ex) {
			throw new RuntimeException(ex);
		}
	}

	public List<?> allMatchingPattern(String fullyQualifiedTypeName, Object pattern) {
		throw new NotYetImplementedException(); // not needed
	}

	public List<?> allMatchingTitle(String fullyQualifiedTypeName, String title) {
		throw new NotYetImplementedException(); // not needed
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> allMatchingQuery(Query<T> query) {
		if (query instanceof QueryFindAllInstances) {
			QueryFindAllInstances<T> queryFindAllInstances = (QueryFindAllInstances<T>) query;
			return (List<T>) allInstances(queryFindAllInstances.getResultTypeName());
		}
		throw new NotYetImplementedException(); // not needed
	}

	public <T> T firstMatchingQuery(Query<T> query) {
		throw new NotYetImplementedException(); // not needed
	}


	private <T> List<T> instancesOf(Class<T> type) {
		return Lists.newArrayList(getObjectStore().instances(type));
	}

	private Class<?> loadClass(String fullyQualifiedTypeName)
			throws ClassNotFoundException {
		return Thread.currentThread().getContextClassLoader().loadClass(fullyQualifiedTypeName);
	}
	

	///////////////////////////////////////////////////////////
	// resolve, objectChanged
	///////////////////////////////////////////////////////////

	public void resolve(Object parent) {
		// lazy loading
	}

	public void resolve(Object parent, Object field) {
		// lazy loading
	}

	public void objectChanged(Object object) {
		// dirty object, save
	}

	///////////////////////////////////////////////////////////
	// flush, commit
	///////////////////////////////////////////////////////////

	public boolean flush() {
		return false;
	}

	public void commit() {
		// nothing to do
	}


	///////////////////////////////////////////////////////////
	// makePersistent, remove
	///////////////////////////////////////////////////////////

	public void makePersistent(Object object) {
		getObjectStore().persist(object);
	}

	public void remove(Object object) {
		getObjectStore().remove(object);
	}

	public PersistenceState getPersistenceState(Object object) {
		if (getObjectStore().isPersistent(object)) {
			return PersistenceState.PERSISTENT;
		}
		NakedObjectSpecification spec = getSpecificationLoader().loadSpecification(object.getClass());
		if (!spec.containsFacet(ValueFacet.class)) {
			return PersistenceState.TRANSIENT;
		} else {
			return PersistenceState.STANDALONE;
		}
	}


	///////////////////////////////////////////////////////////
	// informUser, warnUser, raiseError
	///////////////////////////////////////////////////////////

	public void informUser(String message) {
		Session.get().info(message);
	}

	public void warnUser(String message) {
		Session.get().warn(message);
	}
	
	public void raiseError(String message) {
		Session.get().error(message);
	}


	///////////////////////////////////////////////////////////
	// Dependencies
	///////////////////////////////////////////////////////////
	
	public ObjectStore getObjectStore() {
		return objectStore;
	}

	
	public SpecificationLoader getSpecificationLoader() {
		return specificationLoader;
	}
	public void setSpecificationLoader(SpecificationLoader specificationLoader) {
		this.specificationLoader = specificationLoader;
	}

}
