/**
 * Copyright (C) 2019 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
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;
    }

}
