/**
 * ContextCache.java
 * Created on May 16, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.util;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

import com.tandbergtv.watchpoint.pmm.entities.IContainer;
import com.tandbergtv.watchpoint.pmm.entities.Partner;
import com.tandbergtv.watchpoint.pmm.entities.Service;
import com.tandbergtv.watchpoint.pmm.partner.IPartnerManagement;
import com.tandbergtv.watchpoint.pmm.partner.PartnerManager;
import com.tandbergtv.watchpoint.pmm.service.IServiceManagement;
import com.tandbergtv.watchpoint.pmm.service.ServiceManager;
import com.tandbergtv.workflow.core.service.cache.CacheService;
import com.tandbergtv.workflow.core.service.cache.ICacheService;

/**
 * This is a cache between contextId->partner/service object
 * NOTE: This cache is not updated when the context for a container is updated.
 * e.g. new jobs are added, new schedules are added etc.
 * This cache is not meant to look up the context object within the container
 * but only to get the container (type) object given the contextId.
 * 
 * @author spuranik 
 */
public class ContainerCache extends CacheService<IContainer> {

	private static String SERVICE_NAME = "Container Cache";
	// one for partners and the other for services.
	private static int THREAD_FACTORY_THREAD_COUNT = 2;
	// this is a magic number! We dont assume to have more than 250
	// partners and services at a deployment site.
	public static int MAX_CONTAINER_COUNT = 250;
	private Logger logger = Logger.getLogger(ContainerCache.class);
	private static ICacheService<IContainer> instance;
	private ScheduledThreadPoolExecutor executor;

	public ContainerCache() {
		super(SERVICE_NAME, MAX_CONTAINER_COUNT);
	}

	public static synchronized ICacheService<IContainer> getInstance() {
		if (instance == null) {
			instance = new ContainerCache();
		}
		return instance;
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.ServiceLifecycle#start()
	 */
	public void start() {
		logger.debug("Starting Container Cache service.");

		super.start();

		// Don't increase the start up time. 
		// So loading all partners and services in two separate threads
		logger.debug("Loading partners and services in the background now.");
		executor = new ScheduledThreadPoolExecutor(THREAD_FACTORY_THREAD_COUNT);
		populatePartners();
		populateServices();
		logger.debug("Done starting the cache.");
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.CacheService#stop()
	 */
	public void stop() {
		this.executor.shutdown();
	}

	/**
	 * This method gets all partners from the db via the partner manager
	 * 
	 * @return
	 */
	private List<Partner> populatePartners() {
		Callable<List<Partner>> starterThread = new Callable<List<Partner>>() {
			public List<Partner> call() throws Exception {
				IPartnerManagement partnerMgr = PartnerManager.getInstance();
				List<Partner> partners = partnerMgr.getAllActivePartners();
				logger.debug("Got " + partners.size() + " partners from db.");
				
				for (Partner p : partners) {
					add(p.getContainerContextId(), p);
				}
				logger.debug("Cache has " + count() + " containers.");
				// this list is not read anywhere!
				return partners;
			}
		};
		executor.schedule(starterThread, 0L, TimeUnit.MILLISECONDS);
		return null;
	}

	/**
	 * @return
	 */
	private List<Service> populateServices() {
		Callable<List<Service>> starterThread = new Callable<List<Service>>() {
			public List<Service> call() throws Exception {
				IServiceManagement serviceMgr = ServiceManager.getInstance();
				List<Service> services = serviceMgr.getAllActiveServices();
				logger.debug("Got " + services.size() + " services from db.");
				
				for (Service s : services) {
					add(s.getContainerContextId(), s);
				}
				logger.debug("Cache has " + count() + " containers.");
				return services;
			}
		};
		executor.schedule(starterThread, 0L, TimeUnit.MILLISECONDS);
		return null;
	}

}
