/***
 * 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.ui;

import java.util.Collections;
import java.util.List;

import org.apache.wicket.protocol.http.WebApplication;
import org.nakedobjects.applib.fixtures.CompositeFixture;
import org.nakedobjects.applib.fixtures.InstallableFixture;
import org.nakedobjects.applib.fixtures.LogonFixture;
import org.nakedobjects.applib.profiles.ProfileService;
import org.nakedobjects.applib.profiles.ProfileServiceAware;
import org.nakedobjects.applib.switchuser.SwitchUserService;
import org.nakedobjects.applib.switchuser.SwitchUserServiceAware;
import org.nakedobjects.metamodel.services.ServicesInjector;
import org.nakedobjects.plugins.headless.embedded.NakedObjectsMetaModel;
import org.nakedobjects.runtime.authentication.standard.SimpleSession;
import org.nakedobjects.runtime.authentication.standard.exploration.ExplorationSession;

import com.pragprog.dhnako.carserv.demo.domainservices.CalendarServiceDemo;
import com.pragprog.dhnako.carserv.demo.domainservices.EmailServiceDemo;
import com.pragprog.dhnako.carserv.fixture.CustomerCarsMaintenanceFixture;
import com.pragprog.dhnako.carserv.inmemory.application.CustomerApplicationRepositoryInMemory;
import com.pragprog.dhnako.carserv.inmemory.employee.EmployeeRepositoryInMemory;
import com.pragprog.dhnako.carserv.inmemory.payment.PaymentMethodTypeRepositoryInMemory;
import com.pragprog.dhnako.carserv.inmemory.service.ServiceableVehicleRepositoryInMemory;
import com.pragprog.dhnako.carserv.inmemory.vehicle.MakeRepositoryInMemory;
import com.pragprog.dhnako.carserv.inmemory.vehicle.ModelRepositoryInMemory;
import com.pragprog.dhnako.carserv.inmemory.vehicle.VehicleTypeRepositoryInMemory;
import com.pragprog.dhnako.carserv.wicket.app.EffectiveSessionHolder;
import com.pragprog.dhnako.carserv.wicket.app.EffectiveSessionHolderImpl;
import com.pragprog.dhnako.carserv.wicket.app.EmbeddedContextWicket;
import com.pragprog.dhnako.carserv.wicket.app.ProfileServiceNoop;
import com.pragprog.dhnako.carserv.wicket.app.SwitchUserServiceImpl;
import com.pragprog.dhnako.carserv.wicket.persistence.ObjectStore;

public class NakedObjectsApplication extends WebApplication {

	private final NakedObjectsMetaModel nakedObjectsMetaModel;

	private final EffectiveSessionHolderImpl effectiveSessionHolder = new EffectiveSessionHolderImpl();
	private final SwitchUserService switchUserService = new SwitchUserServiceImpl(
			effectiveSessionHolder);
	private final ProfileService perspectivePersistenceService = new ProfileServiceNoop();

	private LogonFixture logonFixture;

	public NakedObjectsApplication() {
		ObjectStore objectStore = new ObjectStore();
		EmbeddedContextWicket embeddedContext = new EmbeddedContextWicket(
				objectStore, effectiveSessionHolder);

		nakedObjectsMetaModel = new NakedObjectsMetaModel(
				embeddedContext,
				CustomerApplicationRepositoryInMemory.class,
				ServiceableVehicleRepositoryInMemory.class,
				EmployeeRepositoryInMemory.class,
				VehicleTypeRepositoryInMemory.class,
				ModelRepositoryInMemory.class, 
				MakeRepositoryInMemory.class,
				PaymentMethodTypeRepositoryInMemory.class,
				EmailServiceDemo.class, 
				CalendarServiceDemo.class);

		nakedObjectsMetaModel.init();
		objectStore.setSpecificationLoader(
				nakedObjectsMetaModel.getSpecificationLoader());
		embeddedContext.setSpecificationLoader(
				nakedObjectsMetaModel.getSpecificationLoader());

		CustomerCarsMaintenanceFixture topLevelFixture = 
			new CustomerCarsMaintenanceFixture();
		installFixture(topLevelFixture);

		effectiveSessionHolder
				.setSession(logonFixture != null ? new SimpleSession(
						logonFixture.getUsername(), logonFixture.getRoles())
						: new ExplorationSession());
	}

	public NakedObjectsMetaModel getNakedObjectsMetaModel() {
		return nakedObjectsMetaModel;
	}

	public Class<?> getHomePage() {
		return Index.class;
	}

	public EffectiveSessionHolder getEffectiveSessionHolder() {
		return effectiveSessionHolder;
	}

	// //////////////////////////////////////////
	// Fixture installation
	// //////////////////////////////////////////

	private void installFixture(final Object fixture) {
		getServicesInjector().injectDependencies(fixture);

		installFixtures(getFixtures(fixture));
		installSingleFixture(fixture);
		saveLogonFixtureIfRequired(fixture);
	}

	private void installFixtures(final List<Object> fixtures) {
		for (final Object fixture : fixtures) {
			installFixture(fixture);
		}
	}

	private List<Object> getFixtures(final Object fixture) {
		if (fixture instanceof CompositeFixture) {
			CompositeFixture compositeFixture = (CompositeFixture) fixture;
			return compositeFixture.getFixtures();
		}
		return Collections.emptyList();
	}

	private void installSingleFixture(final Object fixture) {
		if (fixture instanceof SwitchUserServiceAware) {
			SwitchUserServiceAware serviceAware = (SwitchUserServiceAware) fixture;
			serviceAware.setService(switchUserService);
		}
		if (fixture instanceof ProfileServiceAware) {
			ProfileServiceAware serviceAware = (ProfileServiceAware) fixture;
			serviceAware.setService(perspectivePersistenceService);
		}

		if (fixture instanceof InstallableFixture) {
			InstallableFixture installableFixture = (InstallableFixture) fixture;
			installableFixture.install();
		}

		if (fixture instanceof LogonFixture) {
			this.logonFixture = (LogonFixture) fixture;
		}
	}

	private void saveLogonFixtureIfRequired(final Object fixture) {
		if (fixture instanceof LogonFixture) {
			this.logonFixture = (LogonFixture) fixture;
		}
	}

	private ServicesInjector getServicesInjector() {
		return nakedObjectsMetaModel.getServicesInjector();
	}
}
