package com.tandbergtv.watchpoint.pmm.core;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;

import com.tandbergtv.cms.portal.util.transaction.Transactional;
import com.tandbergtv.metadatamanager.exception.SearchException;
import com.tandbergtv.metadatamanager.factoryImpl.SpecHandlerFactory;
import com.tandbergtv.metadatamanager.model.Asset;
import com.tandbergtv.metadatamanager.model.Field;
import com.tandbergtv.metadatamanager.model.FieldName;
import com.tandbergtv.metadatamanager.model.Item;
import com.tandbergtv.metadatamanager.model.Relation;
import com.tandbergtv.metadatamanager.model.Spec;
import com.tandbergtv.metadatamanager.model.Item.ItemType;
import com.tandbergtv.metadatamanager.spec.IIdentifier;
import com.tandbergtv.metadatamanager.spec.ISpecHandler;
import com.tandbergtv.watchpoint.pmm.dao.hibernate.HibernateContext;
import com.tandbergtv.watchpoint.pmm.entities.IAssetList;
import com.tandbergtv.watchpoint.pmm.entities.Planner;
import com.tandbergtv.watchpoint.pmm.entities.Schedule;
import com.tandbergtv.watchpoint.pmm.entities.Title;
import com.tandbergtv.watchpoint.pmm.entities.TitleListType;
import com.tandbergtv.watchpoint.pmm.entities.TitleStatus;
import com.tandbergtv.watchpoint.pmm.title.ITitleService;
import com.tandbergtv.watchpoint.pmm.util.ProgressStatusHelper;
import com.tandbergtv.watchpoint.pmm.util.RulesEngineFacade;
import com.tandbergtv.watchpoint.pmm.util.lock.LockService;
import com.tandbergtv.workflow.core.service.ServiceRegistry;

/**
 * Event handler class for PMM
 * 
 * @author Raj Prakash
 */
public class PMMService implements IPMMService {
	private static final Logger logger = Logger.getLogger(PMMService.class);
	
	private static final long LOCK_WAIT_TIME = 1000;
	
	private ITitleService titleService;
	private ProgressManager progressManager;
	/*
	private ISchedulePersistenceService schedulePersistenceService;
	private IScheduleSearchService scheduleSearchService;
	private IPartnerManagement partnerManagement;
	*/
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.Service#getServiceName()
	 */
	public String getServiceName() {
		return "PMM Service";
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.ServiceLifecycle#start()
	 */
	public void start() {
		ServiceRegistry defaultServiceRegistry = ServiceRegistry.getDefault(); 
		titleService = defaultServiceRegistry.lookup(ITitleService.class);
		progressManager = ProgressManager.newInstance();
		/*
		schedulePersistenceService = 
			defaultServiceRegistry.lookup(ISchedulePersistenceService.class);
		scheduleSearchService = defaultServiceRegistry.lookup(IScheduleSearchService.class);
		partnerManagement = PartnerManager.getInstance();
		*/
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.ServiceLifecycle#stop()
	 */
	public void stop() { }
	
	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public AssetMapInfo assetArrived(String filePath,
			String sourceComponentName, String sourceEntityName, String sourceId) {

		String fileName = new File(filePath).getName();
		
		Asset foundAsset = null;
		Title rootTitle = null;
		boolean validationSuccess = true;
		
		while(true) {
			/*
			 * Find the title whose any of the filename metadata property has fileName as value.
			 */
			Collection<Asset> foundAssets = titleService.findAssetsWithFileName(fileName);
			
			//if no asset found
			if(foundAssets == null || foundAssets.isEmpty())
				return new AssetMapInfo(AssetMapInfo.Status.NO_MATCH);
			
			//if more than one asset found
			if(foundAssets.size() > 1)
				return new AssetMapInfo(AssetMapInfo.Status.MULTIPLE_MATCHES);
			
			foundAsset = foundAssets.iterator().next();
			//foundAsset is the one that directly contains the fileName
			
			rootTitle = titleService.getTitle(foundAsset.getRoot());
			
			if(LockService.getInstance().lock(rootTitle)) {
				try {
					//update
					for (Relation r : rootTitle.getAsset().getRelations()) {
						if (r.getTargetAsset().getId() == foundAsset.getId()) {
							mapFile(filePath, r.getTargetAsset());
							break;
						}
					}
					
					try {
						titleService.update(rootTitle, sourceComponentName, sourceEntityName, sourceId);
					} catch (TitleValidationException e) {
						titleService.saveAsDraft(rootTitle);
						sendStatus(rootTitle, ProgressStatusHelper.VALIDATION_FAILURE,
								e.getValidationMessagesAsString(), false,
								sourceComponentName, sourceEntityName, sourceId);
						validationSuccess = false;
					}
				} finally {
					LockService.getInstance().unlock(rootTitle);
				}
				break;
			} else {
				logger.debug("Unable to acquire lock. Sleeping.");
				try {
					Thread.sleep(LOCK_WAIT_TIME);
				} catch(InterruptedException e) {}
			}
		}
		
		Long plannerId = findEarliestUpcomingPlannerId(rootTitle);
		
		//send progress status 
		sendAssetReceivedStatus(rootTitle, ((Item) foundAsset).getType(), plannerId,
				sourceComponentName, sourceEntityName, sourceId);
		
		AssetMapInfo mapInfo = new AssetMapInfo(AssetMapInfo.Status.MATCHED_TITLE_UPDATED);
		mapInfo.addMappedTitle(rootTitle.getId(), validationSuccess, ((Item) foundAsset).getType());
		return mapInfo;
	}
	
	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public MetadataMapInfo metadataArrived(String spec, String filePath,
			boolean createIfNotExist, String sourceComponentName,
			String sourceEntityName, String sourceId) throws PMMException {
		
		Document document = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", Boolean.FALSE);
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			document = docBuilder.parse(new File(filePath));
		} catch(Exception e) {
			throw new PMMException("Unable to parse the metadata file: " + filePath, e);
		}
		
		ISpecHandler specHandler = getSpecHandler(spec);
		
		//if asked to update only
		if (!createIfNotExist) {
			//get identifiers of all assets in the document
			List<IIdentifier> identifiers = null;
			try {
				identifiers = specHandler.getIdentifiers(document);
			} catch (Exception e) {
				throw new PMMException("Failed to get asset identifiers from the metadata document.", e);
			}

			//if any asset is not found in the Metadata Manager, return
			for (IIdentifier identifier : identifiers) {
				try {
					identifier.getAsset();
				} catch (SearchException e) {
					return new MetadataMapInfo(MetadataMapInfo.Status.TITLES_NOT_FOUND);
				}
			}
		}
		
		//either all assets in the document are found in Metadata Manager or
		//createIfNotExist flag is true
		
		MetadataMapInfo mapInfo = new MetadataMapInfo(MetadataMapInfo.Status.TITLES_MERGED);
		
		AssetsManager.getThreadLocalInstance().beginTransaction();
		boolean saveSuccessful = false;
		try {
			List<Asset> mergedAssets = null;
			try {
				//create/update all assets in the document into Metadata Manager 
				mergedAssets = specHandler.mergeWithoutSave(document);
			} catch (Exception e) {
				throw new PMMException("Failed to save the metadata document.", e);
			}
			
			if(mergedAssets != null) {
				for(Asset mergedAsset : mergedAssets) {
					boolean validationSuccess = true;
					
					//attach metadata file to the root asset
					mapFile(filePath, mergedAsset);

					//find & map unmapped assets
					List<Asset> mappedAssets = mapFiles(mergedAsset);

					//flag that notes if a new title was created (true) or an existing title is updated (false)
					boolean created;
					
					//save
					Title assetTitle = null;
					try {
						assetTitle = titleService.getTitle(mergedAsset);
						created = false;
					} catch(NoTitleForAnAssetException e) {
						assetTitle = new Title();
						created = true;
					}
					assetTitle.setAsset(mergedAsset);
					
					try {
						if(created) {
							titleService.create(assetTitle,
									sourceComponentName, sourceEntityName, sourceId);
							
							RulesEngineFacade.sendIngestedCreatedEvent(assetTitle.getId());
						} else {
							titleService.update(assetTitle,
									sourceComponentName, sourceEntityName, sourceId);
							
							RulesEngineFacade.sendIngestedUpdatedEvent(assetTitle.getId());
						}
					} catch(TitleValidationException e) {
						titleService.saveAsDraft(assetTitle);
						sendStatus(assetTitle, ProgressStatusHelper.VALIDATION_FAILURE,
								e.getValidationMessagesAsString(), false,
								sourceComponentName, sourceEntityName, sourceId);
						validationSuccess = false;
					}

					Long plannerId = findEarliestUpcomingPlannerId(assetTitle);
					
					//send progress status for received metadata
					sendMetadataReceivedStatus(assetTitle, plannerId,
							sourceComponentName, sourceEntityName, sourceId);
					
					//send progress status for mapped assets
					for(Asset asset : mappedAssets) {
						sendAssetReceivedStatus(assetTitle, ((Item) asset).getType(), plannerId,
								sourceComponentName, sourceEntityName, sourceId);
					}

					//add mapping info
					List<String> titleNames = new ArrayList<String>();
					for(Asset asset : mappedAssets) {
						titleNames.add(((Item) asset).getType());
					}
					mapInfo.addMappedTitle(assetTitle.getId(), validationSuccess, titleNames);
				}
			}
			
			saveSuccessful = true;
		} finally {
			if(saveSuccessful) {
				AssetsManager.getThreadLocalInstance().commitTransaction();
			} else {
				try {
					AssetsManager.getThreadLocalInstance().rollbackTransaction();
				} catch(Exception e) {
					logger.error("Rolling back failed. | " + e.getMessage(), e);
				}
			}
		}

		return mapInfo;
	}

	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public List<Asset> mapFiles(Asset rootAsset) {
		List<Asset> mappedAssets = new ArrayList<Asset>();
		for(Asset asset : rootAsset.getAllDescendantItems(false)) {
			Item item = (Item) asset;
			
			//get filename in item
			String fileName = getFileName(item);
			
			//if there is no fileName metadata, skip this item
			if(fileName == null)
				continue;
			
			//get matching unmapped file
			File matchingUnmappedFile = null;
			try {
				matchingUnmappedFile = AssetsManager.getThreadLocalInstance().getUnmappedFile(fileName);
			} catch (Exception e) {
				logger.error("Exception when trying to get unmapped file with name: " +
						fileName, e);
			}

			//if there is a matching unmapped file
			if(matchingUnmappedFile != null) {
				//move the unmapped file to mapped files directory
				File movedFile = null;
				try {
					movedFile = AssetsManager.getThreadLocalInstance()
							.moveToMappedFilesDirectory(matchingUnmappedFile);
				} catch (Exception e) {
					logger.error("Unable to move unmapped asset to mapped assets directory. " +
							"Mapping ignored. " +
							"Unmapped asset path: " + matchingUnmappedFile.getAbsolutePath(), e);
				}
				
				if(movedFile != null) {
					//map the file (mark others as non-original)
					mapFile(movedFile.getAbsolutePath(), item);
					mappedAssets.add(item);
					logger.debug("Mapped file: " + movedFile.getAbsolutePath() + " to item: " + item);
				}
			}
		}
		return mappedAssets;
	}
	

	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public void programListArrived(Collection<Title> titles)
			throws PMMException {
		if(titles == null || titles.isEmpty()) {
			throw new IllegalArgumentException("Titles collection is null or empty");
		}
		
		HibernateContext.getContext().beginTransaction();
		boolean success = false;
		try {
			for(Title title : titles) {
				fillRequiredData(title);
				createOrUpdate(title);
			}
			
			HibernateContext.getContext().commitTransaction();
			success = true;
		} finally {
			if(!success) {
				try {
					HibernateContext.getContext().rollbackTransaction();
				} catch(Exception e) {
					logger.warn("Exception in rolling back the transaction.", e);
				}
			}
		}
	}


	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public void plannersArrived(Collection<Planner> planners)
			throws PMMException, IllegalArgumentException {
		if(planners == null || planners.isEmpty())
			throw new IllegalArgumentException("Planners collection is null or empty");
			
		HibernateContext.getContext().beginTransaction();
		boolean success = false;
		try {
			for(Planner planner : planners) {
				try {
					plannerArrived(planner);
				} catch(PMMException e) {
					throw new PMMException(
							"Ingesting planner [Provider ID: " + planner.getProviderID() 
							+ " | Arrival Date: " + planner.getArrivalDate()
							+ "] failed | Reason: " + e.getMessage(), e);
				} catch(RuntimeException e) {
					throw new PMMException(
							"Ingesting planner [Provider ID: " + planner.getProviderID() 
							+ " | Arrival Date: " + planner.getArrivalDate()
							+ "] failed | Reason: " + e.getMessage(), e);
				}
			}
			HibernateContext.getContext().commitTransaction();
			success = true;
		} finally {
			if(!success) {
				try {
					HibernateContext.getContext().rollbackTransaction();
				} catch(Exception e) {
					logger.warn("Exception in rolling back the transaction.", e);
				}
			}
		}
	}


	/**
	 * Ingests the planner.
	 * Does not manage the transaction.
	 */
	protected void plannerArrived(Planner planner)
			throws PMMException {
		/*
		logger.debug("Ingesting planner. | ProviderID: " + planner.getProviderID() +
				" Arrival Date: " + planner.getArrivalDate());
		
		//input validation
		if(planner.getProviderID() == null || planner.getProviderID().trim().length() == 0)
			throw new PMMException("Planner Provider ID cannot be null or empty");
		if(planner.getArrivalDate() == null)
			throw new PMMException("Planner Arrival Date cannot be null");
		if(planner.getTitles() == null || planner.getTitles().isEmpty())
			throw new PMMException("Planner does not have any titles");

		//fill in missing information
		Partner partner = partnerManagement.getSourcePartner(planner.getProviderID());
		if(partner == null) {
			throw new PMMException("No source partner found for providerId: " +
					planner.getProviderID());
		}
		
		planner.setSourcePartnerID(partner.getId());
		planner.setStatus(ScheduleStatus.NEW);
		
		for(Title title : planner.getTitles())
			fillRequiredData(title);
		
		Collection<Planner> matchedPlanners = 
			scheduleSearchService.getPlannersByDate(planner.getSourcePartnerID(),
					planner.getArrivalDate());
		
		if(matchedPlanners != null && matchedPlanners.size() > 1) {
			throw new PMMException("Multiple planners found for partner [providerid: " + 
					planner.getProviderID() + "] and arrival date [" +
					planner.getArrivalDate() + "]" );
		}
		
		//if there are no matching planners
		if(matchedPlanners == null || matchedPlanners.isEmpty()) {
			//create or update titles
			Collection<Title> dbTitles = createOrUpdate(planner.getTitles()); 

			//create a new planner
			Planner newPlanner = new Planner(planner.getSourcePartnerID(), planner.getArrivalDate());
			
			//save the planner
			newPlanner.setTitles(dbTitles);
			schedulePersistenceService.saveWithoutManagingTransaction(newPlanner);
		} else {	//if there is a matching planner
			Planner matchedPlanner = matchedPlanners.iterator().next();
			
			//if the planner titles do not have complete key, reject the schedule
			if(!firstTitleHasCompleteKey) {
				throw new PMMException("Planner already exist for [providerid: " + 
						planner.getProviderID() + "] and arrival date [" +
						planner.getArrivalDate() + "] and this planner contain titles " +
						"that do not have complete key");
			}
			
			//create or update titles
			Collection<Title> dbTitles = createOrUpdate(planner.getTitles());

			//save the planner
			matchedPlanner.setTitles(dbTitles);
			schedulePersistenceService.saveWithoutManagingTransaction(matchedPlanner);
		}

		logger.debug("Planner ingested. | Planner: " + planner);
		*/
	}

	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public void sendAssetReceivedStatus(Title rootTitle, String titleName,
			String sourceComponentName, String sourceEntityName, String sourceId) {
		Long plannerId = findEarliestUpcomingPlannerId(rootTitle);
		sendAssetReceivedStatus(rootTitle, titleName, plannerId,
				sourceComponentName, sourceEntityName, sourceId);
	}
	
	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public void sendMetadataReceivedStatus(Title rootTitle,
			String sourceComponentName, String sourceEntityName, String sourceId) {
		Long plannerId = findEarliestUpcomingPlannerId(rootTitle);
		sendMetadataReceivedStatus(rootTitle, plannerId,
				sourceComponentName, sourceEntityName, sourceId);
	}
	
	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public void sendStatus(Title title, String statusName, String statusValue, boolean success,
			String sourceComponentName, String sourceEntityName, String sourceId) {
		Long plannerId = findEarliestUpcomingPlannerId(title);
		progressManager.updateStatus(title.getId(), plannerId,
				statusName, statusValue, success,
				sourceComponentName, sourceEntityName, sourceId);
	}
	
	/** 
	 * {@inheritDoc}
	 */
	@Transactional
	public void updateTitleStatus(Title rootTitle) {
		Asset rootAsset = rootTitle.getAsset();
		
		//if the title status is approved, do nothing
		if(rootTitle.getStatus() == TitleStatus.APPROVED)
			return;
		
		//if the title is imported from an external system, it is assumed to be ready
		if(containsValue(rootTitle.getExternalLocation())) {
			rootTitle.setStatus(TitleStatus.READY);
			return;
		}
		
		rootTitle.setStatus(TitleStatus.READY);

		//check metadata
		if(!hasOriginalFile(rootAsset)) {
			rootTitle.setStatus(TitleStatus.NEW);
			return;
		}
		
		//check assets
		for(Asset sectionAsset : rootAsset.getAllDescendantItems(false)) {
			Item sectionItem = (Item) sectionAsset;
			//if the item is supposed to have a file
			if(getFileName(sectionItem) != null) {
				//if it does not have an original file
				if(!hasOriginalFile(sectionItem)) {
					rootTitle.setStatus(TitleStatus.NEW);
					return;
				}
			}
		}
	}
	
	protected ISpecHandler getSpecHandler(String spec) {
		return SpecHandlerFactory.getInstance(Spec.valueOf(spec.toUpperCase()));
	}

	/**
	 * Maps the given file to the given asset.
	 */
	protected void mapFile(String filePath, Asset asset) {
		//mark other attached files as not original
		List<com.tandbergtv.metadatamanager.model.File> files =
			asset.getChildrenOfType(com.tandbergtv.metadatamanager.model.File.class);
		if(files != null)
			for(com.tandbergtv.metadatamanager.model.File file : files)
				file.setOriginal(false);
		
		//create a File item
		com.tandbergtv.metadatamanager.model.File newFile = new com.tandbergtv.metadatamanager.model.File();
		newFile.setOriginal(true);
		newFile.setType(ItemType.FILE);
		newFile.addField(new Field(FieldName.FILE_URL.toString(), filePath));
		String size = new File(filePath).length() + "";
		newFile.addField(new Field(FieldName.FILE_SIZE.toString(), size));
		
		//attach the file item to the given asset
		asset.addChild(newFile);
	}
	
	/**
	 *	Gets fileName field value referred in the given item. 
	 */
	protected String getFileName(Item item) {
		Field fileNameField = item.getFirstField(FieldName.URL.toString());
		String fileName = (fileNameField != null) ? fileNameField.getValue() : null;
		
		//if fileName string is empty, set as null
		if(fileName != null) {
			fileName = fileName.trim();
			if(fileName.length() == 0)
				fileName = null;
		}
		
		return fileName;
	}
	
	/**
	 * Checks if the given asset has a original file.
	 */
	protected boolean hasOriginalFile(Asset asset) {
		List<com.tandbergtv.metadatamanager.model.File> files = 
			asset.getChildrenOfType(com.tandbergtv.metadatamanager.model.File.class);
		
		if(files != null)
			for(com.tandbergtv.metadatamanager.model.File file : files)
				if(file.isOriginal())
					return true;
		
		return false;
	}

	protected void sendAssetReceivedStatus(Title rootTitle, String titleName, Long plannerId,
			String sourceComponentName, String sourceEntityName, String sourceId) {
		
		String statusName = ProgressStatusHelper.getAssetReceivedStatusName(titleName);
		String statusValue = ProgressStatusHelper.getAssetReceivedStatusValue(titleName);		
		
		if (statusName != null) {
			// send progress status
			progressManager.updateStatus(rootTitle.getId(), plannerId,
					statusName, statusValue, true,
					sourceComponentName, sourceEntityName, sourceId);
		}
	}
	
	protected void sendMetadataReceivedStatus(Title rootTitle, Long plannerId,
			String sourceComponentName, String sourceEntityName, String sourceId) {
		
		String statusName = ProgressStatusHelper.getMetadataReceivedStatusName();
		String statusValue = ProgressStatusHelper.getMetadataReceivedValue();		
		
		if (statusName != null) {
			// send progress status
			progressManager.updateStatus(rootTitle.getId(), plannerId,
					statusName, statusValue, true,
					sourceComponentName, sourceEntityName, sourceId);
		}
	}
	
	/**
	 * Gets id of the earliest upcoming (>= today) planner that is associated with
	 * the given title. Returns null, if no upcoming planner found.
	 */
	protected Long findEarliestUpcomingPlannerId(Title title) {
		List<Schedule> upcomingPlanners = new ArrayList<Schedule>();
		
		//find associated upcoming planners
		Date now = getToday();
		Collection<IAssetList> associatedAssetLists = title.getTitlelists();
		if(associatedAssetLists != null) {
			for(IAssetList associatedAssetList : associatedAssetLists) {
				if(associatedAssetList.getType() == TitleListType.PLANNER) {
					Schedule associatedPlanner = (Schedule) associatedAssetList; 
					if(associatedPlanner.getDate().compareTo(now) >= 0) {
						upcomingPlanners.add(associatedPlanner);
					}
				}
			}
		}
		
		if(upcomingPlanners.isEmpty())
			return null;
		
		//find earliest in associated upcoming planners
		Collections.sort(upcomingPlanners, new Comparator<Schedule>() {
			public int compare(Schedule o1, Schedule o2) {
				return o1.getDate().compareTo(o2.getDate());
			}
		});
		

		return upcomingPlanners.get(0).getId(); 
	}
	
	/**
	 * Creates or Updates all the given titles.
	 * Does not manage the transaction.
	 */
	protected List<Title> createOrUpdate(Collection<Title> titles) {
		List<Title> dbTitles = new ArrayList<Title>();
		if(titles != null) {
			for(Title title : titles) {
				Title dbTitle = createOrUpdate(title);
				dbTitles.add(dbTitle);
			}
		}
		return dbTitles;
	}
	
	/**
	 * Checks if the given title has complete composite key.
	 * If yes,
	 * 		Checks if it already exist in the PMM database based on the key.
	 * 		If yes, updates the entity with the given object. Or else, creates a new one.
	 * Else,
	 * 		Creates a new one in the database.
	 */
	protected Title createOrUpdate(Title title) {
		//TODO
		/*
		//get key metadata from the given title
		Set<MetadataField> keyMetadata = titleService.getKeyMetadata(title);
		
		if(keyMetadata != null) {
			//search for title in database that has the same spec and key
			Collection<Title> foundTitles =
				titleService.findRootTitles(title.getSpecification(), keyMetadata); 
			if(foundTitles != null && !foundTitles.isEmpty()) {
				if(foundTitles.size() > 1) {
					throw new RuntimeException("Multiple titles found for the same key " +
							keyMetadata);
				}
				Title titleInDB = foundTitles.iterator().next();

				//update the title in database
				TitleUtil.update(title, titleInDB);
				titleService.saveWithoutManagingTransaction(titleInDB);
				
				return titleInDB;
			}
		}
		
		//create the given title in database
		titleService.saveWithoutManagingTransaction(title);
			
		return title;
		*/
		
		return null;
	}

	/**
	 * Sets active flag to true, status to NEW.
	 */
	protected void fillRequiredData(Title rootTitle) {
		rootTitle.setIsActive(true);
		rootTitle.setStatus(TitleStatus.NEW);
	}
	
	/**
	 * Gets the date value of 0 hours today.
	 */
	protected Date getToday() {
		Calendar c = Calendar.getInstance();
		
		//set time to start of today
		c.set(Calendar.HOUR_OF_DAY, 0);
		c.set(Calendar.MINUTE, 0);
		c.set(Calendar.SECOND, 0);
		c.set(Calendar.MILLISECOND, 0);
		
		return c.getTime();
	}
	
	/**
	 * Checks if the given string contains a not null, not empty and a not blank value.
	 */
	protected boolean containsValue(String s) {
		return (s != null && s.trim().length() > 0);
	}

}
