package application.simulator;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.util.EventObject;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
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 com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;
import application.editor.Editor;
import application.layouts.DAGLayout;
import models.algebra.Expression;
import models.algebra.InvalidMessage;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.Position;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.UnificationFailed;
import models.algebra.ValueUndefined;
import models.algebra.Variable;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import models.visualModel.FormulaChannel;
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.ChannelState;
import simulator.Resource;
import simulator.ResourceIdentifier;
import simulator.Simulator;
import simulator.Event;
import simulator.SystemState;
public class InputEventCellEditor implements mxICellEditor {
public int DEFAULT_MIN_WIDTH = 70;
public int DEFAULT_MIN_HEIGHT = 30;
public double DEFAULT_MINIMUM_EDITOR_SCALE = 1;
private double x = 20;
private double y = 20;
protected double minimumEditorScale = DEFAULT_MINIMUM_EDITOR_SCALE;
protected int minimumWidth = DEFAULT_MIN_WIDTH;
protected int minimumHeight = DEFAULT_MIN_HEIGHT;
private SimulatorWindow window;
private Object editingCell;
private EventObject trigger;
private JComboBox<String> comboBox;
private mxGraphComponent graphComponent;
private Simulator simulator;
private JComboBox<String> pulldownMenu;
private Editor editor;
private boolean bReflectingArchitectureModel = false;
public InputEventCellEditor(SimulatorWindow window, mxGraphComponent graphComponent, Simulator simulator, Editor editor) {
this.window = window;
this.graphComponent = graphComponent;
this.simulator = simulator;
this.editor = editor;
}
@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(); // clicked resource
ArrayList<DataTransferChannel> eventChs = new ArrayList<>(); // event channel list
ArrayList<String> messageTexts = new ArrayList<>(); // message text list
ArrayList<Expression> messageExps = new ArrayList<>(); // message expression list
ArrayList<TreeMap<Integer, Expression>> refParams = new ArrayList<>(); // message parameters for ref ports
ResourcePath eventResPath = null;
for (Channel ch: simulator.getModel().getInputChannels()) { // all channels
eventResPath = getSelectableMessages(ch, resId, eventChs, messageTexts, messageExps, refParams, eventResPath);
}
if (messageTexts.isEmpty()) {
return;
}
String[] messageList = messageTexts.toArray(new String[messageTexts.size()]);
JComboBox<String> messageMenu = new JComboBox<String>(messageList);
JPanel eventSelectPanel = new JPanel();
eventSelectPanel.add(messageMenu);
int ret = JOptionPane.showConfirmDialog(window, eventSelectPanel, "Event Choice", JOptionPane.OK_CANCEL_OPTION); // Select an event.
if (ret == JOptionPane.OK_OPTION) {
int i, messageIdx;
i = messageIdx = 0;
for (String messageText : messageList) {
if(messageText.equals(messageMenu.getSelectedItem().toString())) {
messageIdx = i;
}
i++;
}
JPanel inputEventPanel = new JPanel();
JTextArea textArea = new JTextArea(messageExps.get(messageIdx).toString(), 10, 30);
inputEventPanel.add(textArea);
int approve = JOptionPane.showConfirmDialog(window, inputEventPanel, "Event Code", JOptionPane.OK_CANCEL_OPTION); // Input an message text for the event.
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());
if (eventMessage instanceof Term) {
TreeMap<Integer, Expression> refMap = refParams.get(messageIdx);
for (Integer paramIdx: refMap.keySet()) {
((Term) eventMessage).getSymbol().setArity(-1);
((Term) eventMessage).addChild(paramIdx, refMap.get(paramIdx), true);
}
}
Event newEvent = new Event(eventChs.get(messageIdx), eventMessage, eventResPath, simulator.getCurState().getResource(resId));
simulator.transition(newEvent);
graphComponent.setCellEditor(new InputEventCellEditor(window, graphComponent, simulator, this.editor));
} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
| InvalidMessage | UnificationFailed | ValueUndefined | ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) {
e.printStackTrace();
}
}
}
// resource
return;
}
mxCellState state = graphComponent.getGraph().getView().getState(cell);
if (state != null && state.getLabel() != null && !state.getLabel().equals("")) {
editingCell = cell;
trigger = evt;
double scale = Math.max(minimumEditorScale, graphComponent.getGraph().getView().getScale());
Object value = graphComponent.getGraph().getModel().getValue(cell);
if (value != null && 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 ResourcePath getSelectableMessages(Channel ch, ResourceIdentifier resId,
ArrayList<DataTransferChannel> eventChs, ArrayList<String> messageTexts, ArrayList<Expression> messageExps, ArrayList<TreeMap<Integer, Expression>> refParams,
ResourcePath eventResPath) {
if (((DataTransferChannel) ch).getInputResources().size() == 0) { // event ch. or normal ch.
for (ChannelMember out: ((DataTransferChannel) ch).getOutputChannelMembers()) {
ResourcePath resPath = out.getResource();
if (!out.isOutside() && resId.isInstanceOf(resPath)) {
eventResPath = resPath;
eventChs.add(((DataTransferChannel) ch));
String messageText = null;
Expression mesExp = out.getStateTransition().getMessageExpression();
TreeMap<Integer, Expression> refMap = new TreeMap<>();
if (mesExp instanceof Term) {
// Reconstruct an input message template
List<Expression> pathParams = resPath.getPathParams();
List<Expression> children = ((Term) mesExp).getChildren();
mesExp = new Term(((Term) mesExp).getSymbol());
for (int i = 0; i < children.size(); i++) {
Expression child = children.get(i);
boolean isRefVar = false;
for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) {
if (refCm.getStateTransition().getMessageExpression() instanceof Term) {
Expression varExp = ((Term) refCm.getStateTransition().getMessageExpression()).getChild(i);
if (varExp != null && varExp instanceof Variable) {
if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) {
// child has come from a reference resource.
isRefVar = true;
break;
}
}
}
}
if (!isRefVar) {
// child has not come from a reference resource.
if (!pathParams.contains(child)) {
((Term) mesExp).addChild(child);
} else {
int idx = pathParams.indexOf(child);
((Term) mesExp).addChild(resId.getPathParams().get(idx));
}
} else {
// child has come from a reference resource.
refMap.put(i, child);
}
}
messageText = ((Term) mesExp).getSymbol().toString();
} else if(mesExp instanceof Variable) {
messageText = ((Variable) mesExp).getName();
}
messageExps.add(mesExp);
messageTexts.add(messageText); // for the pull-down menu
refParams.add(refMap);
}
}
for (Channel childCh: ch.getChildren()) {
eventResPath = getSelectableMessages(childCh, resId, eventChs, messageTexts, messageExps, refParams, eventResPath);
}
}
return eventResPath;
}
@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 != null && 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) {
attr.selectOption(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 = null;
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 -= state.getWidth();
} else if (horizontal.equals(mxConstants.ALIGN_RIGHT)) {
bounds.x += state.getWidth();
}
String vertical = mxUtils.getString(state.getStyle(),
mxConstants.STYLE_VERTICAL_LABEL_POSITION,
mxConstants.ALIGN_MIDDLE);
if (vertical.equals(mxConstants.ALIGN_TOP)) {
bounds.y -= state.getHeight();
} else if (vertical.equals(mxConstants.ALIGN_BOTTOM)) {
bounds.y += state.getHeight();
}
}
bounds.setSize(
(int) Math.max(bounds.getWidth(),
Math.round(minimumWidth * scale)),
(int) Math.max(bounds.getHeight(),
Math.round(minimumHeight * scale)));
return bounds;
}
}