/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille
 *
 * ludovic.apvrille AT enst.fr
 *
 * This software is a computer program whose purpose is to allow the
 * edition of TURTLE analysis, design and deployment diagrams, to
 * allow the generation of RT-LOTOS or Java code from this diagram,
 * and at last to allow the analysis of formal validation traces
 * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP
 * from INRIA Rhone-Alpes.
 *
 * This software is governed by the CeCILL  license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 */


package graph;

import common.ConfigurationTTool;
import common.SpecConfigTTool;
import myutil.TraceManager;
import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.MultiGraph;
import org.graphstream.stream.ProxyPipe;
import org.graphstream.ui.geom.Point2;
import org.graphstream.ui.geom.Point3;
import org.graphstream.ui.graphicGraph.GraphicGraph;
import org.graphstream.ui.swing.SwingGraphRenderer;
import org.graphstream.ui.swing_viewer.DefaultView;
import org.graphstream.ui.swing_viewer.SwingViewer;
import org.graphstream.ui.view.View;
import org.graphstream.ui.view.Viewer;
import org.graphstream.ui.view.ViewerListener;
import org.graphstream.ui.view.camera.Camera;
import org.graphstream.ui.view.util.ShortcutManager;
import ui.file.PNGFilter;
import ui.util.IconManager;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.logging.Logger;


/**
 * Class AUTGraphDisplay
 * Creation : 01/12/2016
 * * @version 1.0 01/12/2016
 *
 * @author Ludovic APVRILLE
 */
public class AUTGraphDisplay implements MouseListener, ViewerListener, Runnable {

    protected static String STYLE_SHEET = "node {fill-color: #B1CAF1; text-color: black; size: 20px, 20px; text-size:14;}     " +
            "edge {text-color: black; shape: cubic-curve; text-size:10;}    " +
            "edge.defaultedge {text-size:10; text-color:black;}  " +
            "edge.external {text-color:blue; text-size:14;}    " +
            "node.deadlock {fill-color: orange; text-color: red; size: 20px, 20px; text-size:15;}    " +
            "node.init { fill-color: green; text-color: black; size: 20px, 20px; text-size:15;}";
    protected static String STYLE_SHEET2 = "graph {  canvas-color: white; fill-mode: gradient-vertical; fill-color: white, #004; padding: 20px;  } " +
            "node { shape: circle; size-mode: dyn-size; size: 10px; fill-mode: gradient-radial; fill-color: #FFFC, #FFF0; stroke-mode: none; " +
            "shadow-mode: gradient-radial; shadow-color: #FFF5, #FFF0; shadow-width: 5px; shadow-offset: 0px, 0px; } " +
            "node: clicked { fill-color: #F00A, #F000;  } node: selected { fill-color: #00FA, #00F0;  } " +
            "edge { shape: angle; size: 1px; fill-color: red; fill-mode: plain; arrow-shape: circle; } " +
            "edge.defaultedge { shape: curve-cubic; size: 1px; fill-color: #FFF3; fill-mode: plain; arrow-shape: none; } " +
            "edge.external { shape: L-square-line; size: 3px; fill-color: #AAA3; fill-mode: plain; arrow-shape: circle; } " +
            "sprite { shape: circle; fill-mode: gradient-radial; fill-color: #FFF8, #FFF0; }";
    protected AUTGraph graph;
    protected SwingViewer viewer;
    protected MultiGraph vGraph;
    protected boolean loop;
    protected Node firstNode;
    protected ArrayList<Edge> edges;
    protected boolean exitOnClose = false;
    protected BasicFrame bf;

    // see http://graphstream-project.org/doc/Advanced-Concepts/GraphStream-CSS-Reference/
    protected boolean forceAutoLayout;
    protected String USED_STYLE_SHEET = "";


    public AUTGraphDisplay(AUTGraph _graph, boolean _exitOnClose, boolean _forceAutoLayout) {
        graph = _graph;
        exitOnClose = _exitOnClose;
        forceAutoLayout = _forceAutoLayout;
        System.setProperty("org.graphstream.ui", "swing");
    }


    public void display() {
        Node node;
        Edge edge;

        Logger l0 = Logger.getLogger("");
        try {
            if (l0 != null) {
                l0.removeHandler(l0.getHandlers()[0]);
            }
        } catch (Exception e) {
        }

        //System.setProperty("org.graphstream.ui.renderer", "org.graphstream.ui.j2dviewer.J2DGraphRenderer");

        vGraph = new MultiGraph("TTool graph");
        if ((ConfigurationTTool.RGStyleSheet != null) && (ConfigurationTTool.RGStyleSheet.trim().length() > 0)) {
            TraceManager.addDev("Adding stylesheet:" + ConfigurationTTool.RGStyleSheet + "\n\nvs default:" + STYLE_SHEET);
            vGraph.setAttribute("ui.stylesheet", ConfigurationTTool.RGStyleSheet);
            USED_STYLE_SHEET = ConfigurationTTool.RGStyleSheet;
        } else {
            vGraph.setAttribute("ui.stylesheet", STYLE_SHEET);
            USED_STYLE_SHEET = STYLE_SHEET;
        }
        //vGraph.setAttribute("ui.stylesheet", STYLE_SHEET);


        //vGraph.addAttribute("layout.weight", 0.5);
        int cpt = 0;
        graph.computeStates();
        for (AUTState state : graph.getStates()) {
            node = vGraph.addNode("" + state.id);

            if (state.info == null) {
                node.setAttribute("ui.label", "" + state.id);
            } else {
                node.setAttribute("ui.label", "" + state.id + "/" + state.info);
            }

            if (state.getNbOutTransitions() == 0) {
                node.setAttribute("ui.class", "deadlock");
            }

            if ((cpt == 0) || (state.isOrigin)) {
                node.setAttribute("ui.class", "init");
                firstNode = node;
            }
            cpt++;
        }
        cpt = 0;
        //TraceManager.addDev("Here we are!");
        edges = new ArrayList<Edge>(graph.getTransitions().size());
        HashSet<AUTTransition> transitionsMet = new HashSet<>();
        for (AUTTransition transition : graph.getTransitions()) {
            edge = vGraph.addEdge("" + cpt, "" + transition.origin, "" + transition.destination, true);
            /*TraceManager.addDev("Transition=" + transition.transition);
              String tmp = Conversion.replaceAllChar(transition.transition, '(', "$");
              tmp = Conversion.replaceAllChar(tmp, ')', "$");
              TraceManager.addDev("Transition=" + tmp);*/
            edge.setAttribute("ui.label", graph.removeOTime(graph.removeSameSignal(graph.getCompoundString(transition, transitionsMet))));
            //edge.setAttribute("ui.style", "text-offset: -50, -50;");
            //edge.addAttribute("ui.class", "edge");
            //edge.addAttribute("shape", "cubic-curve");
            //edge.addAttribute("arrow-shape", "circle");
            edge.setAttribute("layout.weight", 0.4);
            if (!(transition.transition.startsWith("i("))) {
                edge.setAttribute("ui.class", "external");
            } else {
                edge.setAttribute("ui.class", "defaultedge");
            }
            edges.add(edge);
            cpt++;
        }
        //viewer = vGraph.display();
        //viewer = new Viewer(vGraph, Viewer.ThreadingModel.GRAPH_IN_SWING_THREAD);

        viewer = new SwingViewer(vGraph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
        viewer.enableXYZfeedback(true);


        //SwingUtilities.invokeLater(new InitializeApplication(viewer, vGraph));
        viewer.enableAutoLayout();
        //View   vi = viewer.addDefaultView(true);
        //vi.addMouseListener(this);


        //viewer.setCloseFramePolicy(Viewer.CloseFramePolicy.CLOSE_VIEWER);
        bf = new BasicFrame(this, viewer, vGraph, graph, edges, exitOnClose, forceAutoLayout);


        loop = true;

        Thread t = new Thread(this);
        t.start();

    }

    public void sleep(int time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public void run() {
        /*ViewerPipe fromViewer = viewer.newViewerPipe();
        fromViewer.addViewerListener(this);
        fromViewer.addSink(vGraph);*/


        // Then we need a loop to do our work and to wait for events.
        // In this loop we will need to call the
        // pump() method before each use of the graph to copy back events
        // that have already occurred in the viewer thread inside
        // our thread

        int cpt = 0;
        //ViewerPipe pipeIn = viewer.newViewerPipe();
        ProxyPipe pipe = viewer.newViewerPipe();
        pipe.addAttributeSink(vGraph);
        //pipeIn.addSink(vGraph);
        //pipeIn.addViewerListener(this);


        while (loop) {
            try {
                //TraceManager.addDev("pumping in first thread");
                Thread.sleep(100);
                pipe.pump();
            } catch (Exception e) {
                TraceManager.addDev("pumping exception: " + e.getMessage());
            }


            if (vGraph.hasAttribute("ui.viewClosed")) {
                //TraceManager.addDev("View was closed");
                loop = false;
                if (exitOnClose) {
                    System.exit(1);
                }
            }
            //TraceManager.addDev("bf.updateMe");
            if (bf != null)
                bf.updateMe();
        }

    }


    public void buttonPushed(String id) {
        TraceManager.addDev("Button pushed on node " + id);
    }

    public void buttonReleased(String id) {
        TraceManager.addDev("Button released on node " + id);
    }

    public void viewClosed(String id) {
        //TraceManager.addDev("View closed and closed !");
        loop = false;
        if (viewer != null) {
            viewer.close();
            viewer.disableAutoLayout();
        }
        viewer = null;
        vGraph.clear();
        if (exitOnClose) {
            System.exit(1);
        }
    }


    public void mousePressed(MouseEvent e) {
        TraceManager.addDev("Mouse pressed; # of clicks: "
                + e.getClickCount());
    }

    public void mouseReleased(MouseEvent e) {
        TraceManager.addDev("Mouse released; # of clicks: "
                + e.getClickCount());
    }

    public void mouseEntered(MouseEvent e) {
        TraceManager.addDev("Mouse entered");
    }

    public void mouseExited(MouseEvent e) {
        TraceManager.addDev("Mouse exited");
    }

    public void mouseClicked(MouseEvent e) {
        TraceManager.addDev("Mouse clicked (# of clicks: "
                + e.getClickCount() + ")");
    }


    public void mouseOver(java.lang.String id) {
        TraceManager.addDev("Mouse over: " + id);
    }

    public void mouseLeft(java.lang.String id) {
        TraceManager.addDev("Mouse over: " + id);
    }


    class BasicFrame extends JFrame implements ActionListener {
        public AffineTransform transform;
        protected MultiGraph vGraph;
        protected SwingViewer viewer;
        protected JPanel viewerPanel;
        protected AUTGraph graph;
        protected ArrayList<Edge> edges;
        protected JButton close;
        protected JButton screenshot;
        protected JButton fontPlus, fontMinus;
        protected JButton resetView;
        protected JCheckBox autoLayout;
        protected JCheckBox internalActions;
        protected JCheckBox readActions;
        protected JCheckBox higherQuality, antialiasing;
        protected JLabel help, info;
        protected MouseEvent last;

        protected boolean exitOnClose;

        private AUTGraphDisplay autD;

        private DefaultView dv;
        private Node selectedNode;

        private boolean forceAutoLayout;


        public BasicFrame(AUTGraphDisplay autD, SwingViewer viewer, MultiGraph vGraph, AUTGraph autgraph, ArrayList<Edge> _edges,
                          boolean _exitOnClose, boolean _forceAutoLayout) {
            this.autD = autD;
            this.viewer = viewer;
            this.vGraph = vGraph;
            this.graph = autgraph;
            forceAutoLayout = _forceAutoLayout;


            edges = _edges;
            exitOnClose = _exitOnClose;
            makeComponents();

            if (exitOnClose) {
                setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            }

        }


        public void processDrag(MouseEvent event) {
        /*if (last != null) {
            Camera camera = dv.getCamera();
            Point3 p1 = camera.getViewCenter();
            Point3 p2 = camera.transformGuToPx(p1.x,p1.y,0);
            int xdelta=event.getX()-last.getX();//determine direction
            int ydelta=event.getY()-last.getY();//determine direction

            p2.x-=xdelta;
            p2.y-=ydelta;
            Point3 p3= camera.transformPxToGu(p2.x,p2.y);
            camera.setViewCenter(p3.x,p3.y, 0);
        }
        last=event;*/
        }

        public void resetDrag() {
            //this.last=null;
        }


        public void makeComponents() {


            dv = new DefaultView(viewer, "Graph", new SwingGraphRenderer());

            MouseListener[] listeners = dv.getMouseListeners();
            for (MouseListener l : listeners) {
                dv.removeMouseListener(l);
            }

            MouseMotionListener[] mL = dv.getMouseMotionListeners();
            for (MouseMotionListener l : mL) {
                dv.removeMouseMotionListener(l);
            }

            KeyListener[] keyListeners = dv.getKeyListeners();
            for (KeyListener k : keyListeners) {
                dv.removeKeyListener(k);
            }



            /*((Component) dv).addKeyListener(new KeyListener() {
                @Override
                public void keyTyped(KeyEvent e) {
                    TraceManager.addDev("Key pressed event:" + e.getKeyCode());
                    updateMe();
                }

                @Override
                public void keyPressed(KeyEvent e) {
                    TraceManager.addDev("Key pressed event:" + e.getKeyCode());
                    updateMe();
                }

                @Override
                public void keyReleased(KeyEvent e) {

                    TraceManager.addDev("Key event:" + e.getKeyCode());
                    updateMe();
                }
            });*/


            dv.addMouseMotionListener(new MouseMotionListener() {

                @Override
                public void mouseDragged(MouseEvent e) {
                    //TraceManager.addDev("Dragged!");
                    if (selectedNode != null) {
                        double diffX = 1;
                        double diffY = 1;
                        if (transform != null) {
                            diffX = transform.getScaleX();
                            diffY = transform.getScaleY();
                        }
                        Point3 p = dv.getCamera().transformPxToGu(e.getX() * diffX, e.getY() * diffY);
                        selectedNode.setAttribute("x", p.x);
                        selectedNode.setAttribute("y", p.y);
                    }
                    processDrag(e);
                }

                @Override
                public void mouseMoved(MouseEvent e) {

                }
            });

            dv.addMouseListener(new MouseListener() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    //TraceManager.addDev("Mouse clicked!");
                }

                @Override
                public void mousePressed(MouseEvent e) {


                    Graphics2D g = (Graphics2D) dv.getGraphics();
                    transform = g.getFontRenderContext().getTransform();
                    //TraceManager.addDev("Affine transform = " + transform);


                    //setAutoLayout(false);
                    //viewer.disableAutoLayout();
                    //TraceManager.addDev("Mouse pressed!");

                    Node node = findNodeAt(e.getX(), e.getY());
                    if (node != null) {
                        //TraceManager.addDev("Mouse pressed at node: " + node.getId());
                        selectedNode = node;
                    } else {
                        //TraceManager.addDev("Mouse pressed not on a node");
                    }

                    resetDrag();

                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    //TraceManager.addDev("Mouse released!");
                    selectedNode = null;
                    //viewer.enableAutoLayout();
                }

                @Override
                public void mouseEntered(MouseEvent e) {

                }

                @Override
                public void mouseExited(MouseEvent e) {

                }
            });


            /*((Component) dv).addMouseWheelListener(new MouseWheelListener() {
                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    e.consume();
                    int i = e.getWheelRotation();
                    TraceManager.addDev("Wheel rotation:" + i);
                    double factor = Math.pow(1.02, i);
                    Camera cam = dv.getCamera();
                    double zoom = cam.getViewPercent() * factor;
                Point2 pxCenter  = cam.transformGuToPx(cam.getViewCenter().x, cam.getViewCenter().y, 0);
                double diffX = 1;
                double diffY = 1;
                if (transform != null) {
                    diffX = transform.getScaleX();
                    diffY = transform.getScaleY();
                }
                Point3 guClicked = cam.transformPxToGu(e.getX()*diffX, e.getY()*diffY);

                double newRatioPx2Gu = cam.getMetrics().ratioPx2Gu/factor;
                double x = guClicked.x + (pxCenter.x - e.getX())/newRatioPx2Gu;
                double y = guClicked.y - (pxCenter.y - e.getY())/newRatioPx2Gu;
                cam.setViewCenter(x, y, 0);
                    cam.setViewPercent(zoom);
                }
            });*/


            //Graphics g = dv.getGraphics();
            //TraceManager.addDev("Transform:" + ((Graphics2D)g).getTransform());
            //TraceManager.addDev("scale:" + System.getProperty("sun.java2d.uiScale"));


            add(dv, BorderLayout.CENTER);

            //add(viewer, BorderLayout.CENTER );
            close = new JButton("Close", IconManager.imgic27);
            close.addActionListener(this);
            screenshot = new JButton("Save in png", IconManager.imgic28);
            screenshot.addActionListener(this);
            fontPlus = new JButton(IconManager.imgic144);
            fontPlus.addActionListener(this);
            fontMinus = new JButton(IconManager.imgic146);
            fontMinus.addActionListener(this);
            resetView = new JButton("Reset view");
            resetView.addActionListener(this);
            autoLayout = new JCheckBox("Auto layout", true);
            autoLayout.addActionListener(this);
            close.addActionListener(this);
            help = new JLabel("Zoom with mouse wheel or PageUp/PageDown, move with cursor keys");
            help = new JLabel("click on a node and drag it to move it");
            info = new JLabel("Graph: " + graph.getNbOfStates() + " states, " + graph.getNbOfTransitions() + " transitions");
            internalActions = new JCheckBox("Display internal actions", true);
            internalActions.addActionListener(this);
            readActions = new JCheckBox("Display read/write actions", true);
            readActions.addActionListener(this);
            higherQuality = new JCheckBox("Higher drawing quality", false);
            higherQuality.addActionListener(this);
            antialiasing = new JCheckBox("Anti aliasing", false);
            antialiasing.addActionListener(this);


            JPanel jp01 = new JPanel();
            GridBagLayout gridbag01 = new GridBagLayout();
            GridBagConstraints c01 = new GridBagConstraints();
            jp01.setLayout(gridbag01);
            jp01.setBorder(new javax.swing.border.TitledBorder("Options"));
            //c01.gridwidth = 1;
            c01.gridheight = 1;
            c01.weighty = 1.0;
            c01.weightx = 1.0;
            c01.fill = GridBagConstraints.HORIZONTAL;
            c01.gridwidth = GridBagConstraints.REMAINDER; //end row
            jp01.add(screenshot);
            jp01.add(internalActions);
            jp01.add(readActions);
            jp01.add(higherQuality);
            jp01.add(antialiasing);
            if (!forceAutoLayout) {
                jp01.add(autoLayout);
            }
            jp01.add(fontMinus);
            jp01.add(fontPlus);
            jp01.add(resetView);

            JPanel infoPanel = new JPanel(new BorderLayout());
            JPanel labelPanel = new JPanel(new BorderLayout());
            labelPanel.add(help, BorderLayout.EAST);
            labelPanel.add(info, BorderLayout.WEST);
            infoPanel.add(labelPanel, BorderLayout.NORTH);
            infoPanel.add(close, BorderLayout.SOUTH);
            infoPanel.add(jp01, BorderLayout.CENTER);


            add(infoPanel, BorderLayout.SOUTH);
            //setDefaultCloseOperation(EXIT_ON_CLOSE);
            setSize(1000, 700);
            setVisible(true);
        }

        public void actionPerformed(ActionEvent evt) {
            if (evt.getSource() == close) {
                closeFrame();
            } else if (evt.getSource() == screenshot) {
                screenshot();
            } else if (evt.getSource() == internalActions) {
                manageInternalActions();
            } else if (evt.getSource() == readActions) {
                manageReadActions();
            } else if (evt.getSource() == higherQuality) {
                manageHigherQuality();
            } else if (evt.getSource() == antialiasing) {
                manageAntialiasing();
            } else if (evt.getSource() == fontPlus) {
                fontPlus();
            } else if (evt.getSource() == fontMinus) {
                fontMinus();
            } else if (evt.getSource() == resetView) {
                dv.getCamera().resetView();
                updateMe();
            } else if (evt.getSource() == autoLayout) {
                if (autoLayout.isSelected()) {
                    viewer.enableAutoLayout();
                } else {
                    viewer.disableAutoLayout();
                }
            }
        }

        public void closeFrame() {
            if (autD != null) {
                autD.viewClosed("closed pushed");
            }
            if (exitOnClose) {
                System.exit(1);
            }
            dispose();
        }

        public void takeScreenshot(Component component, File file) {
            BufferedImage image = new BufferedImage(
                    component.getWidth(),
                    component.getHeight(),
                    BufferedImage.TYPE_INT_RGB
            );
            // call the Component's paint method, using
            // the Graphics object of the image.
            component.paint(image.getGraphics()); // alternately use .printAll(..)

            try {
                // save captured image to PNG file
                ImageIO.write(image, "png", file);
            } catch (Exception e) {
            }

        }

        public void fontPlus() {
            setTextSize(1);

        }

        public void fontMinus() {
            setTextSize(-1);
        }

        private void setTextSize(int modifier) {
            String style = USED_STYLE_SHEET;
            if ((style == null) || (style.length() == 0)) {
                return;
            }

            // default.edge
            //TraceManager.addDev("Old style: " + style);
            style = modifyTextSize(modifier, style, "node ");
            style = modifyTextSize(modifier, style, "node.deadlock");
            style = modifyTextSize(modifier, style, "node.init");
            style = modifyTextSize(modifier, style, "edge ");
            style = modifyTextSize(modifier, style, "edge.defaultedge");
            //TraceManager.addDev("New style default: " + style);
            // external
            style = modifyTextSize(modifier, style, "edge.external");
            //TraceManager.addDev("New style external: " + style);
            vGraph.setAttribute("ui.stylesheet", style);
            USED_STYLE_SHEET = style;


        }

        public String modifyTextSize(int modifier, String input, String key) {
            String ret = input;
            int indexD = input.indexOf(key);
            if (indexD != -1) {
                String before = input.substring(0, indexD);

                String tmp = input.substring(indexD);
                int indexBB = tmp.indexOf("{");
                int indexBE = tmp.indexOf("}");
                int indexT = tmp.indexOf("text-size:");
                if ((indexT > -1) && (indexBB > -1) && (indexBE > -1) && (indexT > indexBB) && (indexT < indexBE)) {
                    String bef = tmp.substring(0, indexT + 10);
                    String textSize = tmp.substring(indexT + 10);
                    int indexVirg = textSize.indexOf(";");
                    if (indexVirg > -1) {
                        String textValue = textSize.substring(0, indexVirg);
                        String endf = textSize.substring(indexVirg);
                        int val = Integer.decode(textValue);
                        val += modifier;
                        if (val < 1) {
                            val = 1;
                        }
                        //TraceManager.addDev("Value=" + val);
                        // Rebuild the string
                        ret = before + bef + val + endf;
                    }
                }

            }
            return ret;
        }

        public void screenshot() {
            TraceManager.addDev("Screenshot");
            JFileChooser jfcggraph;
            if (SpecConfigTTool.GGraphPath.length() > 0) {
                jfcggraph = new JFileChooser(SpecConfigTTool.GGraphPath);
            } else {
                jfcggraph = new JFileChooser();
            }
            PNGFilter filter = new PNGFilter();
            jfcggraph.setFileFilter(filter);
            int returnVal = jfcggraph.showDialog(this, "Graph capture (in png)");
            if (returnVal != JFileChooser.APPROVE_OPTION) {
                return;
            }
            File pngFile = jfcggraph.getSelectedFile();
            TraceManager.addDev("Making the screenshot in " + pngFile.getAbsolutePath());

            takeScreenshot(dv, pngFile);

            TraceManager.addDev("Screenshot performed");
        }


        public synchronized void manageInternalActions() {
            if (edges == null) {
                return;
            }
            int cpt = 0;
            for (AUTTransition transition : graph.getTransitions()) {
                if (transition.transition.startsWith("i(")) {
                    if (internalActions.isSelected()) {
                        edges.get(cpt).setAttribute("ui.label", transition.transition);
                    } else {
                        edges.get(cpt).setAttribute("ui.label", "");
                    }
                }
                cpt++;
            }
            //dv.repaint();
        }

        public synchronized void manageReadActions() {
            if (edges == null) {
                return;
            }
            int cpt = 0;
            for (AUTTransition transition : graph.getTransitions()) {
                if (transition.transition.contains("?")) {
                    if (readActions.isSelected()) {
                        edges.get(cpt).setAttribute("ui.label", transition.transition);
                    } else {
                        edges.get(cpt).setAttribute("ui.label", "");
                    }
                }
                cpt++;
            }
            //dv.repaint();
        }

        public synchronized void manageHigherQuality() {
            //viewer.disableAutoLayout();
            if (higherQuality.isSelected()) {
                vGraph.setAttribute("ui.quality");
            } else {
                vGraph.removeAttribute("ui.quality");
            }
            try {
                //viewer.enableAutoLayout();
                //dv.repaint();
            } catch (Exception e) {
            }
        }


        public synchronized void manageAntialiasing() {
            //viewer.disableAutoLayout();
            if (antialiasing.isSelected()) {
                vGraph.setAttribute("ui.antialias");
            } else {
                vGraph.removeAttribute("ui.antialias");
            }
            try {
                //viewer.enableAutoLayout();
                //dv.repaint();
            } catch (Exception e) {
            }
        }

        public synchronized void updateMe() {
            //TraceManager.addDev("updateMe");
            dv.repaint();
        }

        public Node findNodeAt(int x, int y) {
            for (int i = 0; i < vGraph.getNodeCount(); i++) {
                //TraceManager.addDev("index:" + i);
                Node node = vGraph.getNode(i);
                if (node != null) {
                    double pos[] = Toolkit.nodePosition(node);
                    if (pos != null) {
                        //TraceManager.addDev("pos of Node " + node.getId() + " is " + pos[0] + "," + pos[1]);
                        Point3 position = dv.getCamera().transformGuToPx(pos[0], pos[1], 0);
                        //TraceManager.addDev("Coordinate = " + position + " vs mouse : " + x + "," + y);
                        double diffX = 1;
                        double diffY = 1;
                        if (transform != null) {
                            diffX = transform.getScaleX();
                            diffY = transform.getScaleY();
                        }
                        if ((Math.abs(position.x - diffX * x) < 10) && (Math.abs(position.y - diffY * y) < 10)) {
                            return node;
                        }
                    } else {
                        TraceManager.addDev("Null pos");
                    }
                }
            }

            return null;
        }

    /*public void setAutoLayout(boolean auto) {
        autoLayout.setSelected(auto);
    }*/


    } // Basic Frame


} // Main class