/*
* Copyright 2013-2016 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE 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.
*
* The CCRE 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 the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.channel;
import ccre.time.Time;
import ccre.verifier.SetupPhase;
/**
* An event input or source. This produces events when it fires. A user can
* register listeners to be called when the EventInput fires.
*
* @see EventCell
* @author skeggsc
*/
public interface EventInput extends UpdatingInput {
/**
* An EventInput that will never be fired. Ever.
*/
public static EventInput never = new EventInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
if (notify == null) {
throw new NullPointerException();
}
return CancelOutput.nothing;
}
};
/**
* Register a listener for when this event is fired, so that whenever this
* event is fired, the specified output will get fired as well.
*
* If the same listener is added multiple times, it has the same effect as
* if it was added once.
*
* @param output the listener to add.
* @return a CancelOutput that deregisters the registered EventOutput. DO
* NOT FIRE THIS RETURNED EVENT MORE THAN ONCE: UNDEFINED BEHAVIOR MAY
* RESULT.
*/
@SetupPhase
public default CancelOutput send(EventOutput output) {
if (output == null) {
throw new NullPointerException();
}
return onUpdate(output);
}
/**
* Provides an EventInput that fires when either this EventInput or
* <code>other</code> fires.
*
* @param other the other EventInput.
* @return the combined EventInput.
*/
@SetupPhase
public default EventInput or(EventInput other) {
if (other == null) {
throw new NullPointerException();
}
EventInput original = this;
return new EventInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
return original.onUpdate(notify).combine(other.onUpdate(notify));
}
};
}
/**
* Provides an EventInput that fires only when this EventInput fires and the
* current value of <code>allow</code> is true.
*
* @param allow if events should be allowed through.
* @return the restricted EventInput.
*/
@SetupPhase
public default EventInput and(BooleanInput allow) {
if (allow == null) {
throw new NullPointerException();
}
EventInput original = this;
return new DerivedEventInput(original) {
@Override
protected boolean shouldProduce() {
return allow.get();
}
};
}
/**
* Provides an EventInput that fires only when this EventInput fires and the
* current value of <code>deny</code> is false.
*
* @param deny if events should be allowed through.
* @return the restricted EventInput.
*/
@SetupPhase
public default EventInput andNot(BooleanInput deny) {
if (deny == null) {
throw new NullPointerException();
}
EventInput original = this;
return new DerivedEventInput(original) {
@Override
protected boolean shouldProduce() {
return !deny.get();
}
};
}
/**
* Provides a debounced version of this EventInput, such that any events
* that occur within <code>minMillis</code> of the last event will be
* dropped.
*
* Only events that are propagated reset the timer: if an event is ignored,
* it does not extend the reactivation deadline.
*
* @param minMillis the minimum spacing between events.
* @return the debounced version of this EventInput.
*/
@SetupPhase
public default EventInput debounced(final long minMillis) {
if (minMillis <= 0) {
throw new IllegalArgumentException("debounced() parameter must be positive!");
}
return new DerivedEventInput(this) {
private long nextFire = 0;
@Override
protected boolean shouldProduce() {
long now = Time.currentTimeMillis();
if (now < nextFire) {
return false;// Ignore event.
}
nextFire = now + minMillis;
return true;
}
};
}
}