/**
 * PartnerManagemer.java
 * Created on May 13, 2008
 * (C) Copyright TANDBERG Television Ltd.
 */
package com.tandbergtv.watchpoint.pmm.partner;

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.io.Serializable;
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.Job;
import com.tandbergtv.watchpoint.pmm.entities.Partner;
import com.tandbergtv.watchpoint.pmm.entities.PartnerContext;
import com.tandbergtv.watchpoint.pmm.entities.PartnerType;
import com.tandbergtv.watchpoint.pmm.entities.Planner;
import com.tandbergtv.watchpoint.pmm.entities.Service;
import com.tandbergtv.watchpoint.pmm.job.IJobManager;
import com.tandbergtv.watchpoint.pmm.job.JobManager;
import com.tandbergtv.watchpoint.pmm.partner.dao.PartnerHDAO;
import com.tandbergtv.watchpoint.pmm.schedule.search.IScheduleSearchService;
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 PartnerManager implements IPartnerManagement {

	private static PartnerManager uniqueInstance = new PartnerManager();
	private static final Logger logger = Logger.getLogger(PartnerManager.class);
	private static String CONTAINER_CACHE_SERVICE_NAME = "Container Cache";

	public static IPartnerManagement getInstance() {
		return uniqueInstance;
	}

	@Transactional
	public synchronized List<Partner> getPartnersBySearchCriteria(
			SearchCriteria searchCriteria) {
		List<Partner> partnerList = null;
		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			PartnerHDAO partnerDAO = new PartnerHDAO(session);
			partnerList = partnerDAO.findActiveBySearchCriteria(searchCriteria);
		} catch (Exception e) {
			throw new RuntimeException("Error while retrieving partners: ", e);
		} catch (Throwable t) {
			logger.error("Error while searching for partners: ", t);
		}
		return partnerList;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public Partner createPartner(Partner partner) {
		PartnerContext context = new PartnerContext();
		context.setPartnerContainer(partner);
		partner.setContext(context);
		partner.setIsActive(true);

		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			PartnerHDAO partnerDAO = new PartnerHDAO(session);
			partnerDAO.create(partner);
		} catch (Exception e) {
			throw new RuntimeException("Error while creating a partner: ", e);
		}
		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		containerCache.add(partner.getContainerContextId(), partner);
		return partner;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public synchronized boolean deletePartner(long partnerId)
			throws DataOperationException {
		// partner object required to obtain its context id, which is then used
		// to remove it from the cache
		Partner partner = getPartner(partnerId);
		long contextId = partner.getContainerContextId();

		// check if there are jobs and/or schedules associated with the partner
		/* had to be commented out because JobManager was closing the session
		IJobManager jobManager = JobManager.getInstance();
		List<Job> lsJobs = jobManager.getAllJobs(contextId);
		if (lsJobs != null && lsJobs.size() > 0) {
			// don't delete the partner while there're jobs associated
			logger.error("Cannot delete the partner with partnerId="
					+ partnerId + " since there are " + lsJobs.size()
					+ " jobs associated with the partner");
			throw new DataOperationException(
					"Partner ["
							+ partnerId
							+ "] cannot be deleted since there are jobs associated with it.");
		}
		 */
		int schedulesCount = 0;
		PartnerType partnerType = partner.getType();
		if (partnerType == PartnerType.SOURCE) {
			schedulesCount = countPlanners(partnerId);
		} else if (partnerType == PartnerType.DISTRIBUTION) {
			schedulesCount = countPitchSchedules(new Long(contextId));
		} else {// it's SELF partner
			schedulesCount = 0;
		}
		if (schedulesCount > 0) {
			// don't delete the partner while there're schedules associated
			logger.error("Cannot delete the partner with partnerId="
					+ partnerId + " since there are " + schedulesCount
					+ " schedules associated with the partner");
			throw new DataOperationException(
					"Partner ["
							+ partnerId
							+ "] cannot be deleted since there are schedules associated with it.");
		}

		// check if none of services is associated with this partner
		if (partner.getServices() != null && partner.getServices().size() > 0) {
			logger.error("Cannot delete the partner with partnerId="
					+ partnerId
					+ " since there are services associated with the partner");
			throw new DataOperationException(
					"Partner ["
							+ partnerId
							+ "] cannot be deleted since there are services associated with it.");
		}

		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			PartnerHDAO partnerDAO = new PartnerHDAO(session);
			partner.setIsActive(false);
			partnerDAO.update(partner);
		} catch (Exception e) {
			logger.error("Error while deleting partner (id: " + partnerId
					+ "): ", 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;
	}

	private int countPlanners(long partnerId) {
		int result = 0;

		SearchCriteria criteria = new SearchCriteria();
		Entity e = new Entity("planner", Planner.class, "p");

		e.addParameter(new ValueParameter(ACTIVE.toString(), NUMERIC, 1));
		e
				.addParameter(new ValueParameter("sourcePartnerID", NUMERIC,
						partnerId));

		criteria.addParameter(e);
		IScheduleSearchService searchService = ServiceRegistry.getDefault()
				.lookup(IScheduleSearchService.class);
		result = searchService.count(criteria);

		return result;
	}

	@Transactional
	public synchronized Partner getPartner(long partnerId) {
		Partner result = null;
		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			PartnerHDAO partnerDAO = new PartnerHDAO(session);
			result = partnerDAO.findByKey(partnerId);
		} catch (Exception e) {
			throw new RuntimeException("Error while getting a partner: ", e);
		}
		return result;
	}

	@Transactional
	public synchronized List<Partner> getAllActivePartners() {
		Session session = null;
		List<Partner> partnerList = null;
		try {
			session = HibernateContext.getContext().getCurrentSession();
			PartnerHDAO partnerDAO = new PartnerHDAO(session);
			partnerList = partnerDAO.findByActive(true);
		} catch (Exception e) {
			throw new RuntimeException("Error while retrieving partners: ", e);
		}
		return partnerList;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public synchronized Partner updatePartner(Partner partner) {
		try {
			Session session = HibernateContext.getContext().getCurrentSession();
			PartnerHDAO partnerDAO = new PartnerHDAO(session);
			partnerDAO.update(partner);
		} catch (Exception e) {
			throw new RuntimeException("Error while updating a partner: ", e);
		}

		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		containerCache.add(partner.getContainerContextId(), partner);
		return partner;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public Partner getSourcePartner(String providerId) {
		Partner result = null;
		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		for (Serializable key : containerCache.getKeys()) {
			IContainer container = containerCache.get(key);
			if (container.getContainerType() == ContainerType.PARTNER) {
				Partner partner = (Partner) container;
				if (partner.getProviderId().equals(providerId)
						&& partner.getType() == PartnerType.SOURCE) {
					result = partner;
					break;
				}
			}
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Partner> getAllPartnersAssociatedWithService(long contextId) {
		Set<Partner> partners = new HashSet<Partner>();
		ICacheService<IContainer> containerCache = (ICacheService<IContainer>) ServiceRegistry
				.getDefault().lookup(CONTAINER_CACHE_SERVICE_NAME);
		IContainer container = containerCache.get(contextId);
		if (container.getContainerType() == ContainerType.SERVICE) {
			Service service = (Service) container;
			partners = service.getPartners();
		}
		return partners;
	}

	public int getTotalActivePartnerCount() {
		return this.getAllActivePartners().size();
	}
}
