/* 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 ui.avatarinteractivesimulation;

import avatartranslator.*;
import avatartranslator.directsimulation.AvatarSimulationBlock;
import avatartranslator.directsimulation.AvatarSimulationTransaction;
import avatartranslator.directsimulation.AvatarSpecificationSimulation;
import myutil.GraphicLib;
import ui.ColorManager;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

/**
   * Class AvatarSpecificationSimulationSDPanel
   * Avatar: panel for displaying simulation transactions in the form
   * of a Sequence Diagram
   * Creation: 03/02/2011
   * @version 1.0 03/02/2011
   * @author Ludovic APVRILLE
 */
public class AvatarSpecificationSimulationSDPanel extends JPanel implements MouseMotionListener  {

    private static int MAX_X = 800;
    private static int MAX_Y = 200;
    private static long stamp = 0;

    // Drawing area
    private int minLimit = 10;
    private int maxX = MAX_X;
    private int maxY = MAX_Y;
    private final int limit = 10;

    // Drawing parameters
    private int minSpaceBetweenLifeLines = 5;
    private int spaceBetweenLifeLines = 150;
    private int basicSpaceBetweenLifeLines = 150;
    private boolean spaceBetweenLifeLinesComputed = false;
    private int spaceAtEnd = 50;
    private int spaceAtTop = 50;
    private int verticalSpaceUnderBlocks = 15;
    private int spaceVerticalText = 2;
    private int spaceHorizontalText = 2;
    private int spaceStop = 20;
    private int verticalLink = 10;
    private int lengthAsync = 50;
    private int spaceBroadcast = 25;

    private boolean showHiddenStates = false;
    private boolean showIDs = false;


    private double[] zoomValues = {0.2, 0.3, 0.5, 0.60, 0.80, 1.0, 1.2, 1.5, 2, 2.5, 3, 4};
    private int zoomIndex = 5;
    private int maxZoomIndex = 11;



    private AvatarSpecificationSimulation ass;

    // Transactions
    private int drawnTransactions = 1000;
    private int lastDrawnTransactions = 0; // -1 means the last current one

    // My scroll panel
    private JScrollPane jsp;
    private boolean mustScroll = true;

    // Mouse
    private int xMouse, yMouse;
    private boolean drawInfo = false;
    private long clockValueMouse;
    private Vector<Point> points;
    private Vector<AvatarSimulationTransaction> transactionsOfPoints;

    // Saving states
    private class StateYCd {
        public String stateName;
        public int y;
    }

    private HashMap<AvatarSimulationBlock, ArrayList<StateYCd>> statesMap = new HashMap<AvatarSimulationBlock, ArrayList<StateYCd>>();

    // Graphics
    private Graphics2D lastGraphics;
    private boolean trace = true;



    public AvatarSpecificationSimulationSDPanel(AvatarSpecificationSimulation _ass) {
        ass = _ass;

        ass.computeSelectedSimulationBlocks();
        points = new Vector<Point>();
        transactionsOfPoints = new Vector<AvatarSimulationTransaction>();

        setBackground(Color.WHITE);

        setNewSize();

        addMouseMotionListener(this);
    }

    public void setNbOfDrawnTransactions(int _d) {
        drawnTransactions = _d;
    }

    public void setLastDrawnTransactions(int _d) {
        lastDrawnTransactions = _d;
    }

    public void setTrace(boolean b) {
        trace = b;
    }

    public void setMyScrollPanel(JScrollPane _jsp) {
        jsp = _jsp;
    }

    public Graphics2D getLastGraphics() {
        return lastGraphics;
    }

    public void setShowHiddenStates(boolean _showHiddenStates) {
        showHiddenStates = _showHiddenStates;
        repaint();
    }

    public void setShowIDs(boolean _showIDs) {
        showIDs = _showIDs;
        repaint();
    }

    public void paintComponent(Graphics g) {
        if (!trace) {
            g.clearRect(0, 0, getWidth(), getHeight());
            return;
        }

        statesMap.clear();

        //TraceManager.addDev("Painting components");
        lastGraphics = (Graphics2D)g;
        super.paintComponent(g);
        int currentY = spaceAtTop;
        int currentX = spaceAtEnd;
        int oldMaxY = maxY;
        int oldMaxX = maxX;

        if (!spaceBetweenLifeLinesComputed) {
            computeSpaceBetweenLifeLines(g);
            basicSpaceBetweenLifeLines = spaceBetweenLifeLines;
        }

        try {
            currentY = paintTopElements(g, currentX, currentY);
            paintTransactions(g, currentX, currentY);
            stamp ++;

            if ((oldMaxY != maxY) || (oldMaxX != maxX)) {
                maxX = Math.max(maxX, MAX_X);
                maxY = Math.max(maxY, MAX_Y);
                if ((oldMaxY != maxY) || (oldMaxX != maxX)) {
                    setNewSize();
                    //repaint();
                }
            } else {
                if (mustScroll) {
                    scrollToLowerPosition();
                    mustScroll = false;
                }
            }

            if (drawInfo) {
                drawInfo(g);
            }
        } catch (Exception e) {
        }

        //TraceManager.addDev("Painting components: done");
    }

    protected void scrollToLowerPosition() {
        if (jsp != null) {
            jsp.getVerticalScrollBar().setValue(jsp.getVerticalScrollBar().getMaximum());
        }
    }

    // Returns the currentY position
    protected int paintTopElements(Graphics g, int currentX, int currentY) {
        String name;
        int w;

        Vector<AvatarSimulationBlock> blocks = ass.getSelectedSimulationBlocks();

        for(AvatarSimulationBlock block: blocks) {
            if (block.selected) {
                g.drawLine(currentX + (spaceBetweenLifeLines/4), currentY, currentX + (3*spaceBetweenLifeLines/4), currentY);
                g.drawLine(currentX + (spaceBetweenLifeLines/2), currentY, currentX + (spaceBetweenLifeLines/2), currentY + verticalSpaceUnderBlocks);
                name = block.getBlock().getName();
                w = g.getFontMetrics().stringWidth(name);
                g.drawString(name, currentX + ((spaceBetweenLifeLines-w)/2), currentY - spaceVerticalText);
                currentX += spaceBetweenLifeLines;
            }
        }

        maxX = currentX;

        return currentY + verticalSpaceUnderBlocks;
    }

    private void computeSpaceBetweenLifeLines(Graphics g) {
        int w;

        spaceBetweenLifeLinesComputed = true;

        for(AvatarSimulationBlock block: ass.getSimulationBlocks()) {
            w = g.getFontMetrics().stringWidth(block.getBlock().getName());
            if ((w+minSpaceBetweenLifeLines) > spaceBetweenLifeLines) {
                spaceBetweenLifeLines = w+minSpaceBetweenLifeLines;
            }
        }

    }

    // returns the currentY position
    protected int paintTransactions(Graphics g, int currentX, int currentY) {
        AvatarSimulationTransaction ast;
        long clockValue = -1;
        int index;
        int xOfBlock;
        int newCurrentY = currentY;

        // Draw only the last drawnTransactions
        Vector<AvatarSimulationTransaction> allTransactions = ass.getAllTransactions();
        //Vector<AvatarSimulationBlock> blocks = ass.getSimulationBlocks();
        Vector<AvatarSimulationBlock> blocks = ass.getSelectedSimulationBlocks();

        // Find starting clock value
        if (allTransactions.size() > 0) {
            ast = allTransactions.get(Math.max(0, allTransactions.size()-drawnTransactions));
            clockValue = ast.clockValueWhenFinished-ast.duration;
            clockValueMouse = clockValue;
            g.drawString("@" + clockValue, 10, currentY+g.getFontMetrics().getHeight()/2);

            // Find first name of states
            if (allTransactions.size()-drawnTransactions > 0) {
                AvatarSimulationTransaction astEnd = allTransactions.get(allTransactions.size() - drawnTransactions - 1);
                for(AvatarSimulationBlock block: blocks) {
                    String tmp = "start state";
                    for(AvatarSimulationTransaction astr: allTransactions) {
                        if (astr == astEnd) break;
                        if ((astr.asb == block) && (astr.executedElement instanceof AvatarState)){
                            tmp = astr.executedElement.getName();
                        }
                    }
                    ArrayList<StateYCd> states = null;
                    if (statesMap.containsKey(block)) {
                        states = statesMap.get(block);
                    } else {
                        states = new ArrayList<StateYCd>();
                        statesMap.put(block, states);
                    }
                    StateYCd ycd = new StateYCd();
                    ycd.stateName = tmp;
                    ycd.y = 0;
                    states.add(ycd);
                }
            } else {
                for(AvatarSimulationBlock block: blocks) {
                    ArrayList<StateYCd> states = null;
                    if (statesMap.containsKey(block)) {
                        states = statesMap.get(block);
                    } else {
                        states = new ArrayList<StateYCd>();
                        statesMap.put(block, states);
                    }
                    StateYCd ycd = new StateYCd();
                    ycd.stateName = "start state";
                    ycd.y = 0;
                    states.add(ycd);
                }
            }
        }




        int realLast;
        if (lastDrawnTransactions == 0) {
            realLast = allTransactions.size();
        } else {
            realLast = Math.min(lastDrawnTransactions, allTransactions.size());
        }

        for(int i=Math.max(realLast-drawnTransactions, 0); i<realLast; i++) {
            ast = allTransactions.get(i);
            ast.stamp = stamp;

            index = blocks.indexOf(ast.asb);
            if (index != -1) {
                xOfBlock = currentX + (index * spaceBetweenLifeLines) + spaceBetweenLifeLines/2;

                points.clear();
                transactionsOfPoints.clear();

                if (ast.executedElement instanceof AvatarState) {
                    if ((showHiddenStates) || (!ast.executedElement.isHidden())) {
                        newCurrentY = drawState(g, ast, (AvatarState)(ast.executedElement), xOfBlock, currentY);
                        ArrayList<StateYCd> states = null;
                        if (statesMap.containsKey(ast.asb)) {
                            states = statesMap.get(ast.asb);
                        } else {
                            states = new ArrayList<StateYCd>();
                            statesMap.put(ast.asb, states);
                        }
                        StateYCd ycd = new StateYCd();
                        ycd.stateName = ast.executedElement.getName();
                        ycd.y = currentY + verticalLink;
                        states.add(ycd);
                    }
                } else if (ast.executedElement instanceof AvatarTransition) {
                    newCurrentY = drawTransition(g, (AvatarTransition)(ast.executedElement), ast, xOfBlock, currentY);
                } else if (ast.executedElement instanceof AvatarActionOnSignal) {
                    newCurrentY = drawAvatarActionOnSignal(g, (AvatarActionOnSignal)(ast.executedElement), ast, xOfBlock, currentY, currentX);
                } else if (ast.executedElement instanceof AvatarStopState) {
                    newCurrentY = drawAvatarStopState(g, ast, xOfBlock, currentY, currentX);
                } else if (ast.executedElement instanceof AvatarRandom) {
                    newCurrentY = drawRandom(g, (AvatarRandom)(ast.executedElement), ast, xOfBlock, currentY);
                }

                if ((yMouse>= currentY) && (yMouse <= newCurrentY)) {
                    for(int cpt = 0; cpt<points.size(); cpt++) {
			if (showIDs) {
			    drawIDInfo(g, points.get(cpt).x, points.get(cpt).y, transactionsOfPoints.get(cpt).id);
			}
                    }
                }


                // Draw the line of other blocks
                if (currentY != newCurrentY) {
                    xOfBlock = currentX + spaceBetweenLifeLines/2;
                    for(AvatarSimulationBlock block: blocks) {
                        if (block != ast.asb) {
                            g.drawLine(xOfBlock, currentY, xOfBlock, newCurrentY);
                        }
                        xOfBlock += spaceBetweenLifeLines;
                    }
                    if (ast.clockValueWhenFinished != clockValue) {
                        clockValue = ast.clockValueWhenFinished;
                        if (yMouse >= newCurrentY) {
                            clockValueMouse = clockValue;
                        }
                        g.drawString("@" + clockValue, 10, newCurrentY+g.getFontMetrics().getHeight()/2);
                    }
                }
            }

            // Update currentY;
            currentY = newCurrentY;

        }
        maxY = currentY;
        return currentY;
    }

    private int drawState(Graphics g, AvatarSimulationTransaction _ast, AvatarState as, int currentX, int currentY) {
        int w;
        int x, y, width, height;

        g.drawLine(currentX, currentY, currentX, currentY+verticalLink);
        currentY += verticalLink;

        w = g.getFontMetrics().stringWidth(as.getName());
        Color c = g.getColor();
        Color avat = ColorManager.AVATAR_STATE;
        g.setColor(avat);

        x = currentX - w/2 - spaceHorizontalText;
        y = currentY;
        width = w + 2*spaceHorizontalText;
        height = g.getFontMetrics().getHeight() + spaceVerticalText * 2;

        g.fillRoundRect(x, y, width, height, 5, 5);
        points.add(new Point(x+width, y));
        transactionsOfPoints.add(_ast);
        g.setColor(c);
        g.drawRoundRect(x, y, width, height, 5, 5);

        g.drawString(as.getName(), x + spaceHorizontalText, y+height-2*spaceVerticalText);

        currentY += height;
        g.drawLine(currentX, currentY, currentX, currentY+verticalLink);
        return currentY + verticalLink;
    }

    private int drawTransition(Graphics g, AvatarTransition at, AvatarSimulationTransaction ast, int currentX, int currentY) {
        int w;
        int x, y, width, height;
        int cpt;
        Color c = g.getColor();

        // Duration ?
        if (ast.duration > 0) {
            g.drawLine(currentX, currentY, currentX, currentY+verticalLink);
            currentY += verticalLink;
            g.drawRect(currentX-5, currentY, 10, 30);
            points.add(new Point(currentX+10, currentY));
            transactionsOfPoints.add(ast);
            g.drawString(""+ ast.duration, currentX+6, currentY+17);
            currentY += 30;
            g.setColor(ColorManager.AVATAR_TIME);
            g.drawLine(currentX, currentY, currentX, currentY+verticalLink);
            g.setColor(c);
            currentY += verticalLink;
        }

        if (ast.actions == null) {
            return currentY;
        }

        if (ast.actions.size() == 0) {
            return currentY;
        }


        w = 0;
        for(String action: ast.actions) {
            w = Math.max(g.getFontMetrics().stringWidth(action), w);
        }

        x = currentX - w/2 - spaceHorizontalText;
        y = currentY;
        width = w + 2*spaceHorizontalText;
        height = (g.getFontMetrics().getHeight() + spaceVerticalText * 2) * ast.actions.size();

        g.setColor(Color.WHITE);
        g.fillRoundRect(x, y, width, height, 5, 5);
        g.setColor(c);
        g.drawRoundRect(x, y, width, height, 5, 5);
        points.add(new Point(x+width, y));
        transactionsOfPoints.add(ast);

        cpt = 1;

        Color avat = ColorManager.AVATAR_ACTION;
        g.setColor(avat);
        int decVert = height / ast.actions.size();
        for(String action: ast.actions) {
            g.drawString(action, x + (width - g.getFontMetrics().stringWidth(action))/2, y+(decVert*cpt) - (spaceVerticalText * 2));
            cpt ++;
        }
        g.setColor(c);

        currentY += height;
        g.drawLine(currentX, currentY, currentX, currentY+verticalLink);

        return currentY + verticalLink;
    }

    private int drawRandom(Graphics g, AvatarRandom arandom, AvatarSimulationTransaction ast, int currentX, int currentY) {
        int w;
        int x, y, width, height;
        Color c = g.getColor();

        if (ast.actions == null) {
            return currentY;
        }

        String action = ast.actions.get(0);

        g.drawLine(currentX, currentY, currentX, currentY+verticalLink);
        currentY += verticalLink;

        w = g.getFontMetrics().stringWidth(action);
        x = currentX - w/2 - spaceHorizontalText;
        y = currentY;
        width = w + 2*spaceHorizontalText;
        height = g.getFontMetrics().getHeight() + spaceVerticalText * 2;
        g.setColor(Color.WHITE);
        g.fillRoundRect(x, y, width, height, 5, 5);
        points.add(new Point(x+width, y));
        transactionsOfPoints.add(ast);
        g.setColor(c);
        g.drawRoundRect(x, y, width, height, 5, 5);

        g.drawString(action, x + spaceHorizontalText, y+height-2*spaceVerticalText);

        currentY += height;
        g.drawLine(currentX, currentY, currentX, currentY+verticalLink);
        return currentY + verticalLink;
    }

    private int drawAvatarActionOnSignal(Graphics g, AvatarActionOnSignal aaos, AvatarSimulationTransaction ast, int currentX, int currentY, int startX) {
        int w;
        Color c = g.getColor();

        avatartranslator.AvatarSignal sig = aaos.getSignal();
        avatartranslator.AvatarRelation rel = ass.getAvatarSpecification().getAvatarRelationWithSignal(sig);
        if (sig.isIn()) {
            if (!(rel.isAsynchronous())) {

                //Synchronous
                if (ast.linkedTransaction != null) {
                    // Computing message name
                    AvatarActionOnSignal otherAaos = (AvatarActionOnSignal)(ast.linkedTransaction.executedElement);
                    String messageName = otherAaos.getSignal().getName();
                    if (messageName.compareTo(sig.getName()) != 0) {
                        messageName += "_" + sig.getName();
                    }
                    messageName += "(";

                    if(ast.actions != null) {
                        messageName += ast.actions.get(0);
                    }
                    messageName += ")";

                    //TraceManager.addDev("Working on message name:" + messageName);
                    // Drawing the arrow
                    // Assume a different block in the two transactions
                    int index = ass.getSelectedSimulationBlocks().indexOf(ast.linkedTransaction.asb);
                    if (index != -1) {
                        int xOf2ndBlock = startX + (index * spaceBetweenLifeLines) + spaceBetweenLifeLines/2;

                        currentY += 10;
                        g.setColor(ColorManager.AVATAR_RECEIVE_SIGNAL);
                        g.drawLine(xOf2ndBlock, currentY-1, currentX, currentY-1);
                        g.setColor(c);
                        GraphicLib.arrowWithLine(g, 1, 0, 10, xOf2ndBlock, currentY, currentX, currentY, true);
                        points.add(new Point(xOf2ndBlock, currentY));
                        transactionsOfPoints.add(ast.linkedTransaction);
                        transactionsOfPoints.add(ast);
                        points.add(new Point(currentX, currentY));


                        // Putting the message name
                        w = g.getFontMetrics().stringWidth(messageName);
                        int xtmp = (xOf2ndBlock + currentX)/2 - w/2;
                        g.drawString(messageName, xtmp, currentY-2);


                        currentY += 10;

                        // Vertical line of receiving block
                        g.drawLine(currentX, currentY-20, currentX, currentY);
                    }
                    return currentY;
                }
            } else {
                // In, asynchronous
                String messageName = sig.getName() + "(";
                if(ast.actions != null) {
                    messageName += ast.actions.get(0);
                }
                messageName += ")";

                currentY += 10;
                g.setColor(ColorManager.AVATAR_RECEIVE_SIGNAL);
                g.drawLine(currentX-lengthAsync, currentY-1, currentX, currentY-1);
                g.setColor(c);
                GraphicLib.arrowWithLine(g, 1, 1, 10, currentX-lengthAsync, currentY, currentX, currentY, false);
                points.add(new Point(currentX, currentY));
                transactionsOfPoints.add(ast);

                // Putting the message name
                w = g.getFontMetrics().stringWidth(messageName);
                g.drawString(messageName, currentX-lengthAsync+w/2, currentY-2);

                // Search for sender
                if (ast.linkedTransaction != null) {
                    if (ast.linkedTransaction.stamp == ast.stamp) {
                        if ((ast.linkedTransaction.x >0) && (ast.linkedTransaction.y >0)) {
                            int x = ast.linkedTransaction.x;
                            int y = ast.linkedTransaction.y;

                            if (x + lengthAsync < currentX-lengthAsync) {
                                // Forward
                                g.setColor(ColorManager.AVATAR_RECEIVE_SIGNAL);
                                GraphicLib.dashedLine(g, x + lengthAsync - 1, y, x + lengthAsync-1, currentY);
                                GraphicLib.dashedLine(g, x + lengthAsync, currentY-1, currentX-lengthAsync, currentY-1);
                                g.setColor(c);
                                GraphicLib.dashedLine(g, x + lengthAsync, y, x + lengthAsync, currentY);
                                GraphicLib.dashedLine(g, x + lengthAsync, currentY, currentX-lengthAsync, currentY);
                            } else {
                                // Backward
                                g.setColor(ColorManager.AVATAR_RECEIVE_SIGNAL);
                                GraphicLib.dashedLine(g, x + lengthAsync-1, y, x + lengthAsync-1, y+7);
                                GraphicLib.dashedLine(g, x + lengthAsync, y+6, currentX-lengthAsync, y+6);
                                GraphicLib.dashedLine(g, currentX-lengthAsync-1, currentY, currentX-lengthAsync-1, y+7);
                                g.setColor(c);
                                GraphicLib.dashedLine(g, x + lengthAsync, y, x + lengthAsync, y+7);
                                GraphicLib.dashedLine(g, x + lengthAsync, y+7, currentX-lengthAsync, y+7);
                                GraphicLib.dashedLine(g, currentX-lengthAsync, currentY, currentX-lengthAsync, y+7);
                            }

                            //g.drawLine(x + lengthAsync, y, currentX-lengthAsync, currentY);
                        }
                    }
                } else {
                    //TraceManager.addDev("No linked transaction");
                }

                currentY += 10;

                // Vertical line of receiving block
                g.drawLine(currentX, currentY-20, currentX, currentY);



                return currentY;
            }
        } else {
            if (rel.isAsynchronous()) {
                // Out, asynchronous
                String messageName = sig.getName() + "(";
                if(ast.actions != null) {
                    messageName += ast.actions.get(0);
                }
                messageName += ")";

                currentY += 10;
                g.setColor(ColorManager.AVATAR_RECEIVE_SIGNAL);
                g.drawLine(currentX+lengthAsync, currentY-1, currentX, currentY-1);
                g.setColor(c);
                GraphicLib.arrowWithLine(g, 1, 2, 10, currentX, currentY, currentX+lengthAsync, currentY, false);
                points.add(new Point(currentX, currentY));
                transactionsOfPoints.add(ast);

                if (ast.isLost) {
                    g.fillOval(currentX+lengthAsync, currentY-5, 10,10);
                }

                // Putting the message name
                w = g.getFontMetrics().stringWidth(messageName);
                g.drawString(messageName, currentX+lengthAsync-w/2, currentY-2);

                ast.x = currentX;
                ast.y = currentY;

                currentY += 10;

                // Vertical line of receiving block
                g.drawLine(currentX, currentY-20, currentX, currentY);
                return currentY;

                // Solo Broadcast Sending?
            } else if (ast.isSolo) {
                // Draw a lost message
                String messageName = sig.getName() + "(";
                if(ast.actions != null) {
                    messageName += ast.actions.get(0);
                }
                messageName += ")";

                currentY += 10;
                g.setColor(ColorManager.AVATAR_SEND_SIGNAL);
                g.drawLine(currentX+spaceBetweenLifeLines-spaceBroadcast, currentY-1, currentX, currentY-1);
                g.setColor(c);
                GraphicLib.arrowWithLine(g, 1, 0, 10, currentX, currentY, currentX+spaceBetweenLifeLines-spaceBroadcast, currentY, true);
                points.add(new Point(currentX, currentY));
                transactionsOfPoints.add(ast);
                g.fillOval(currentX+spaceBetweenLifeLines-spaceBroadcast, currentY-5, 10, 10);

                // Putting the message name
                w = g.getFontMetrics().stringWidth(messageName);
                g.drawString(messageName, currentX+10, currentY-2);

                ast.x = currentX;
                ast.y = currentY;

                currentY += 10;

                // Vertical line of receiving block
                g.drawLine(currentX, currentY-20, currentX, currentY);
                return currentY;

            }
        }

        return currentY;

    }

    private int drawAvatarStartState(Graphics g, int currentX, int currentY, int startX) {
        currentX -= 7;
        g.fillOval(currentX, currentY, 15, 15);
        g.drawLine(currentX, currentY, currentX, currentY+20);

        currentY += 20;
        return currentY;
    }

    private int drawAvatarStopState(Graphics g, AvatarSimulationTransaction _ast, int currentX, int currentY, int startX) {
        g.drawLine(currentX, currentY, currentX, currentY+spaceStop+3);
        currentX -= (spaceStop/2);
        g.drawLine(currentX, currentY, currentX+spaceStop, currentY+spaceStop);
        g.drawLine(currentX, currentY+spaceStop, currentX+spaceStop, currentY);
        points.add(new Point(currentX+spaceStop, currentY));
        transactionsOfPoints.add(_ast);

        currentY += spaceStop + 3;
        return currentY;
    }

    public void setNewSize() {
        setPreferredSize(new Dimension(maxX + limit, maxY + limit));
        mustScroll = true;
        revalidate();
    }

    public void mouseDragged(MouseEvent e) {
    }

    public void mouseMoved(MouseEvent e) {
        xMouse = e.getX();
        yMouse = e.getY();
        if ((xMouse > minLimit) && (xMouse<maxX) && (yMouse> spaceAtTop) && (yMouse<(maxY))) {
            drawInfo = true;
            repaint();
            return;
        }

        if( drawInfo == true) {
            drawInfo = false;
            repaint();
            return;
        }

        drawInfo = false;
    }

    private String getNameOfState(AvatarSimulationBlock block, int y) {
        if (!(statesMap.containsKey(block))) {
            return null;
        }

        String name = null;
        ArrayList<StateYCd> ycd = statesMap.get(block);
        if (ycd == null) {
            return name;
        }

        for(StateYCd st: ycd) {
            if (st.y > y) {
                return name;
            }
            name = st.stateName;
        }

        return name;
    }

    private void drawInfo(Graphics g) {
        String timeValue = "@" + clockValueMouse;
        Color c = g.getColor();
        g.setColor(ColorManager.AVATAR_ACTION);
        GraphicLib.dashedLine(g, spaceAtEnd, yMouse, maxX-spaceAtEnd, yMouse);
        g.drawString(timeValue, 10, yMouse+g.getFontMetrics().getHeight()/2);
        g.drawString(timeValue, maxX-spaceAtEnd + 1, yMouse+g.getFontMetrics().getHeight()/2);


        //TraceManager.addDev("yMouse=" + yMouse);

        /*if (minIdValueMouse == maxIdValueMouse) {
          g.drawString("ID: " + minIdValueMouse, 10, yMouse+(g.getFontMetrics().getHeight()/2)+12);
          } else {
          g.drawString("ID: " + minIdValueMouse + " to " + maxIdValueMouse, 10, yMouse+(g.getFontMetrics().getHeight()/2)+12);
          }*/

        String name;
        int w;
        Vector<AvatarSimulationBlock> blocks = ass.getSelectedSimulationBlocks();
        int x = spaceAtEnd;

        Font normal =  g.getFont();
        Font it = normal.deriveFont(Font.ITALIC);

        for(AvatarSimulationBlock block: blocks) {
            name = block.getBlock().getName();
            w = g.getFontMetrics().stringWidth(name);
            g.drawString(name, x + ((spaceBetweenLifeLines-w)/2), yMouse - spaceVerticalText);


            // Write the name of the current state
            name = getNameOfState(block, yMouse);
            if (name == null) {
                name = " - ";
            }

            g.setFont(it);
            w = g.getFontMetrics().stringWidth(name);
            g.drawString(name, x + ((spaceBetweenLifeLines-w)/2), yMouse + (6 * spaceVerticalText));
            g.setFont(normal);

            // Add the space between lifelines
            x += spaceBetweenLifeLines;
        }
        g.setColor(c);
    }

    private void drawIDInfo(Graphics g, int _x, int _y, long _id) {
        Color c = g.getColor();
        g.setColor(ColorManager.AVATAR_EXPIRE_TIMER);
        g.fillOval(_x-3, _y-3, 6, 6);
        g.drawLine(_x, _y, _x+6, _y-6);
        g.drawLine(_x+6, _y-6, _x+12, _y-6);
        g.drawString(""+_id, _x+13, _y-6);
        g.setColor(c);
    }

    public BufferedImage performCapture() {
        int w = this.getWidth();
        int h = this.getHeight()+50;
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, w+1, h+1);
        g.setColor(Color.black);
        paintComponent(g);
        g.dispose();
        return image;
    }

    public void zoomIn() {
        zoomIndex ++;
        zoomIndex = Math.min(maxZoomIndex, zoomIndex);
        spaceBetweenLifeLines = (int)(basicSpaceBetweenLifeLines*zoomValues[zoomIndex]);
        repaint();
    }

    public void zoomOut() {
        zoomIndex --;
        zoomIndex = Math.max(0, zoomIndex);
        spaceBetweenLifeLines = (int)(basicSpaceBetweenLifeLines*zoomValues[zoomIndex]);
        repaint();
    }

}