class SelectionManager implements VizArchitecture {

    private final List<ChangeListener> listeners;
    private VizConfig vizConfig;
    private AbstractEngine engine;
    //Settings
    private int mouseSelectionDiameter;
    private boolean mouseSelectionZoomProportionnal;
    private boolean selectionUpdateWhileDragging;
    //States
    private boolean blocked = false;
    private boolean wasAutoSelectNeighbors = false;
    private boolean wasRectangleSelection = false;
    private boolean wasDragSelection = false;
    private boolean wasDirectSelection = false;

    public SelectionManager() {
        listeners = new ArrayList<>();
    }

    @Override
    public void initArchitecture() {
        this.vizConfig = VizController.getInstance().getVizConfig();
        this.engine = VizController.getInstance().getEngine();
        mouseSelectionDiameter = vizConfig.getMouseSelectionDiameter();
        selectionUpdateWhileDragging = vizConfig.isMouseSelectionUpdateWhileDragging();
    }

    public void blockSelection(boolean block) {
        if (vizConfig.isRectangleSelection()) {
            this.blocked = block;
            vizConfig.setSelectionEnable(!block);
            fireChangeEvent();
        } else {
            setDirectMouseSelection();
        }
    }

    public void disableSelection() {
        vizConfig.setSelectionEnable(false);
        this.blocked = false;
        fireChangeEvent();
    }

    public void setDraggingEnable(boolean dragging) {
        vizConfig.setMouseSelectionUpdateWhileDragging(!dragging);
        fireChangeEvent();
    }

    public void setRectangleSelection() {
        vizConfig.setDraggingEnable(false);
        vizConfig.setCustomSelection(false);
        vizConfig.setSelectionEnable(true);
        engine.setRectangleSelection(true);
        this.blocked = false;
        fireChangeEvent();
    }

    public void setDirectMouseSelection() {
        vizConfig.setSelectionEnable(true);
        vizConfig.setDraggingEnable(false);
        vizConfig.setCustomSelection(false);
        engine.setRectangleSelection(false);
        this.blocked = false;
        fireChangeEvent();
    }

    public void setDraggingMouseSelection() {
        vizConfig.setDraggingEnable(true);
        vizConfig.setMouseSelectionUpdateWhileDragging(false);
        vizConfig.setSelectionEnable(true);
        vizConfig.setCustomSelection(false);
        engine.setRectangleSelection(false);
        this.blocked = false;
        fireChangeEvent();
    }

    public void setCustomSelection() {
        vizConfig.setSelectionEnable(false);
        vizConfig.setDraggingEnable(false);
        vizConfig.setCustomSelection(true);
        engine.setRectangleSelection(false);
        //this.blocked = true;
        fireChangeEvent();
    }

    public void resetSelection(VizModel vizModel) {<FILL_FUNCTION_BODY>}

    public List<Node> getSelectedNodes() {
        return engine.getSelectedUnderlyingNodes();
    }

    public List<Edge> getSelectedEdges() {
        return engine.getSelectedUnderlyingEdges();
    }

    public void selectNodes(Node[] nodes, VizModel vizModel) {
        if (!isCustomSelection()) {
            wasAutoSelectNeighbors = vizModel.isAutoSelectNeighbor();
            wasDragSelection = isDraggingEnabled();
            wasDirectSelection = isDirectMouseSelection();
            wasRectangleSelection = isRectangleSelection();
            vizModel.setAutoSelectNeighbor(false);
            setCustomSelection();
        }

        Model[] models = engine.getNodeModelsForNodes(nodes);
        engine.selectObject(models);
    }

    public void selectEdges(Edge[] edges, VizModel vizModel) {
        if (!isCustomSelection()) {
            wasAutoSelectNeighbors = vizModel.isAutoSelectNeighbor();
            wasDragSelection = isDraggingEnabled();
            wasDirectSelection = isDirectMouseSelection();
            wasRectangleSelection = isRectangleSelection();
            vizModel.setAutoSelectNeighbor(false);
            setCustomSelection();
        }

        Model[] models = engine.getEdgeModelsForEdges(edges);
        engine.selectObject(models);
    }

    public void centerOnNode(Node node) {
        if (node != null) {
            VizController.getInstance().getGraphIO()
                .centerOnCoordinate(node.x(), node.y(), node.z() + node.size() * 40);
            engine.getScheduler().requireUpdateVisible();
        }
    }

    public void centerOnEdge(Edge edge) {
        if (edge != null) {
            Node source = edge.getSource();
            Node target = edge.getTarget();
            float len = (float) Math.hypot(source.x() - target.x(), source.y() - target.y());
            VizController.getInstance().getGraphIO()
                .centerOnCoordinate((source.x() + target.x()) / 2f, (source.y() + target.y()) / 2f,
                    (source.z() + target.z()) / 2f + len * 5);
            engine.getScheduler().requireUpdateVisible();
        }
    }

    public int getMouseSelectionDiameter() {
        return mouseSelectionDiameter;
    }

    public void setMouseSelectionDiameter(int mouseSelectionDiameter) {
        this.mouseSelectionDiameter = mouseSelectionDiameter;
    }

    public boolean isMouseSelectionZoomProportionnal() {
        return mouseSelectionZoomProportionnal;
    }

    public void setMouseSelectionZoomProportionnal(boolean mouseSelectionZoomProportionnal) {
        this.mouseSelectionZoomProportionnal = mouseSelectionZoomProportionnal;
    }

    public boolean isSelectionUpdateWhileDragging() {
        return selectionUpdateWhileDragging;
    }

    public void setSelectionUpdateWhileDragging(boolean selectionUpdateWhileDragging) {
        this.selectionUpdateWhileDragging = selectionUpdateWhileDragging;
    }

    public boolean isBlocked() {
        return blocked;
    }

    public boolean isRectangleSelection() {
        return vizConfig.isSelectionEnable() && vizConfig.isRectangleSelection();
    }

    public boolean isDirectMouseSelection() {
        return vizConfig.isSelectionEnable() && !vizConfig.isRectangleSelection() && !vizConfig.isDraggingEnable();
    }

    public boolean isCustomSelection() {
        return vizConfig.isCustomSelection();
    }

    public boolean isSelectionEnabled() {
        return vizConfig.isSelectionEnable();
    }

    public boolean isDraggingEnabled() {
        return vizConfig.isDraggingEnable();
    }

    //Event
    public void addChangeListener(ChangeListener changeListener) {
        listeners.add(changeListener);
    }

    public void removeChangeListener(ChangeListener changeListener) {
        listeners.remove(changeListener);
    }

    private void fireChangeEvent() {
        ChangeEvent evt = new ChangeEvent(this);
        for (ChangeListener l : listeners) {
            l.stateChanged(evt);
        }
    }
}

        if (isCustomSelection()) {
            engine.resetSelection();
            vizModel.setAutoSelectNeighbor(wasAutoSelectNeighbors);
            if (wasRectangleSelection) {
                setRectangleSelection();
            } else if (wasDragSelection) {
                setDraggingMouseSelection();
            } else if (wasDirectSelection) {
                setDirectMouseSelection();
            }
        }
    
<no_super_class>