/**
 * TitleActivator.java
 * Created May 14, 2008
 * Copyright (c) TANDBERG Television 2007-2008
 */
package com.tandbergtv.watchpoint.pmm.title;

import static com.tandbergtv.watchpoint.pmm.title.search.TitleSearchKey.DEFAULT_SYSTEM_ID;

import java.util.Collection;
import java.util.HashSet;

import org.apache.log4j.Logger;
import org.java.plugin.PluginManager;
import org.java.plugin.registry.Extension;
import org.java.plugin.registry.ExtensionPoint;
import org.java.plugin.registry.PluginRegistry;
import org.springframework.context.ApplicationContext;

import com.tandbergtv.metadatamanager.factoryImpl.SpecHandlerFactory;
import com.tandbergtv.watchpoint.pmm.dao.hibernate.HibernateContext;
import com.tandbergtv.watchpoint.pmm.title.conf.ISpecificationManager;
import com.tandbergtv.watchpoint.pmm.title.conf.Specification;
import com.tandbergtv.watchpoint.pmm.title.conf.SpecificationBuilder;
import com.tandbergtv.watchpoint.pmm.title.conf.SpecificationManager;
import com.tandbergtv.watchpoint.pmm.title.conf.specs.RightsManagerFactory;
import com.tandbergtv.watchpoint.pmm.title.provider.ITitleProvider;
import com.tandbergtv.watchpoint.pmm.title.provider.ITitleProviderRegistry;
import com.tandbergtv.watchpoint.pmm.title.provider.ITitleSearchStrategy;
import com.tandbergtv.watchpoint.pmm.title.provider.internal.AbstractTitleProvider;
import com.tandbergtv.watchpoint.pmm.title.provider.internal.DefaultTitleSearchStrategy;
import com.tandbergtv.watchpoint.pmm.title.provider.internal.TitleProvider;
import com.tandbergtv.watchpoint.pmm.title.provider.internal.TitleProviderFactory;
import com.tandbergtv.watchpoint.pmm.title.provider.internal.TitleProviderRegistry;
import com.tandbergtv.watchpoint.pmm.title.search.ITitleSearchService;
import com.tandbergtv.watchpoint.pmm.title.search.TitleSearchService;
import com.tandbergtv.watchpoint.pmm.title.template.ITitleTemplateProvider;
import com.tandbergtv.watchpoint.pmm.title.template.TitleTemplateProvider;
import com.tandbergtv.watchpoint.pmm.title.validation.ITitleValidationService;
import com.tandbergtv.watchpoint.pmm.title.validation.TitleValidationService;
import com.tandbergtv.workflow.core.service.Service;
import com.tandbergtv.workflow.core.service.ServiceRegistry;

/**
 * Activator for title management related services
 * 
 * @author Sahil Verma
 */
public class TitleActivator {

	private PluginManager pluginManager;

	private ApplicationContext context;

	private static final String PROVIDER_NAME = "Default";

	private static final String PLUGIN_ID = "com.tandbergtv.metadata";

	private static final Logger logger = Logger.getLogger(TitleActivator.class);

	/**
	 * @return the pluginManager
	 */
	public PluginManager getPluginManager() {
		return pluginManager;
	}

	/**
	 * @param pluginManager
	 *            the pluginManager to set
	 */
	public void setPluginManager(PluginManager pluginManager) {
		this.pluginManager = pluginManager;
	}

	/**
	 * @return the context
	 */
	public ApplicationContext getContext() {
		return context;
	}

	/**
	 * @param context the context to set
	 */
	public void setContext(ApplicationContext context) {
		this.context = context;
	}

	/**
	 * Starts the title bundle
	 */
	public void start() {
		HibernateContext.getContext().initializeContext();
		addService(new DefaultTitlePersistenceService());
		addService(new TitleSearchService());
		addService(new TitleProviderRegistry());
		addService(SpecificationManager.newInstance());
		addService(new TitleValidationService());
		addService(TitleService.getInstance());
		addService(new TitleTemplateProvider());

		addDefaultTitleProvider();

		/* Eager loading... it's a little overzealous */
		addMetadataFormatSpecifications();
		addTitleProviders();
		
		/* load the application wide rights manager */
		loadRightsManager();

		logger.debug("Title services started");
	}

	/**
	 * Invokes the rights manager factory to create and cache the specified
	 * rights manager.
	 */
	private void loadRightsManager() {
		PluginRegistry registry = pluginManager.getRegistry();
		ExtensionPoint point = registry.getExtensionPoint(PLUGIN_ID,
				"rightsManager");
		RightsManagerFactory.createRightsManager(pluginManager, point);
	}

	/**
	 * Stops the title bundle
	 */
	public void stop() {
		removeTitleProviders();

		removeService(ITitleTemplateProvider.class);
		removeService(ITitleService.class);
		removeService(ITitleValidationService.class);
		removeService(ISpecificationManager.class);
		removeService(ITitleProviderRegistry.class);
		removeService(ITitleSearchService.class);
		removeService(ITitlePersistenceService.class);

		HibernateContext.getContext().closeContext();

		logger.debug("Title services stopped");
	}

	/**
	 * Adds the specified service
	 * 
	 * @param service
	 */
	private void addService(Service service) {
		ServiceRegistry.getDefault()
				.register(service.getServiceName(), service);

		service.start();
	}

	/**
	 * Removes the specified service
	 * 
	 * @param service
	 */
	private void removeService(Class<? extends Service> clazz) {
		ServiceRegistry registry = ServiceRegistry.getDefault();
		Service service = registry.lookup(clazz);

		if (service != null) {
			service.stop();
			registry.unregister(service);
		}
	}

	/**
	 * Adds pluggable {@link ITitleProvider} implementations to the registry of
	 * providers
	 */
	private void addTitleProviders() {
		ITitleProviderRegistry registry = ServiceRegistry.getDefault().lookup(
				ITitleProviderRegistry.class);

		for (ITitleProvider provider : loadTitleProviders())
			registry.addProvider(provider);
	}

	/**
	 * Adds the system default title provider to the registry
	 */
	private void addDefaultTitleProvider() {
		ITitleProviderRegistry registry = ServiceRegistry.getDefault().lookup(
				ITitleProviderRegistry.class);
		ISpecificationManager sm = ServiceRegistry.getDefault().lookup(
				ISpecificationManager.class);
		Collection<String> names = new HashSet<String>();
		ITitleSearchStrategy strategy = new DefaultTitleSearchStrategy();

		for (Specification spec : sm.getAllSpecifications())
			names.add(spec.getName());

		AbstractTitleProvider provider = new TitleProvider(DEFAULT_SYSTEM_ID
				.toString(), PROVIDER_NAME, strategy, names);
		strategy.setTitleProvider(provider);

		registry.addProvider(provider);
	}

	private void removeTitleProviders() {
		/* No-op for now, but we should shutdown the extensions */
	}

	/**
	 * Loads ITitleProvider implementations using the extension registry
	 * 
	 * @return
	 */
	private Collection<ITitleProvider> loadTitleProviders() {
		Collection<ITitleProvider> providers = new HashSet<ITitleProvider>();

		PluginRegistry registry = pluginManager.getRegistry();
		ExtensionPoint point = registry.getExtensionPoint(PLUGIN_ID,
				"titleprovider");

		TitleProviderFactory factory = TitleProviderFactory.newInstance();

		for (Extension extension : point.getAvailableExtensions()) {
			try {
				ITitleProvider provider = factory.createProvider(pluginManager,
						extension);
				providers.add(provider);
			} catch (Exception e) {
				String id = extension.getDeclaringPluginDescriptor().getId();
				id += ":" + extension.getId();
				logger.error("Failed to load Title Provider for extension["
						+ id + "], error: " + e.getMessage(), e);
			}
		}

		return providers;
	}

	private void addMetadataFormatSpecifications() {
		/* Start the metadata manager first */
		new com.tandbergtv.metadatamanager.conf.SpecificationBuilder(this.pluginManager, this.context)
			.buildSpecifications();
		
		ISpecificationManager sm = ServiceRegistry.getDefault().lookup(ISpecificationManager.class);
		SpecificationBuilder builder = 
			SpecificationBuilder.createInstance(pluginManager, SpecHandlerFactory.getHandlers());

		for (Specification specification : builder.buildSpecifications())
			sm.addSpecification(specification);
	}
}
