/**
 * ServiceManager.java
 * Created on Jun 25, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.service;

import static com.tandbergtv.watchpoint.pmm.schedule.search.ScheduleSearchKey.ACTIVE;
import static com.tandbergtv.watchpoint.pmm.schedule.search.ScheduleSearchKey.CONTEXT;
import static com.tandbergtv.workflow.driver.search.SearchType.NUMERIC;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Session;

import com.tandbergtv.cms.portal.util.transaction.Transactional;
import com.tandbergtv.watchpoint.pmm.dao.hibernate.HibernateContext;
import com.tandbergtv.watchpoint.pmm.entities.ContainerType;
import com.tandbergtv.watchpoint.pmm.entities.DistributionSchedule;
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.entities.ServiceContext;
import com.tandbergtv.watchpoint.pmm.partner.PartnerManager;
import com.tandbergtv.watchpoint.pmm.schedule.search.IScheduleSearchService;
import com.tandbergtv.watchpoint.pmm.service.dao.ServiceHDAO;
import com.tandbergtv.watchpoint.pmm.util.DataOperationException;
import com.tandbergtv.watchpoint.search.Entity;
import com.tandbergtv.workflow.core.service.ServiceRegistry;
import com.tandbergtv.workflow.core.service.cache.ICacheService;
import com.tandbergtv.workflow.driver.search.ValueParameter;
import com.tandbergtv.workflow.util.SearchCriteria;

/**
 * @author Vlada Jakobac
 * 
 */
public class ServiceManager implements IServiceManagement {

	private static ServiceManager uniqueInstance = new ServiceManager();
	private static final Logger logger = Logger.getLogger(ServiceManager.class);
	private static String CONTAINER_CACHE_SERVICE_NAME = "Container Cache";

	public static IServiceManagement getInstance() {
		return uniqueInstance;
	}

	@Transactional
	public synchronized List<Service> getServicesBySearchCriteria(
			SearchCriteria searchCriteria) {
		List<Service> serviceList = null;
		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			ServiceHDAO serviceDAO = new ServiceHDAO(session);
			serviceList = serviceDAO.findActiveBySearchCriteria(searchCriteria);
		} catch (Exception e) {
			logger.error("Error while retrieving services: " + e.getMessage(),
					e);
		}
		return serviceList;
	}

	@Transactional
	public synchronized List<Service> getAllActiveServices() {
		List<Service> serviceList = null;
		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			ServiceHDAO serviceDAO = new ServiceHDAO(session);
			serviceList = serviceDAO.findByActive(true);
		} catch (Exception e) {
			logger.error("Error while retrieving services: " + e.getMessage(),
					e);
		}
		return serviceList;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public Service createService(Service service) {
		ServiceContext context = new ServiceContext();
		context.setServiceContainer(service);
		service.setContext(context);
		service.setIsActive(true);

		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			ServiceHDAO serviceDAO = new ServiceHDAO(session);
			serviceDAO.create(service);
		} catch (Exception e) {
			logger
					.error("Error while creating a service: " + e.getMessage(),
							e);
			return null;
		}
		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		containerCache.add(service.getContainerContextId(), service);

		return service;
	}

	@Transactional
	public synchronized Service getService(long serviceId) {
		Service result = null;

		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			ServiceHDAO serviceDAO = new ServiceHDAO(session);
			result = serviceDAO.findByKey(serviceId);
		} catch (Exception e) {
			logger.error("Error while getting a service: " + e.getMessage(), e);
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public synchronized boolean deleteService(long serviceId)
			throws DataOperationException {
		// service object required to obtain its context id, which is then used
		// to remove it from the cache
		Service service = getService(serviceId);
		long contextId = service.getContainerContextId();

		// check if there are no jobs, schedules, and partners associated with
		// the service
		/* JobManager.getAllJobs() closes the session which it should not.
		 * Jobs section have not been fixed to work in CMS.
		 * So, commenting out temporarily.
		 */
		/*IJobManager jobManager = JobManager.getInstance();
		List<Job> lsJobs = jobManager.getAllJobs(contextId);
		if (lsJobs != null && lsJobs.size() > 0) {
			// don't delete the service while there're jobs associated
			logger.error("Cannot delete the service with serviceId="
					+ serviceId + " since there are " + lsJobs.size()
					+ " jobs associated with the service");
			throw new DataOperationException(
					"Service ["
							+ serviceId
							+ "] cannot be deleted since there are jobs associated with it.");
		}*/

		// only pitch schedules are possible for a service
		int schedulesCount = countPitchSchedules(new Long(contextId));
		if (schedulesCount > 0) {
			// don't delete the service while there're schedules associated
			logger.error("Cannot delete the service with serviceId="
					+ serviceId + " since there are " + schedulesCount
					+ " schedules associated with the service");
			throw new DataOperationException(
					"Service ["
							+ serviceId
							+ "] cannot be deleted since there are schedules associated with it.");
		}

		// check if none of partners is associated with this service
		if (service.getPartnerCount() > 0) {
			logger.error("Cannot delete the service with serviceId="
					+ serviceId + " since there are "
					+ service.getPartnerCount()
					+ " partners associated with the service");
			throw new DataOperationException(
					"Service ["
							+ serviceId
							+ "] cannot be deleted since there are partners associated with it.");
		}

		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			ServiceHDAO serviceDAO = new ServiceHDAO(session);
			service.setIsActive(false);
			serviceDAO.update(service);
		} catch (Exception e) {
			logger.error("Error while deleting service (id: " + serviceId
					+ "): ", e);
			return false;
		}

		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		containerCache.remove(contextId);
		return true;
	}

	private int countPitchSchedules(Long contextId) {
		int result = 0;

		SearchCriteria criteria = new SearchCriteria();

		Entity e = new Entity("pitch", DistributionSchedule.class, "d");

		e.addParameter(new ValueParameter(ACTIVE.toString(), NUMERIC, 1));
		e.addParameter(new ValueParameter(CONTEXT.toString(), NUMERIC,
				contextId));
		criteria.addParameter(e);
		IScheduleSearchService searchService = ServiceRegistry.getDefault()
				.lookup(IScheduleSearchService.class);
		result = searchService.count(criteria);

		return result;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public Service updateService(Service service) {
		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			ServiceHDAO serviceDAO = new ServiceHDAO(session);
			serviceDAO.update(service);
		} catch (Exception e) {
			logger
					.error("Error while updating a service: " + e.getMessage(),
							e);
			return null;
		}
		
		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		containerCache.add(service.getContainerContextId(), service);

		return service;
	}

	@SuppressWarnings("unchecked")
	public Set<Service> getAllServicesAssociatedWithPartner(long contextId) {
		Set<Service> services = new HashSet<Service>();
		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		IContainer container = containerCache.get(contextId);
		if (container.getContainerType() == ContainerType.PARTNER) {
			Partner partner = (Partner) container;
			services = PartnerManager.getInstance().getPartner(partner.getId())
					.getServices();
		}
		return services;
	}

	public int getTotalActiveServiceCount() {
		return this.getAllActiveServices().size();
	}
}
