/* 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 tmltranslator;

import compiler.tmlparser.ParseException;
import compiler.tmlparser.SimpleNode;
import compiler.tmlparser.TMLExprParser;
import compiler.tmlparser.TokenMgrError;
import myutil.Conversion;
import myutil.TraceManager;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;


/**
 * Class TMLSyntaxChecking
 * Used verifying the syntax of a TML specification
 * Creation: 12/09/2007
 *
 * @author Ludovic APVRILLE
 * @version 1.0 12/09/2007
 */
public class TMLSyntaxChecking {

    private final String WRONG_ORIGIN_CHANNEL = "is not declared as an origin channel of the task";
    private final String WRONG_DESTINATION_CHANNEL = "is not declared as a destination channel of the task";
    private final String WRONG_ORIGIN_EVENT = "is not declared as an origin event of the task";
    private final String WRONG_DESTINATION_EVENT = "is not declared as a destination event of the task";
    private final String WRONG_ORIGIN_REQUEST = "is not declared as an origin request of the task";
    private final String SYNTAX_ERROR = "syntax error";
    private final String WRONG_VARIABLE_IDENTIFIER = "forbidden variable's name";
    private final String VARIABLE_ERROR = "variable is not used according to its type";
    private final String UNDECLARED_VARIABLE = "unknown variable";
    private final String SYNTAX_ERROR_VARIABLE_EXPECTED = "syntax error (variable expected)";
    private final String TIME_UNIT_ERROR = "unknown time unit";
    private final String NO_NEXT_OPERATOR_ERROR = "No next operator";
    private final String SAME_PORT_NAME = "Two ports have the same name";

    private final String TOO_MANY_MEMORIES = "Channel is mapped on more than one memory";
    private final String INVALID_CHANNEL_PATH = "Channel path is invalid";
    private final String INVALID_BUS_PATH = "Bus path is invalid for channel"; // Should be a warning only

    private final String DUPLICATE_PATH_TO_BUS = "Path to bus is duplicated"; // Should be a warning only
    private final String ONLY_ONE_NOC = "Only one NoC can be used"; // Should be a warning only


    private ArrayList<TMLError> errors;
    private ArrayList<TMLError> warnings;
    private TMLModeling<?> tmlm;
    private TMLMapping<?> mapping;

    private boolean syntaxCheckForMappingOnly = false;


    public TMLSyntaxChecking(TMLModeling<?> _tmlm) {
        tmlm = _tmlm;
    }

    public TMLSyntaxChecking(TMLMapping<?> _mapping) {
        mapping = _mapping;
        tmlm = mapping.getTMLModeling();
    }

    public TMLSyntaxChecking(TMLMapping<?> _mapping, boolean _syntaxCheckForMappingOnly) {
        mapping = _mapping;
        syntaxCheckForMappingOnly = _syntaxCheckForMappingOnly;
        tmlm = mapping.getTMLModeling();
    }

    public void checkSyntax() {

        errors = new ArrayList<TMLError>();
        warnings = new ArrayList<TMLError>();

        TraceManager.addDev("Checking syntax of TML Mapping/ Modeling");
        if (!syntaxCheckForMappingOnly) {

            checkReadAndWriteInChannelsEventsAndRequests();

            checkActionSyntax();

            checkNextActions();

            checkPortName();

            //added by minh hiep
            checkAValidPortName();

        }

        // Mapping or architecture
        if (mapping != null) {
            checkMemoriesOfChannels();
            checkPathToMemory();
            checkPathValidity();
            checkNonDuplicatePathToBuses();
            checkOneNOC();

            // Check that if their is a memory for a channel, the memory is connected to the path
        }

    }


    public int hasErrors() {
        if (errors == null) {
            return 0;
        }
        return errors.size();
    }

    public int hasWarnings() {
        if (warnings == null) {
            return 0;
        }
        return warnings.size();
    }

    public ArrayList<TMLError> getErrors() {
        return errors;
    }

    public ArrayList<TMLError> getWarnings() {
        return warnings;
    }

    public void addErrorByReference(Object referenceObject, TMLTask t, TMLActivityElement elt, String message, int type) {
        TMLError error = new TMLError(type);
        error.message = message;
        error.task = t;
        error.element = elt;
        error.referenceObject = referenceObject;
        errors.add(error);
    }


    public void addError(TMLTask t, TMLActivityElement elt, String message, int type) {
        TMLError error = new TMLError(type);
        error.message = message;
        error.task = t;
        error.element = elt;
        if (t != null) {
            error.referenceObject = t.getReferenceObject();
        } else if (elt!= null) {
            error.referenceObject = elt.getReferenceObject();
        }
        errors.add(error);
    }


    public void checkNextActions() {
        for (TMLTask t : tmlm.getTasks()) {
            TMLActivity tactivity = t.getActivityDiagram();
            int n = tactivity.nElements();
            for (int i = 0; i < n; i++) {
                TMLActivityElement elt = tactivity.get(i);
                if (!(elt instanceof TMLStopState)) {
                    if (elt.getNbNext() == 0) {
                        addError(t, elt, elt.getName() + ": " + NO_NEXT_OPERATOR_ERROR, TMLError.ERROR_BEHAVIOR);
                    }
                }
            }
        }
    }

    //added by minh hiep
    public void checkAValidPortName() {
        List<TMLChannel> tmlChannels = tmlm.getChannels();
        List<TMLEvent> tmlEvents = tmlm.getEvents();
        List<TMLRequest> tmlRequests = tmlm.getRequests();

        for (TMLChannel channel : tmlChannels) {
            if (!TMLTextSpecification.isAValidId(channel.getName()))
                addError(null, null, WRONG_VARIABLE_IDENTIFIER + ": invalid port name", TMLError.ERROR_STRUCTURE);
        }

        for (TMLEvent event : tmlEvents) {
            if (!TMLTextSpecification.isAValidId(event.getName()))
                addError(null, null, WRONG_VARIABLE_IDENTIFIER + ": invalid port name", TMLError.ERROR_STRUCTURE);
        }

        for (TMLRequest request: tmlRequests) {
            if (!TMLTextSpecification.isAValidId(request.getName()))
                addError(null, null, WRONG_VARIABLE_IDENTIFIER + ": invalid port name", TMLError.ERROR_STRUCTURE);
        }
    }

    public void checkPortName() {
        // checks if two ports with the same type (origin, destination) have the same name
        HashMap<String, TMLPort> origin = new HashMap<String, TMLPort>();
        HashMap<String, TMLPort> destination = new HashMap<String, TMLPort>();

        // Channels
        for (TMLChannel tmlc : tmlm.getChannels()) {
            TMLPort p = tmlc.getOriginPort();
            tryToAddPort(p, origin, "origin");
            p = tmlc.getDestinationPort();
            tryToAddPort(p, destination, "destination");

            for (TMLPort po : tmlc.getOriginPorts()) {
                tryToAddPort(po, origin, "origin");
            }

            for (TMLPort po : tmlc.getDestinationPorts()) {
                tryToAddPort(po, destination, "destination");
            }
        }

        // Events
        for (TMLEvent tmle : tmlm.getEvents()) {
            TMLPort p = tmle.getOriginPort();
            tryToAddPort(p, origin, "origin");
            p = tmle.getDestinationPort();
            tryToAddPort(p, destination, "destination");

            for (TMLPort po : tmle.getOriginPorts()) {
                tryToAddPort(po, origin, "origin");
            }

            for (TMLPort po : tmle.getDestinationPorts()) {
                tryToAddPort(po, destination, "destination");
            }
        }

        // Request

        /*for(TMLRequest tmlr: tmlm.getRequests()) {
            TMLPort p = tmlr.getOriginPort();
            tryToAddPort(p, origin, "origin");
            p = tmlr.getDestinationPort();
            tryToAddPort(p, destination, "destination");

            for(TMLPort po: tmlr.getOriginPorts()) {
                tryToAddPort(po, origin, "origin");
            }

            for(TMLPort po: tmlr.getDestinationPorts()) {
                tryToAddPort(po, destination, "destination");
            }
        }*/

    }

    private void tryToAddPort(TMLPort _p, HashMap<String, TMLPort> map, String origin) {
        if (_p == null) {
            return;
        }

        TMLPort inP = map.get(_p.getName());

        if (inP != null) {
            addError(null, null, SAME_PORT_NAME + ": " + _p.getName() + "(" + origin + " ports)", TMLError.ERROR_STRUCTURE);
        } else {
            //TraceManager.addDev("Adding port with name=" + _p.getName() + " for kind=" + origin);
            map.put(_p.getName(), _p);
        }
    }

    public void checkReadAndWriteInChannelsEventsAndRequests() {
        TMLChannel ch;
        TMLEvent evt;
        TMLRequest request;

        for (TMLTask t : tmlm.getTasks()) {
            TMLActivity tactivity = t.getActivityDiagram();
            TMLActivityElement elt;
            int n = tactivity.nElements();
            for (int i = 0; i < n; i++) {
                elt = tactivity.get(i);
                //TraceManager.addDev("Task= " + t.getName() + " element=" + elt);

                if (elt instanceof TMLWriteChannel) {
                    for (int j = 0; j < ((TMLWriteChannel) elt).getNbOfChannels(); j++) {
                        ch = ((TMLWriteChannel) elt).getChannel(j);
                        if (ch.isBasicChannel()) {
                            //TraceManager.addDev("Write in channel" + ch.getName());
                            if (ch.getOriginTask() != t) {
                                //TraceManager.addDev("Origin task=" + ch.getOriginTask().getName() + " / task = " + t.getName() + "tch=" + ch.getOriginTask() + " t=" + t);
                                //TraceManager.addDev("tml:" + tmlm.toString());
                                //  TMLTextSpecification  tmlt = new TMLTextSpecification("toto");
                                //TraceManager.addDev("tml:" + tmlt.toTextFormat(tmlm));
                                addError(t, elt, ch.getName() + ": " + WRONG_ORIGIN_CHANNEL, TMLError.ERROR_BEHAVIOR);
                            }
                        }
                    }
                }

                if (elt instanceof TMLReadChannel) {
                    ch = ((TMLReadChannel) elt).getChannel(0);
                    if (ch.isBasicChannel()) {
                        //TraceManager.addDev("Read channel");
                        if (ch.getDestinationTask() != t) {
                            addError(t, elt, ch.getName() + ": " + WRONG_DESTINATION_CHANNEL, TMLError.ERROR_BEHAVIOR);
                        }
                    }
                }

                if (elt instanceof TMLSendEvent) {

                    evt = ((TMLSendEvent) elt).getEvent();
                    if (evt.isBasicEvent()) {
                        //TraceManager.addDev("send evt= " + evt.getName() + " task=" + t.getName() + " origin=" + evt.getOriginTask().getName());
                        if (evt.getOriginTask() != t) {
                            addError(t, elt, evt.getName() + ": " + WRONG_ORIGIN_EVENT, TMLError.ERROR_BEHAVIOR);
                        }
                    }
                }

                if (elt instanceof TMLWaitEvent) {
                    evt = ((TMLWaitEvent) elt).getEvent();
                    if (evt.isBasicEvent()) {
                        /*try {
                            TraceManager.addDev("wait evt= " + evt.getName());
                        } catch (Exception e) {
                            TraceManager.addDev("Error on evt = " + evt);
                        }
                        if (evt.getDestinationTask() == null) {
                            TraceManager.addDev("Null destination task");
                        }*/

                        //TraceManager.addDev("wait evt= " + evt.getName() + " task=" + t.getName() + " destination=" + evt.getDestinationTask().getName());
                        if (evt.getDestinationTask() != t) {
                            addError(t, elt, evt.getName() + ": " + WRONG_DESTINATION_EVENT, TMLError.ERROR_BEHAVIOR);
                        }
                    }
                }

                if (elt instanceof TMLNotifiedEvent) {
                    evt = ((TMLNotifiedEvent) elt).getEvent();
                    //TraceManager.addDev("Write channel");
                    if (evt.getDestinationTask() != t) {
                        addError(t, elt, evt.getName() + ": " + WRONG_DESTINATION_EVENT, TMLError.ERROR_BEHAVIOR);
                    }
                }

                if (elt instanceof TMLSendRequest) {
                    request = ((TMLSendRequest) elt).getRequest();
                    //TraceManager.addDev("Write channel");
                    if (!request.isAnOriginTask(t)) {
                        addError(t, elt, request.getName() + ": " + WRONG_ORIGIN_REQUEST, TMLError.ERROR_BEHAVIOR);
                    }
                }
            }
        }
    }

    public void checkActionSyntax() {
        TMLWaitEvent tmlwe;
        TMLSendEvent tmlase;
        TMLSendRequest tmlsr;
        TMLChoice choice;
        TMLForLoop loop;
        TMLEvent evt;
        TMLRequest req;
        TMLType type;
        TMLRandom random;
        int j;
        int elseg, afterg;
        TMLAttribute attr;

        //  StringReader toParse;
        String action;


        for (TMLTask t : tmlm.getTasks()) {
            TMLActivity tactivity = t.getActivityDiagram();
            TMLActivityElement elt;

            // Checking names of atrributes
            for (TMLAttribute attri : t.getAttributes()) {
                if (!TMLTextSpecification.isAValidId(attri.getName())) {
                    addError(t, null, WRONG_VARIABLE_IDENTIFIER + ": invalid identifier", TMLError.ERROR_STRUCTURE);
                }
            }

            int n = tactivity.nElements();
            //TraceManager.addDev("Task" + t.getName());
            for (int i = 0; i < n; i++) {
                elt = tactivity.get(i);
                //TraceManager.addDev("elt=" + elt);
                if (elt instanceof TMLActionState) {
                    action = ((TMLActivityElementWithAction) elt).getAction();
                    parsingAssignment(t, elt, action);

                } else if (elt instanceof TMLActivityElementWithAction) {
                    action = ((TMLActivityElementWithAction) elt).getAction();
                    parsing(t, elt, "actionnat", action);

                } else if (elt instanceof TMLActivityElementWithIntervalAction) {
                    //TraceManager.addDev("Parsing TMLActivityElementWithIntervalAction");
                    action = ((TMLActivityElementWithIntervalAction) elt).getMinDelay();
                    parsing(t, elt, "actionnat", action);
                    action = ((TMLActivityElementWithIntervalAction) elt).getMaxDelay();
                    parsing(t, elt, "actionnat", action);

                    if (elt instanceof TMLDelay) {
                        action = ((TMLDelay) elt).getUnit().trim();

                        if (!(TMLDelay.isAValidUnit(action))) {
                            addError(t, elt, TIME_UNIT_ERROR + "in expression " + action, TMLError.ERROR_BEHAVIOR);
                        }
                    }

                } else if (elt instanceof TMLActivityElementChannel) {
                    action = ((TMLActivityElementChannel) elt).getNbOfSamples();
                    parsing(t, elt, "actionnat", action);

                } else if (elt instanceof TMLSendEvent) {
                    tmlase = (TMLSendEvent) elt;
                    evt = tmlase.getEvent();
                    for (j = 0; j < tmlase.getNbOfParams(); j++) {
                        action = tmlase.getParam(j);
                        if ((action != null) && (action.length() > 0)) {
                            type = evt.getType(j);
                            if ((type == null) || (type.getType() == TMLType.NATURAL)) {
                                parsing(t, elt, "actionnat", action);
                            } else {
                                parsing(t, elt, "actionbool", action);
                            }
                        }
                    }

                } else if (elt instanceof TMLWaitEvent) {
                    tmlwe = (TMLWaitEvent) elt;
                    evt = tmlwe.getEvent();
                    //TraceManager.addDev("Nb of params of wait event:" + tmlwe.getNbOfParams());
                    for (j = 0; j < tmlwe.getNbOfParams(); j++) {
                        action = tmlwe.getParam(j).trim();
                        if ((action != null) && (action.length() > 0)) {
                            if (!(Conversion.isId(action))) {
                                addError(t, elt, SYNTAX_ERROR_VARIABLE_EXPECTED + " in expression " + action, TMLError.ERROR_BEHAVIOR);
                            } else {
                                // Declared variable?
                                attr = t.getAttributeByName(action);
                                if (attr == null) {
                                    addError(t, elt, UNDECLARED_VARIABLE + " :" + action + " in expression " + action, TMLError.ERROR_BEHAVIOR);
                                } else {
                                    //TraceManager.addDev("Nb of params:" + tmlwe.getEvent().getNbOfParams() + " j:" + j);
                                    if (tmlwe.getEvent().getType(j).getType() == 0) {
                                        TraceManager.addDev("0");
                                    }
                                    if (attr.getType().getType() != tmlwe.getEvent().getType(j).getType()) {
                                        TraceManager.addDev("Type0:" + attr.getType().getType() + " type1:" + tmlwe.getEvent().getType(j).getType());
                                        addError(t, elt, VARIABLE_ERROR + " :" + action + " in expression " + action, TMLError.ERROR_BEHAVIOR);
                                    }
                                }
                            }
                        }
                    }

                } else if (elt instanceof TMLSendRequest) {
                    tmlsr = (TMLSendRequest) elt;
                    req = tmlsr.getRequest();
                    for (j = 0; j < tmlsr.getNbOfParams(); j++) {
                        action = tmlsr.getParam(j);
                        if ((action != null) && (action.length() > 0)) {
                            type = req.getType(j);
                            if ((type == null) || (type.getType() == TMLType.NATURAL)) {
                                parsing(t, elt, "actionnat", action);
                            } else {
                                parsing(t, elt, "actionbool", action);
                            }
                        }
                    }

                } else if (elt instanceof TMLChoice) {
                    choice = (TMLChoice) elt;
                    elseg = choice.getElseGuard();
                    afterg = choice.getAfterGuard();
                    for (j = 0; j < choice.getNbGuard(); j++) {
                        /*if (action.length() == 1) {
                            if ((action.compareTo("[") == 0) || (action.compareTo("]") == 0)) {
                                addError(t, elt, SYNTAX_ERROR  + " in expression " + action, TMLError.ERROR_BEHAVIOR);
                            }

                        }*/
                        //TraceManager.addDev("Testing guard: " + choice.getGuard(j));
                        if (!choice.isNonDeterministicGuard(j) && !choice.isStochasticGuard(j)) {
                            if ((j != elseg) && (j != afterg)) {
                                action = choice.getGuard(j);
                                action = action.trim();

                                parsing(t, elt, "guard", action);
                            }
                        }
                    }

                } else if (elt instanceof TMLForLoop) {
                    loop = (TMLForLoop) elt;
                    if (loop.getInit().trim().length() > 0) {
                        parsing(t, elt, "assnat", loop.getInit());
                    }
                    if (loop.getCondition().trim().length() > 0) {
                        parsing(t, elt, "actionbool", loop.getCondition());
                    }
                    if (loop.getIncrement().trim().length() > 0) {
                        parsing(t, elt, "assnat", loop.getIncrement());
                    }

                } else if (elt instanceof TMLRandom) {
                    random = (TMLRandom) elt;
                    parsing(t, elt, "actionnat", random.getMinValue());
                    parsing(t, elt, "actionnat", random.getMaxValue());
                    parsing(t, elt, "natid", random.getVariable());
                    parsing(t, elt, "natnumeral", "" + random.getFunctionId());
                }
            }
        }
    }

    public void parsingAssignment(TMLTask t, TMLActivityElement elt, String action) {
        int index = action.indexOf("=");

        if (index == -1) {
            addError(t, elt, SYNTAX_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR);
            return;
        }

        String var = action.substring(0, index).trim();
        TMLAttribute attrFound = null;
        for (TMLAttribute attr : t.getAttributes()) {
            if (attr.getName().compareTo(var) == 0) {
                attrFound = attr;
                break;
            }
        }

        if (attrFound == null) {
            addError(t, elt, UNDECLARED_VARIABLE + " :" + var + " in expression " + action, TMLError.ERROR_BEHAVIOR);
            return;
        }

        if (attrFound.isNat()) {
            parsing(t, elt, "assnat", action);
        } else {
            parsing(t, elt, "assbool", action);
        }

    }


    /**
     * Parsing in two steps:
     * 1. Parsing the expression with no variable checking
     * 2. Parsing the expression with variables values to see whether variables are well-placed or not
     * The second parsing is performed iff the first one succeeds
     * @param t         : TML task {@link TMLTask}
     * @param elt       : TML activity element {@link TMLActivityElement}
     * @param parseCmd  : String - parse command
     * @param action    : String
     */
    public void parsing(TMLTask t, TMLActivityElement elt, String parseCmd, String action) {
        if (action == null) {
            return;
        }
        TMLExprParser parser;
        SimpleNode root;

        // First parsing
        if (!(parseCmd.startsWith("guard"))) {
            parser = new TMLExprParser(new StringReader(parseCmd + " " + action));
            try {
                //TraceManager.addDev("\nParsing :" + parseCmd + " " + action);
                root = parser.CompilationUnit();
                //root.dump("pref=");
                //TraceManager.addDev("Parse ok");
            } catch (ParseException e) {
                TraceManager.addDev("ParseException --------> Parse error in: " + parseCmd + " " + action);
                addError(t, elt, SYNTAX_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR);
                return;
            } catch (TokenMgrError tke) {
                TraceManager.addDev("TokenMgrError --------> Parse error in: " + parseCmd + " " + action);
                addError(t, elt, SYNTAX_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR);
                return;
            }
        }

        // Second parsing
        // We only replace variables values after the "=" sign
        if (parseCmd.compareTo("natnumeral") == 0) {
            return;
        }
        int index = action.indexOf('=');
        String modif = action;

        if ((parseCmd.compareTo("assnat") == 0) || (parseCmd.compareTo("assbool") == 0)) {
            if (index != -1) {
                modif = action.substring(index + 1, action.length());
            }

            if (parseCmd.compareTo("assnat") == 0) {
                parseCmd = "actionnat";
            } else {
                parseCmd = "actionbool";
            }
        }

        if (parseCmd.compareTo("natid") == 0) {
            parseCmd = "natnumeral";
        }

        for (TMLAttribute attr : t.getAttributes()) {
            modif = tmlm.putAttributeValueInString(modif, attr);
        }
        parser = new TMLExprParser(new StringReader(parseCmd + " " + modif));
        try {
            //TraceManager.addDev("\nParsing :" + parseCmd + " " + modif);
            root = parser.CompilationUnit();
            //root.dump("pref=");
            //TraceManager.addDev("Parse ok");
        } catch (ParseException e) {
            TraceManager.addDev("ParseException --------> Parse error in :" + parseCmd + " " + action);
            addError(t, elt, VARIABLE_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR);
            return;
        } catch (TokenMgrError tke) {
            TraceManager.addDev("TokenMgrError --------> Parse error in :" + parseCmd + " " + action + " modified action:" + modif);
            addError(t, elt, VARIABLE_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR);
            return;
        }

        // Tree analysis: if the tree contains a variable, then, this variable has not been declared
        List<String> vars = root.getVariables();
        for (String s : vars) {
            addError(t, elt, UNDECLARED_VARIABLE + " :" + s + " in expression " + action, TMLError.ERROR_BEHAVIOR);
        }

    }

    public String printSummary() {
        String ret = "";
        if (errors.size() == 0) {
            ret += printWarnings();
            ret += "Syntax checking: successful\n";
            ret += "No error, " + warnings.size() + " warning(s)\n";
        } else {
            ret += printErrors() + printWarnings();
            ret += "Syntax checking: failed\n";
            ret += errors.size() + " error(s), " + warnings.size() + " warning(s)\n";
        }

        return ret;
    }

    public String printErrors() {
        String ret = "*** ERRORS:";
        for (TMLError error : errors) {
            ret += "ERROR / task " + error.task.getName() + " / element " + error.element.getName() + ": " + error.message + "\n";
        }
        return ret;
    }

    public String printWarnings() {
        String ret = "";
        for (TMLError error : warnings) {
            ret += "ERROR / task " + error.task.getName() + " / element: " + error.element.getName() + ": " + error.message + "\n";
        }
        return ret;
    }


    // Mapping
    public void checkMemoriesOfChannels() {

        Iterator<TMLChannel> channelIt = tmlm.getChannels().iterator();
        while (channelIt.hasNext()) {
            TMLChannel ch = channelIt.next();
            int n = mapping.getNbOfMemoriesOfChannel(ch);
            if (n > 1) {
                // Too many memories
                addError(null, null, TOO_MANY_MEMORIES + ": " + ch.getName(), TMLError.ERROR_STRUCTURE);
            }
        }
    }

    // For each hw element to which a path is mapped
    // We must check that both the reader and the writer
    // can access to that element without going through a CPU
    public void checkPathToMemory() {
        Iterator<TMLChannel> channelIt = tmlm.getChannels().iterator();
        while (channelIt.hasNext()) {
            TMLChannel ch = channelIt.next();
            checkPathToMemoryFromCPU(ch);
            checkPathToMemoryFromAllHwCommNode(ch);
        }
    }

    private void checkPathToMemoryFromCPU(TMLChannel ch) {
        //We must consider all channel sources and destination
        // We first select all involved tasks
        ArrayList<TMLTask> tasks = ch.getAllTasks();

        // Then we find the corresponding CPUs
        for (TMLTask task : tasks) {
            //We collect all the CPUs
            //TraceManager.addDev("Collecting all CPUs of task: " + task.getTaskName());
            for (HwExecutionNode origin : mapping.getAllHwExecutionNodesOfTask(task)) {
                // And then we check the paths between node and all the nodes of ch
                for (HwCommunicationNode destination : mapping.getAllCommunicationNodesOfChannel(ch)) {
                    //TraceManager.addDev("Computing path between " + origin + " and " + destination);
                    if (!mapping.checkPath(origin, destination)) {
                        addError(null, null, INVALID_CHANNEL_PATH + ": " + ch.getName(), TMLError.ERROR_STRUCTURE);
                        return;
                    }
                }

            }
        }
    }

    private void checkPathToMemoryFromAllHwCommNode(TMLChannel ch) {
        HwMemory mem = mapping.getMemoryOfChannel(ch);
        if (mem != null) {
            for (HwCommunicationNode origin : mapping.getAllCommunicationNodesOfChannel(ch)) {
                if (origin != mem) {
                    if (!mapping.checkPath(origin, mem)) {
                        addError(null, null, INVALID_CHANNEL_PATH + ": " + ch.getName(), TMLError.ERROR_STRUCTURE);
                        return;
                    }
                }
            }
        }
    }


    private void checkPathValidity() {
        Iterator<TMLChannel> channelIt = tmlm.getChannels().iterator();
        while (channelIt.hasNext()) {
            TMLChannel ch = channelIt.next();
            checkPathValidityForChannel(ch);
        }
    }

    private void checkPathValidityForChannel(TMLChannel ch) {
        HwMemory mem = mapping.getMemoryOfChannel(ch);

        // we want to verify that if there is at least one bus/bridge which is used in the mapping
        // the path is complete. Otherwise, a warning is given for that channel mapping
        ArrayList<HwCommunicationNode> elts = mapping.getAllCommunicationNodesOfChannel(ch);

        if (elts.size() > 1) {
            // We construct the hardware paths of ch
            TMLChannelPath path = mapping.makePathOfChannel(ch);
            if (path == null) {
                addError(null, null, INVALID_BUS_PATH + ": " + ch.getName(), TMLError.ERROR_STRUCTURE);
            }
        }

    }


    private void checkNonDuplicatePathToBuses() {
        TraceManager.addDev("Checking duplicate links to buses");
        HashMap<HwBus, ArrayList<HwNode>> map = new HashMap<HwBus, ArrayList<HwNode>> ();

        ArrayList<HwNode> list;
        for (HwLink link: mapping.getTMLArchitecture().getHwLinks()) {
            list = map.get(link.bus);
            if (list == null) {
                ArrayList<HwNode> newList = new ArrayList<HwNode>();
                newList.add(link.hwnode);
                map.put(link.bus, newList);
            } else if (list.contains(link.hwnode)) {
                addErrorByReference(null, null, null, DUPLICATE_PATH_TO_BUS + ": from " + link.hwnode.getName() + " to " + link.bus.getName(), TMLError.ERROR_STRUCTURE);
            } else {
                list.add(link.hwnode);
            }
        }
    }


    private void checkOneNOC() {
        TraceManager.addDev("Checking NOC Nodes");
        int nb = mapping.getNbOfNoCs();

        if (nb > 1) {
            addError(null, null, ONLY_ONE_NOC, TMLError.ERROR_STRUCTURE);
            return;
        }

    }



}