package nl.utwente.viskell.ui.components;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import nl.utwente.viskell.ghcj.GhciSession;
import nl.utwente.viskell.haskell.expr.Expression;
import nl.utwente.viskell.haskell.type.Type;
import nl.utwente.viskell.haskell.type.TypeScope;
import nl.utwente.viskell.ui.ComponentLoader;
import nl.utwente.viskell.ui.ToplevelPane;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* A CLaSH-specific block that is a counterpart to the `simulate` function in Clash. Expects a function that turns a
* Signal of monotonically increasing numbers into a Signal of representable result values.
*
* While signals are infinite, SimulateBlock only shows the first N iterations/steps.
*/
public class SimulateBlock extends Block implements ComponentLoader {
/** The Anchor that is used as input. */
protected InputAnchor inputAnchor;
/** The space containing the input anchor. */
@FXML protected Pane inputSpace;
/** The label on which to display type information. */
@FXML protected Label inputType;
/** The label on which to display the value of this block */
@FXML protected Label value;
@FXML protected Button iterationLabel;
/** Constrained type variable for the input anchor */
private final Type funConstraint;
/** The number of results to calculate and show */
private int iteration;
public SimulateBlock(ToplevelPane pane) {
super(pane);
loadFXML("SimulateBlock");
inputAnchor = new InputAnchor(this);
inputSpace.getChildren().add(inputAnchor);
iteration = 0;
String signature = "(Num a, Show b) => Signal a -> Signal b";
funConstraint = getToplevel().getEnvInstance().buildType(signature);
}
@SuppressWarnings("UnusedParameters")
public static SimulateBlock fromBundleFragment(ToplevelPane pane, Map<String, Object> bundleFragment) {
return new SimulateBlock(pane);
}
@Override
public final void invalidateVisualState() {
this.inputAnchor.invalidateVisualState();
inputType.setText(inputAnchor.getStringType());
if (this.inValidContext && inputAnchor.hasValidConnection()) {
GhciSession ghciSession = getToplevel().getGhciSession();
String format = "Data.List.take %d $ simulate (%s) [1..]";
String expr = String.format(format, iteration, inputAnchor.getFullExpr().toHaskell());
ListenableFuture<String> result = ghciSession.pullRaw(expr);
// See DisplayBlock.invalidateVisualState
Futures.addCallback(result, new FutureCallback<String>() {
public void onSuccess(String s) { Platform.runLater(() -> value.setText(s)); }
public void onFailure(Throwable t) { Platform.runLater(() -> value.setText("?!?!?!")); }
});
} else {
value.setText("?");
}
}
/** Step to the next iteration. */
public void step() {
setIteration(iteration + 1);
}
/** Reset to the first iteration. */
public void reset() {
setIteration(0);
}
private void setIteration(int i) {
iteration = i;
iterationLabel.setText(String.valueOf(iteration));
this.invalidateVisualState();
}
@Override
public List<InputAnchor> getAllInputs() {
return ImmutableList.of(inputAnchor);
}
@Override
public List<OutputAnchor> getAllOutputs() {
return ImmutableList.of();
}
@Override
public Optional<Block> getNewCopy() {
return Optional.empty();
}
@Override
public Expression getLocalExpr(Set<OutputAnchor> outsideAnchors) {
return inputAnchor.getLocalExpr(outsideAnchors);
}
@Override
public void refreshAnchorTypes() {
inputAnchor.setFreshRequiredType(funConstraint, new TypeScope());
}
@Override
public boolean checkValidInCurrentContainer() {
if (this.container instanceof LambdaContainer || this.container instanceof Lane) {
return false;
} else {
return super.checkValidInCurrentContainer();
}
}
}