package nl.utwente.viskell.ui.components;
import java.util.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import javafx.geometry.Pos;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import nl.utwente.viskell.haskell.expr.Binder;
import nl.utwente.viskell.haskell.expr.ConstructorBinder;
import nl.utwente.viskell.haskell.expr.Expression;
import nl.utwente.viskell.haskell.type.TupleTypeCon;
import nl.utwente.viskell.haskell.type.Type;
import nl.utwente.viskell.haskell.type.TypeApp;
import nl.utwente.viskell.haskell.type.TypeScope;
import nl.utwente.viskell.ui.ToplevelPane;
public class SplitterBlock extends Block {
/** The InputAnchor of the input tuple. */
private final InputAnchor input;
/** A list of OutputAnchors for every tuple element. */
private final List<OutputAnchor> outputs;
/** The pattern matching binder for the whole output. */
private ConstructorBinder primaryBinder;
public SplitterBlock(ToplevelPane pane, int arity) {
super(pane);
this.input = new InputAnchor(this);
this.outputs = new ArrayList<>();
List<Binder> elemBinders = new ArrayList<>();
for (int i = 0; i < arity; i++) {
Binder eb = new Binder("e_"+i);
elemBinders.add(eb);
this.outputs.add(new OutputAnchor(this, eb));
}
this.primaryBinder = new ConstructorBinder(TupleTypeCon.tupleName(arity), elemBinders);
HBox inputSpace = new HBox(this.input);
inputSpace.setPickOnBounds(false);
inputSpace.setAlignment(Pos.CENTER);
HBox outputSpace = new HBox(40);
outputSpace.getChildren().addAll(this.outputs);
outputSpace.setPickOnBounds(false);
outputSpace.setAlignment(Pos.CENTER);
int xbase = 10; // = anchor_width / 2
int xstep = (arity - 1) * 5; // 5 = horizontal_spacing / 8
int ystep = 8; // = height / 3
Polygon splitter = new Polygon(
-xbase-4*xstep, 3*ystep, xbase+4*xstep, 3*ystep,
xbase+3*xstep, 2*ystep, xbase+1*xstep, 1*ystep,
xbase+0*xstep, 0*ystep, -xbase-0*xstep, 0*ystep,
-xbase-1*xstep, 1*ystep, -xbase-3*xstep, 2*ystep);
splitter.setFill(Color.MIDNIGHTBLUE);
splitter.setPickOnBounds(true);
VBox body = new VBox(inputSpace, splitter, outputSpace);
body.setPickOnBounds(false);
this.getChildren().add(body);
this.setPickOnBounds(false);
}
/** @return class-specific properties of this Block. */
@Override
protected Map<String, Object> toBundleFragment() {
return ImmutableMap.of("arity", this.outputs.size());
}
public static SplitterBlock fromBundleFragment(ToplevelPane pane, Map<String,Object> bundleFragment) {
int arity = ((Double)bundleFragment.get("arity")).intValue();
return new SplitterBlock(pane, arity);
}
@Override
public List<InputAnchor> getAllInputs() {
return ImmutableList.of(input);
}
@Override
public List<OutputAnchor> getAllOutputs() {
return this.outputs;
}
@Override
protected void refreshAnchorTypes() {
List<Type> elems = new ArrayList<>();
for (int i = 0; i < this.outputs.size(); i++) {
elems.add(TypeScope.unique("e_"+i));
}
this.input.setFreshRequiredType(Type.tupleOf(elems.toArray(new Type[elems.size()])), new TypeScope());
List<Type> tupleElems = ((TypeApp)this.input.getType()).asFlattenedAppChain();
tupleElems.remove(0);
for (int i = 0; i < this.outputs.size(); i++) {
this.outputs.get(i).setExactRequiredType(tupleElems.get(i));
}
}
@Override
public Expression getLocalExpr(Set<OutputAnchor> outsideAnchors) {
return this.input.getLocalExpr(outsideAnchors);
}
@Override
public void invalidateVisualState() {
this.input.invalidateVisualState();
}
@Override
public Optional<Block> getNewCopy() {
return Optional.of(new SplitterBlock(this.getToplevel(), this.outputs.size()));
}
public Binder getPrimaryBinder() {
return primaryBinder;
}
}