/*
 * Copyright (C) 2017 COSLING S.A.S.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.variables.SetVar;
import org.chocosolver.util.ESat;

/**
 * Simple Choco Solver propagator to ensure that at most one set variable is empty
 * @author Jean-Guillaume FAGES (cosling)
 * @version choco-solver-4.0.4
 */
public class PropAtLeastOneEmptySet extends Propagator<SetVar> {

	public PropAtLeastOneEmptySet(SetVar... vars) {
		super(vars);
	}

	@Override
	public void propagate(int evtmask) throws ContradictionException {
		SetVar toEmpty = null;
		for (SetVar var : vars) {
			if (var.getUB().isEmpty()) {
				// the constraint is always satisfied, no matter what happens next
				// setPassive() avoids the Propagator to be called next time
				setPassive();
				return;
			} else {
				// if the lower bound is empty, that set COULD be empty
				if (var.getLB().isEmpty()) {
					if (toEmpty == null) {
						toEmpty = var;
					} else {
						// two variables probably empty -> we can't know which one must be empty
						return;
					}
				}
			}
		}
		if (toEmpty != null) {
			// We remove all the elements from the upper bound to make this SetVar empty
			for (int v : toEmpty.getUB()) {
				toEmpty.remove(v,this);
			}
		} else {
			// the constraint cannot be satisfied if all the sets can't be empty
			fails();
		}
	}

	@Override
	public ESat isEntailed() {
		// number of non empty sets
		int nbNE = 0;
		for (SetVar var : vars) {
			// if this set is instantiated and has no element, it is empty
			if (var.getUB().isEmpty()) {
				return ESat.TRUE;
			}
			// if the lower bound of the variable is not empty, the SetVar can't be empty
			if (!var.getLB().isEmpty()) {
				nbNE++;
			}
		}
		// If ALL the SetVars cannot be empty, the constraint cannot be satisfied
		if (nbNE == vars.length) {
			return  ESat.FALSE;
		}
		// Otherwise we cannot state about the satisfaction
		return ESat.UNDEFINED;
	}
}