package application.simulator;
import application.editor.Editor;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.view.mxICellEditor;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
import models.algebra.*;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.ResourcePath;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import parser.Parser;
import parser.Parser.TokenStream;
import parser.exceptions.ExpectedColon;
import parser.exceptions.ExpectedDoubleQuotation;
import parser.exceptions.ExpectedRightBracket;
import parser.exceptions.WrongJsonExpression;
import simulator.Event;
import simulator.Resource;
import simulator.ResourceIdentifier;
import simulator.Simulator;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
public class InputEventCellEditor implements mxICellEditor {
public int DEFAULT_MIN_WIDTH = 70;
public int DEFAULT_MIN_HEIGHT = 30;
public double DEFAULT_MINIMUM_EDITOR_SCALE = 1;
protected double minimumEditorScale = DEFAULT_MINIMUM_EDITOR_SCALE;
protected int minimumWidth = DEFAULT_MIN_WIDTH;
protected int minimumHeight = DEFAULT_MIN_HEIGHT;
private final mxGraphComponent graphComponent;
private final SimulatorWindow simulatorWindow;
private final Simulator simulator;
private final Editor editor;
private Object editingCell;
private EventObject trigger;
private JComboBox<String> comboBox;
public InputEventCellEditor(mxGraphComponent graphComponent, SimulatorWindow simulatorWindow) {
this.graphComponent = graphComponent;
this.simulatorWindow = simulatorWindow;
simulator = simulatorWindow.getSimulator();
editor = simulatorWindow.getEditor();
}
@Override
public Object getEditingCell() {
return editingCell;
}
@Override
public void startEditing(Object cell, EventObject evt) {
if (editingCell != null) {
stopEditing(true);
}
if (!graphComponent.getGraph().getModel().isEdge(cell)) {
Resource res = simulator.getCurState().getResource((String) ((mxCell) cell).getValue());
ResourceIdentifier resId = res.getResourceIdentifier(); // Resource
ArrayList<DataTransferChannel> eventChs = new ArrayList<>(); // IOChannel List
ArrayList<String> messages = new ArrayList<>(); // ADL message
ArrayList<Expression> eventMessages = new ArrayList<>(); // Message list
ResourcePath eventResPath = null;
for (Channel ch : simulator.getModel().getInputChannels()) { // All channel
if (((DataTransferChannel) ch).getInputResources().isEmpty()) { // Input channel or Normal channel
for (ChannelMember out : ((DataTransferChannel) ch).getOutputChannelMembers()) {
ResourcePath resPath = out.getResource();
if (resId.isInstanceOf(resPath)) { // account.uid == accounts.123
eventResPath = resPath;
eventChs.add(((DataTransferChannel) ch));
String message = null;
Expression mesExp = out.getStateTransition().getMessageExpression(); // sync(a,b)
eventMessages.add(mesExp);
if (mesExp instanceof Term) {
message = ((Term) mesExp).getSymbol().toString(); // sync
} else if (mesExp instanceof Variable) {
message = ((Variable) mesExp).getName(); // x,y,z..
}
messages.add(message); // Pull-down
}
}
}
}
if (messages.isEmpty()) {
return;
}
String[] eventList = messages.toArray(new String[messages.size()]);
JComboBox<String> event = new JComboBox<>(eventList);
JPanel eventChoice = new JPanel();
eventChoice.add(event); // FirstEventChoice
int ret = JOptionPane.showConfirmDialog(simulatorWindow, eventChoice, "Event Choice", JOptionPane.OK_CANCEL_OPTION);
if (ret == JOptionPane.OK_OPTION) {
JPanel inputEvent = new JPanel();
int i, eventNum;
i = eventNum = 0;
for (String eventString : eventList) {
if (eventString.equals(event.getSelectedItem().toString())) {
eventNum = i;
}
i++;
}
JTextArea textArea = new JTextArea(eventMessages.get(eventNum).toString(), 10, 30); // EventInput
inputEvent.add(textArea);
int approve = JOptionPane.showConfirmDialog(simulatorWindow, inputEvent, "Event Code", JOptionPane.OK_CANCEL_OPTION);
if (approve == JOptionPane.OK_OPTION) {
try {
TokenStream stream = new Parser.TokenStream();
Parser parser = new Parser(stream);
stream.addLine(textArea.getText());
Expression eventMessage = parser.parseTerm(stream, simulator.getModel());
Event newEvent = new Event(eventChs.get(eventNum), eventMessage, eventResPath, simulator.getCurState().getResource(resId));
simulator.transition(newEvent);
// SimulationLayout layout = new SimulationLayout(simulator.getCurState());
// layout.constructSimulateGraph(graph, simulator);
graphComponent.setCellEditor(new InputEventCellEditor(graphComponent, simulatorWindow));
} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork |
InvalidMessage | UnificationFailed | ValueUndefined | ExpectedRightBracket |
WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) {
e.printStackTrace();
}
}
}
return;
}
mxCellState state = graphComponent.getGraph().getView().getState(cell);
if (state != null && state.getLabel() != null && !state.getLabel().isEmpty()) {
editingCell = cell;
trigger = evt;
double scale = Math.max(minimumEditorScale, graphComponent.getGraph().getView().getScale());
Object value = graphComponent.getGraph().getModel().getValue(cell);
if (value instanceof PushPullAttribute) {
PushPullAttribute attr = (PushPullAttribute) value;
comboBox = new JComboBox<>(attr.getOptionStrings());
comboBox.setBorder(BorderFactory.createEmptyBorder());
comboBox.setOpaque(false);
comboBox.setBounds(getEditorBounds(state, scale));
comboBox.setVisible(true);
graphComponent.getGraphControl().add(comboBox, 0);
comboBox.updateUI();
}
}
}
// private mxGraph constructNextSimulateGraph(Set<Resource> simulateRes, DataTransferModel model,DataFlowGraph dataFlowGraph, mxGraph nextGraph) {
// bReflectingArchitectureModel = true;
// ((mxGraphModel) nextGraph.getModel()).clear();
// Object parent = nextGraph.getDefaultParent();
// nextGraph.getModel().beginUpdate();
//
// try {
// Map<Resource, Object> resources = new HashMap<>();
//
// // create resource vertices
// for (Resource resNode: simulateRes) {
// int w = 400;
// int h = 150;
// ResourcePath res = resNode.getResourceIdentifier();
// Object resource = nextGraph.insertVertex(parent, null,
// res.toString(), x, y, w, h,
// "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex
// resources.put(resNode, resource);
//
// createNextChildSimulateResourceVerticies(resource, resNode, resources, w, h);
// x+=400;
// }
//
// } finally {
// nextGraph.getModel().endUpdate();
// }
//
// bReflectingArchitectureModel = false;
// return nextGraph;
// }
//
// private void createNextChildSimulateResourceVerticies(Object resource, Resource resNode, Map<Resource, Object> resources, int w, int h) { //sample
//
// if(resNode.getChildren() != null) {
// for (Resource childNode: resNode.getChildren()) {
// int i = 1;
// ResourcePath childRes = childNode.getResourceIdentifier();
// Object childResource = graph.insertVertex(resource, null,
// childRes.toString(), x/i, 0, w/(i+1), h/(i+2),
// "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex
// resources.put(childNode, childResource);
// i++;
// createNextChildSimulateResourceVerticies(childResource, childNode, resources, w, h);
// }
// }
// if (resNode.getChildren() == null || resNode.getChildren().size() == 0) {
// Object state = graph.insertVertex(resource, null,
// resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex
// }
// }
@Override
public void stopEditing(boolean cancel) {
if (editingCell != null) {
comboBox.transferFocusUpCycle();
Object cell = editingCell;
editingCell = null;
if (!cancel) {
EventObject trig = trigger;
trigger = null;
Object value = graphComponent.getGraph().getModel().getValue(cell);
if (value instanceof PushPullAttribute) {
PushPullAttribute attr = (PushPullAttribute) value;
List<PushPullValue> options = attr.getOptions();
PushPullValue selected = null;
for (PushPullValue option : options) {
if (option.toString().equals(getCurrentValue())) {
selected = option;
break;
}
}
if (selected != null) {
options.remove(selected);
options.add(0, selected);
}
graphComponent.labelChanged(cell, attr, trig);
}
} else {
mxCellState state = graphComponent.getGraph().getView().getState(cell);
graphComponent.redraw(state);
}
if (comboBox.getParent() != null) {
comboBox.setVisible(false);
comboBox.getParent().remove(comboBox);
}
graphComponent.requestFocusInWindow();
}
}
public String getCurrentValue() {
return (String) comboBox.getSelectedItem();
}
/**
* Returns the bounds to be used for the editor.
*/
public Rectangle getEditorBounds(mxCellState state, double scale) {
mxIGraphModel model = state.getView().getGraph().getModel();
Rectangle bounds;
bounds = state.getLabelBounds().getRectangle();
bounds.height += 10;
// Applies the horizontal and vertical label positions
if (model.isVertex(state.getCell())) {
String horizontal = mxUtils.getString(state.getStyle(), mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
if (horizontal.equals(mxConstants.ALIGN_LEFT)) {
bounds.x -= (int) state.getWidth();
} else if (horizontal.equals(mxConstants.ALIGN_RIGHT)) {
bounds.x += (int) state.getWidth();
}
String vertical = mxUtils.getString(state.getStyle(), mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
if (vertical.equals(mxConstants.ALIGN_TOP)) {
bounds.y -= (int) state.getHeight();
} else if (vertical.equals(mxConstants.ALIGN_BOTTOM)) {
bounds.y += (int) state.getHeight();
}
}
bounds.setSize((int) Math.max(bounds.getWidth(), Math.round(minimumWidth * scale)), (int) Math.max(bounds.getHeight(), Math.round(minimumHeight * scale)));
return bounds;
}
}