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

import java.util.Collections;
import java.util.Map;

import javax.xml.bind.JAXBException;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.converter.jaxb.JaxbDataFormat;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.spring.handler.CamelNamespaceHandler;
import org.apache.camel.util.MainSupport;
import org.apache.camel.view.ModelFileGenerator;
import org.apache.log4j.Logger;
import org.nakedobjects.metamodel.authentication.AuthenticationSession;
import org.nakedobjects.metamodel.config.ConfigurationBuilder;
import org.nakedobjects.metamodel.config.ConfigurationBuilderFileSystem;
import org.nakedobjects.runtime.authentication.standard.exploration.ExplorationSession;
import org.nakedobjects.runtime.boot.NakedObjectsSystemBootstrapper;
import org.nakedobjects.runtime.system.DeploymentType;
import org.nakedobjects.runtime.system.NakedObjectsSystem;
import org.nakedobjects.runtime.system.SystemConstants;
import org.nakedobjects.runtime.viewer.NakedObjectsViewer;
import org.nakedobjects.runtime.viewer.NakedObjectsViewerInstallerAbstract;

import com.pragprog.dhnako.carserv.camel.inout.messages.CustomerMessage;
import com.pragprog.dhnako.carserv.camel.inout.processors.LookUpEmailProcessor;

public class CamelEsbAdapter extends MainSupport {

	private static Logger LOG = Logger.getLogger(CamelEsbAdapter.class);

	public static void main(String... args) {
		CamelEsbAdapter adapter = new CamelEsbAdapter();
		adapter.init();
		adapter.run(args);
	}

	private CamelContext camelContext;
	@SuppressWarnings("unused")
	private NakedObjectsSystem nakedObjectsSystem;

	// //////////////////////////////////////////////////////
	// Initialization
	// //////////////////////////////////////////////////////

	private void init() {
		enableHangupSupport();
		bootstrapNakedObjects();
		bootstrapCamel();
		try {
			camelContext.start();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void enableHangupSupport() {
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				LOG.info("shutdown hook: stopping...");
				try {
					CamelEsbAdapter.this.stop();
					LOG.info("shutdown hook: stopped");
				} catch (Exception ex) {
					LOG.warn("shutdown hook: failed to stop", ex);
				}
			}
		});
	}

	// //////////////////////////////////////////////////////
	// Naked Objects
	// //////////////////////////////////////////////////////

	public static class NoopClientInstaller extends NakedObjectsViewerInstallerAbstract {
		@Override
		public NakedObjectsViewer createViewer() {
			return null;
		}
		public String getName() {
			return "noop";
		}
	}
	private void bootstrapNakedObjects() {
		ConfigurationBuilder configurationBuilder = 
			new ConfigurationBuilderFileSystem("../carserv/commandline/config");
		configurationBuilder.add(SystemConstants.VIEWER_KEY, NoopClientInstaller.class.getName());
		configurationBuilder.add(SystemConstants.NOSPLASH_KEY, ""+true);
		
		NakedObjectsSystemBootstrapper systemBootstrapper = 
			new NakedObjectsSystemBootstrapper(configurationBuilder, getClass());
		
		nakedObjectsSystem = systemBootstrapper.bootSystem(DeploymentType.EXPLORATION);
	}

	/**
	 * In a production system we would need to obtain credentials properly.
	 */
	private AuthenticationSession currentAuthenticationSession() {
		return new ExplorationSession();
	}

	
	// //////////////////////////////////////////////////////
	// Camel
	// //////////////////////////////////////////////////////

	private void bootstrapCamel() {
		camelContext = new DefaultCamelContext();
		try {
			camelContext.addRoutes(new LookUpEmailRoute());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private final class LookUpEmailRoute extends RouteBuilder {
		@Override
		public void configure() throws Exception {
			String packageName = CustomerMessage.class.getPackage().getName();
			JaxbDataFormat jaxb = new JaxbDataFormat(packageName);
			from("file:messages/in")
				.setOutHeader("nakedObjectsSession")
					.constant(currentAuthenticationSession())
				.convertBodyTo(String.class)
				.unmarshal(jaxb)
				.process(new LookUpEmailProcessor())
				.marshal(jaxb)
				.to("file:messages/out");
		}
	}
	
	// //////////////////////////////////////////////////////
	// Camel Boilerplate
	// //////////////////////////////////////////////////////

	@Override
	protected ModelFileGenerator createModelFileGenerator()
			throws JAXBException {
		return new ModelFileGenerator(new CamelNamespaceHandler().getJaxbContext());
	}

	@Override
	protected ProducerTemplate findOrCreateCamelTemplate() {
		for (CamelContext camelContext : getCamelContexts()) {
			return camelContext.createProducerTemplate();
		}
		throw new IllegalArgumentException(
				"No CamelContexts are available so cannot create a ProducerTemplate!");
	}

	@Override
	protected Map<String, CamelContext> getCamelContextMap() {
		return Collections.singletonMap("default", camelContext);
	}

}
