package application.simulator;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.view.mxGraph;
import models.dataConstraintModel.ResourcePath;
import simulator.Resource;
import simulator.ResourceIdentifier;
import simulator.Simulator;
import simulator.SystemState;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SimulationLayout {
// Hold the division level for each resource
HashMap<ResourceIdentifier,Integer> divisionLevelMap = new HashMap<>();
// Hold the scaling factor for each resource
HashMap<ResourceIdentifier,Double> resourceScaleMap = new HashMap<>();
int maxDivisionLevel = 1;
public final double BASE_WIDTH = 720;
public final double BASE_HEIGHT = BASE_WIDTH/2;
public final double MARGIN_SCALE = 0.92;
SystemState systemState;
public SimulationLayout(SystemState systemState){
this.systemState = systemState;
// Calculate the division level for each resource
for (Resource res:systemState.getRootResources()) {
setDivisionLevel(divisionLevelMap, res.getResourceIdentifier(), 1);
}
// Calculate the scaling factor for each resource
for (ResourceIdentifier res:divisionLevelMap.keySet()) {
double scale = (double) maxDivisionLevel / divisionLevelMap.get(res);
resourceScaleMap.put(res, scale);
}
}
public double getScale(ResourceIdentifier resource){
return resourceScaleMap.get(resource);
}
public double getDivision(ResourceIdentifier resource){
return divisionLevelMap.get(resource);
}
public int getMaxDivisionLevel() {
return maxDivisionLevel;
}
public boolean isExistResource(ResourceIdentifier resourceIdentifier){
return divisionLevelMap.containsKey(resourceIdentifier);
}
private void setDivisionLevel(HashMap<ResourceIdentifier,Integer> resourceSet, ResourceIdentifier resource, int childCount){
int divisionLevel;
if(resourceSet.get(resource.getParent()) == null){
divisionLevel = 1;
}else{
divisionLevel = resourceSet.get(resource.getParent()) * childCount;
}
if (divisionLevel > maxDivisionLevel)maxDivisionLevel = divisionLevel;
resourceSet.put(resource,divisionLevel);
Collection<Resource> identifiers = systemState.getResource(resource).getChildren();
if (identifiers != null) {
for (Resource child:identifiers) {
setDivisionLevel(resourceSet, child.getResourceIdentifier(), identifiers.size());
}
}
}
/**
*
* Draw an object corresponding to the target resource on the mxGraph.
* @param graph The mxGraph for drawing.
* @param parent The parent object of the target resource.
* @param resourceIdentifier The ID of the target resource.
* @param index The index.
* @param length The number of sibling resources of the target resource.
* @return The drawing object of the target resource.
*/
private Object setLayout(mxGraph graph,Object parent, ResourceIdentifier resourceIdentifier,double index, double length){
double width, height, x, y;
double parentWidth = graph.getCellGeometry(parent).getWidth();
double parentHeight = graph.getCellGeometry(parent).getHeight();
width = parentWidth / length * MARGIN_SCALE;
height = width/2;
x = parentWidth * (index - 1)/length + (parentWidth/length)*(1-MARGIN_SCALE)/2;
// Process to avoid hiding the parent's resource name
if ((int)length == 1) {
y = 20;
height -=20;
} else {
y = parentHeight/2 - height/2;
}
Object result = graph.insertVertex(parent, null,
resourceIdentifier.toString(), x, y, width, height,
"shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;");
System.out.println(result);
return result;
}
public mxGraph constructSimulateGraph(mxGraph graph, Simulator simulator){
((mxGraphModel) graph.getModel()).clear();
Object parent = graph.getDefaultParent();
graph.getModel().beginUpdate();
try {
Map<Resource, Object> resources = new HashMap<>();
int i = 0;
int childCount = simulator.getCurState().getRootResources().size();
// create resource vertices
for (Resource resNode: simulator.getCurState().getRootResources()) {
double scale = this.getScale(resNode.getResourceIdentifier());
double maxDiv = this.getMaxDivisionLevel();
double w = this.BASE_WIDTH * scale / maxDiv / childCount;
double h = this.BASE_HEIGHT * scale / maxDiv / childCount;
double x = w * i + 50;
double y = 20 ;
ResourcePath res = resNode.getResourceIdentifier();
Object resource = graph.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);
i++;
createNextChildSimulateResourceVerticies(graph, resource, resNode, resources);
}
} finally {
graph.getModel().endUpdate();
}
return graph;
}
private void createNextChildSimulateResourceVerticies(mxGraph graph, Object parent, Resource resNode, Map<Resource, Object> resources) { //sample
Collection<Resource> children = resNode.getChildren();
if (children != null) {
//List<Resource> children = resNode.getChildren();
double i = 1;
double childCount = children.size();
for (Resource childNode: children) {
Object childResource = setLayout(graph, parent, childNode.getResourceIdentifier(), i, childCount);
resources.put(childNode, childResource);
i++;
createNextChildSimulateResourceVerticies(graph, childResource, childNode, resources);
}
}
if (children == null || children.size() == 0) {
Object state = graph.insertVertex(parent, 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
}
}
}