package com.tandbergtv.watchpoint.pmm.core;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.tandbergtv.watchpoint.pmm.dao.hibernate.HibernateContext;
import com.tandbergtv.watchpoint.pmm.entities.TitleProperty;

/**
 * DAO for TitleProperty entity.
 * 
 * @author Raj Prakash
 */
public class TitlePropertyManager {
	private static Logger logger = Logger.getLogger(TitlePropertyManager.class);
	
	/**
	 * Gets a new instance of this class.
	 */
	public static TitlePropertyManager getInstance() {
		return new TitlePropertyManager();
	}
	
	/**
	 * Gets the property entity from database that matches the given criteria.
	 * 
	 * @param name			name of the property
	 * @param titleId		database id of the title to which the property is associated 
	 * @param assetListId	database id of the asset list to which the property
	 * 						is associated (can be null)
	 * @param contextId		database id of the context to which the property
	 * 						is associated (can be null)
	 * @return
	 */
	public TitleProperty retrieve(String name, long titleId, Long assetListId, Long contextId) {
		Session session = null;
		try {
			session = getSession();
			return retrieve(session, name, titleId, assetListId, contextId);
		} finally {
			closeSession(session);
		}
	}
	
	/*
	 * Gets the property entity from database that matches the given criteria.
	 * 
	 * @param session	session to use for db operations
	 */
	 private TitleProperty retrieve(Session session, String name, long titleId, Long assetListId,
			Long contextId) {
		
		StringBuffer querySB = new StringBuffer();
		querySB.append("from TitleProperty p where p.name = :name and p.titleId = :titleId and ");
		
		if(assetListId != null)
			querySB.append("assetListId = :assetListId and ");
		else
			querySB.append("assetListId is null and ");
		
		if(contextId != null)
			querySB.append("contextId = :contextId");
		else
			querySB.append("contextId is null");
			
		
		Query query = session.createQuery(querySB.toString());
		
		query.setParameter("name", name);
		query.setParameter("titleId", titleId);
		if(assetListId != null)
			query.setParameter("assetListId", assetListId);
		if(contextId != null)
			query.setParameter("contextId", contextId);
		
		return (TitleProperty) query.uniqueResult();
	}
	
	/**
	 * Updates or creates the given property object in the database.
	 * 
	 * @param property	the property to update or create
	 * @return			the updated or newly created propery
	 */
	public TitleProperty store(TitleProperty property) {
		if(property.getName() == null || property.getName().trim().length() == 0)
			throw new IllegalArgumentException("name cannot be null or empty");
		if(property.getTitleId() == null)
			throw new IllegalArgumentException("titleId cannot be null");
		
		Session session = getSession();
		try {
			TitleProperty propertyInDB = retrieve(session, property.getName(), property.getTitleId(),
					property.getAssetListId(), property.getContextId());
			if(propertyInDB != null) {
				propertyInDB.setValue(property.getValue());
				save(session, propertyInDB);
				return propertyInDB;
			} else {
				property.setId(null);
				save(session, property);
				return property;
			}
		} finally {
			closeSession(session);
		}
		
	}
	
	/**
	 * Saves the given property object to the database using the given session.
	 */
	private void save(Session session, TitleProperty property) {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			session.saveOrUpdate(property);
			tx.commit();
			logger.debug("Saved TitleProperty " + property);
		} catch (Exception e) {
			if (tx != null)
				try {
					tx.rollback();
				} catch(HibernateException he) {
					logger.warn("Transaction rollback failed", he);
				}
			throw new RuntimeException("Failed to save " + property, e);
		}
	}

	/**
	 * Gets a hibernate session.
	 */
	private Session getSession() {
		return HibernateContext.getContext().openSession();
	}
	
	/**
	 * Closes the given session.
	 */
	private void closeSession(Session session) {
		if (session != null)
			session.close();
	}	

}
