/*
 * Copyright (c) 2011, Patrik Dufresne. All rights reserved.
 * Patrik Dufresne PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.planimod.ui.views.nonavailabilities;

import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.FilterIterator;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.value.IObservableValue;

import com.patrikdufresne.managers.ManagerException;
import com.patrikdufresne.managers.databinding.ManagedObjectComputedSet;
import com.planimod.core.TimeRange;
import com.planimod.core.TimeRanges;
import com.planimod.core.NonAvailability;
import com.planimod.core.NonAvailabilityManager;
import com.planimod.core.PlanimodManagers;

/**
 * This computed set wrap the
 * {@link NonAvailabilityManager#listByVisibility(Date, Date, boolean)}
 * function.
 * 
 * @author Patrik Dufresne
 * 
 */
public class NonAvailabilitiesComputedSet extends ManagedObjectComputedSet {

	/**
	 * The observable time range.
	 */
	private IObservableValue timerange;
	/**
	 * True to only return calendar events to be displayed.
	 */
	private boolean visibility;

	/**
	 * Create a new computed set.
	 * 
	 * @param managers
	 *            the managers to use
	 * @param timerange
	 *            the observable timerange
	 * @param visibility
	 *            True to display item with the same visibility.
	 */
	public NonAvailabilitiesComputedSet(PlanimodManagers managers,
			IObservableValue timerange, boolean visibility) {
		super(managers, NonAvailability.class, new IObservable[] { timerange });
		if (timerange == null) {
			throw new NullPointerException();
		}
		this.timerange = timerange;
		this.visibility = visibility;
	}

	/**
	 * This implementation is calling the function
	 */
	@Override
	protected Collection doList() throws ManagerException {
		TimeRange t = getTimeRange();
		if (t == null) {
			return Collections.EMPTY_SET;
		}
		// Filter non-availabilities
		final List<NonAvailability> list = ((PlanimodManagers) getManagers())
				.getNonAvailabilityManager().listByVisibility(t.getStartDate(),
						t.getEndDate(), this.visibility);
		return new AbstractSet() {

			@Override
			public Iterator iterator() {
				return new FilterIterator(list.iterator(), new Predicate() {
					@Override
					public boolean evaluate(Object object) {
						return doSelect(object);
					}
				});
			}

			@Override
			public int size() {
				int count = 0;
				Iterator iter = iterator();
				while (iter.hasNext()) {
					iter.next();
					count++;
				}
				return count;
			}

		};
	}

	/**
	 * This implementation check if the element is part of a shift calendar
	 * entry and is within the time range.
	 */
	@Override
	protected boolean doSelect(Object element) {
		TimeRange t = getTimeRange();
		if (t == null) {
			return false;
		}
		NonAvailability na = (NonAvailability) element;
		return this.visibility == na.isVisible()
				&& TimeRanges.intersectEquals(na, t.getStartDate(),
						t.getEndDate());
	}

	/**
	 * Utility function return the observable time range or null if not set.
	 * 
	 * @return
	 */
	protected TimeRange getTimeRange() {
		if (this.timerange.getValue() instanceof TimeRange) {
			return (TimeRange) this.timerange.getValue();
		}
		return null;
	}

}
