/* 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 avatartranslator.tosysmlv2;

import avatartranslator.*;

import static avatartranslator.AvatarTransition.*;
import static avatartranslator.tosysmlv2.Avatar2SysMLNames.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.function.BiConsumer;


/**
 * Class Avatar2SysMLV2
 * Creation: 20/06/2024
 *
 * @author Sophie Coudert
 * @version 0.1 20/06/2024
 */
public class Avatar2SysML {
    /** the source Avatar Specification */
    private AvatarSpecification avspec;
    /** the buffer in which theSysML Model is written (returned at the end of the generation process) */
    private StringBuffer avsysml;
    /** signals concerned by AvatarQueryOnSignal (collected at beginning because require some specific handling)*/
    private HashSet<AvatarSignal> fifoSet = new HashSet<AvatarSignal>();
    /** infos about methods and signals collected while generation declarations and used while generating state machines */
    private HashMap<AvatarMethod, MethodInfo> methodMap = new HashMap<AvatarMethod, MethodInfo>();
    /** path from top level to the block (block sequence) being currently handled */
    private ArrayList<String> blockChain = new ArrayList<String>();
    /** SysML code to make inner blocks visible at top level. Contains one declaration per inner block */
    private StringBuffer blockLinks;
    /** the standard indentation step string */
    private final String indentStep = "    ";
    /** size of the standard indentation step */
    private final int indentStepSize = 4;
    /** the current indentation string */
    private String indentation;

    /** (fixed) header for any Avatar SysML model instance */
    private final String header = "package AvatarInstance {\n" +
            indentStep + "private import AvatarGeneral::*;\n" +
            indentStep + "private import AvatarBlockTypes::*;\n" +
            indentStep + "private import AvatarCommunication::*;\n" +
            indentStep + "private import AvatarTransitionServer::*;\n";

    /** the single constructor */
    public Avatar2SysML(AvatarSpecification _avspec) { avspec = _avspec; }

    /** the generator. Builds the SysML model of the specification provided to the constructor */
    public StringBuffer avatar2SysML() {
        fifoSet.clear();
        methodMap.clear();
        blockChain.clear();
        indentation = "";
        blockLinks = new StringBuffer(indentStep + "// Block Shortcut Links $$$$$$$$$$$$\n");
        avsysml = new StringBuffer(header);
        indent(1);
        searchCountSignals();;
        dataTypes2SysML();
        communications2SysML();
        blocks2SysML();
        avsysml.append(blockLinks);
        avsysml.append("}\n");
        return avsysml;
    }

    /** increase indentation */
    private void indent(int n) { for (int i= 0; i < n; i++) indentation += indentStep; }
    /** decrease indentation */
    private void unindent(int n) { indentation = indentation.substring(n * indentStepSize); }

    /** infos collected while generating declarations of methods and signals, to be used later.
     *
     * Used when generating state machine description, for example when handling ActionsOnSignal. */
    static private class MethodInfo {
        private String name; // method name
        private String msgtype; // null for methods that are not signals
        private ArrayList<String> fieldNames; // names of parameters
        private ArrayList<AvatarType> fieldTypes; // int, bool or undefined
        private ArrayList<AvatarDataType> dataTypes; // datatype for "undefined" fieldType, otherwise null.
        private AvatarType returnType; // int, bool or undefined
        private AvatarDataType returnDataType; // datatype if returnType is "undefined", otherwise null.

        /** for void methods (without return type) */
        MethodInfo(String _name) {
            name = _name;
            msgtype = null;
            fieldNames = new ArrayList<String>();
            fieldTypes = new ArrayList<AvatarType>();
            dataTypes = new ArrayList<AvatarDataType>();
            returnType = null;
            returnDataType = null;
        }

        /** for non-void methods (with return type) */
        MethodInfo(String _name, AvatarType _returnType, AvatarDataType dt) {
            name = _name;
            msgtype = null;
            fieldNames = new ArrayList<String>();
            fieldTypes = new ArrayList<AvatarType>();
            dataTypes = new ArrayList<AvatarDataType>();
            returnType = _returnType;
            if (_returnType != AvatarType.UNDEFINED)
                returnDataType = null;
            else
                returnDataType = dt;
       }

       /** for signals */
       MethodInfo(String _name, String _msgtype) {
            name = _name;
            msgtype = _msgtype;
            fieldNames = new ArrayList<String>();
            fieldTypes = new ArrayList<AvatarType>();
            dataTypes = new ArrayList<AvatarDataType>();
            returnType = null;
            returnDataType = null;
       }

        /** add parameter at the end.
         * @param _dt datatype, only relevant if _type is UNDEFINED
         */
        public void addField(String _fieldName, AvatarType _type, AvatarDataType _dt){
           if (_type == AvatarType.UNDEFINED)
               dataTypes.add(_dt);
           else if (!(_type == AvatarType.BOOLEAN || _type == AvatarType.INTEGER))
               return; // unknown type... TO CHANGE ? ...
           else
               dataTypes.add(null);
           fieldNames.add(_fieldName);
           fieldTypes.add(_type);
        }

        /** number of parameters */
        public int getArity() { return fieldNames.size(); }

        /** SysML name */
        public String getName(){ return name; }

        /** SysML name for message type describing profile */
        public String getMessageType(){ return msgtype; }

        /** method Avatar return type (null if none)*/
        public AvatarType getReturnType(){ return returnType; }

        /** method Avatar return datatype (null if no return type or return primitive type)*/
        public AvatarDataType getReturnDataType(){ return returnDataType; }

        /** ith parameter SysML name */
        public String getFieldName(int i){ return fieldNames.get(i); }

        /** ith parameter Avatar type */
        public AvatarType getFieldType(int i){ return fieldTypes.get(i); }

        /** ith parameter Avatar datatype (null if primitive type)*/
        public AvatarDataType getDataType(int i){ return dataTypes.get(i); }
    }

    /**
     * Memorized while naming states, to be reused for generating states
     */
    static private class StateInfo {
        /** SysML name */
        private String name;

        /** Sysml name of the state that is technically added before this (if exists) */
        private String prename = "";

        /** for states that havn't technically added state before them
         * @param _name SysML name
         */
        StateInfo(String _name) {
            name = _name;
        }

        /** for states that have a technically added state before them
         * @param _name SysML name of the state
         * @param _prename SysML name of the technically added state before
         */
        StateInfo(String _name, String _prename) { name = _name; prename = _prename; }

        /** SysML name of the state */
        public String getName(){ return name; }

        /** SysML name of the technically added state before */
        public String getPreName(){ return prename; }
    }

    /** Collect (in fifoSet) all signals that are used in an AvatarQueryOnSignal element of the specification.
     *
     * (they require some specific handling while generating...)
     */
    private void searchCountSignals() {
        for(AvatarBlock block: avspec.getListOfBlocks()) {
            for(AvatarStateMachineElement sme : block.getStateMachine().getListOfElements()) {
                    if(sme instanceof AvatarQueryOnSignal)
                        fifoSet.add(((AvatarQueryOnSignal) sme).getSignal());
            }
        }
    }

    /** update path blockChain when entering the block-handling procedure for a a block / sub-block
     * @param _block: the block /sub-block to handle
     */
    private void chainBlock(String _block) { blockChain.add(_block); }

    /** update path blockChain when exiting the block-handling procedure for a a block / sub-block */
    private void unchainBlock() { if(blockChain.size() > 0) blockChain.remove(blockChain.size() - 1); }

    /** generate SysML code to make a block visible at top level through its name (and not its path).
     *
     * Links block name to block path (updates blockLinks which is added to avsysml at the end of
     * the generation process). Consistent because all blocks have different names.
     */
    private void blocklink() {
        int size = blockChain.size();
        if (size < 2) return;
        StringBuffer chain = new StringBuffer();
        for (int i=0; i < size-1; i++){
            chain.append(blockChain.get(i) + ".");
        }
        chain.append(blockChain.get(size - 1));
        blockLinks.append( indentStep + "part " + blockChain.get(size - 1) +
                " : '#AvatarBlock' :> " + chain.toString() + " = " + chain.toString() + ";\n" );
    }

    /** generates SysML datatype declarations and put them at the current position (end) of avsysml */
    private void dataTypes2SysML(){
        if (avspec.getDataTypes() == null || avspec.getDataTypes().size() == 0) return;
        avsysml.append("\n" + indentation + "// DATATYPES $$$$$$$$$$$$$$$$$$$$$$$$\n");
        for(AvatarDataType dt : avspec.getDataTypes()){
            // put Header
            avsysml.append("\n" + indentation + "attribute def " + dataTypeSysMLname(dt.getName()) + " :> '#AvatarDataType'");
            // handle fields
            int nbFields = dt.attributeNb();
            if (nbFields == 0)
                avsysml.append(";\n");
            else { // put fields.
                   // Note: SysML has no default values thus default values are explicitely set
                avsysml.append(" { \n");
                indent(1);
                for (int i = 0; i < nbFields; i++) {
                    AvatarAttribute aa = dt.getAttribute(i);
                    avsysml.append( indentation + "attribute " + fieldSysMLname(aa.getName()) + " : " );
                    if (aa.getType() == AvatarType.BOOLEAN) {
                        avsysml.append("Boolean");
                        if (aa.hasInitialValue())
                            avsysml.append(" := " + aa.getInitialValue().trim() + ";\n");
                        else
                            avsysml.append(" default := " + AvatarType.BOOLEAN.getDefaultInitialValue().trim() + ";\n");
                    }
                    else if (aa.getType() == AvatarType.INTEGER) {
                        avsysml.append("Integer");
                         if (aa.hasInitialValue())
                            avsysml.append(" := " + aa.getInitialValue().trim() + ";\n");
                        else
                            avsysml.append(" default := " + AvatarType.INTEGER.getDefaultInitialValue().trim() + ";\n");
                    }
                    else
                        avsysml.append(aa.getDataType().getName() + ";\n"); // datatypes don't have default values
                }
                unindent(1);
                avsysml.append(indentation + "}\n");
            }
        }
    }
    /** generates SysML declarations of communication structures and put them at the current position (end) of avsysml.
     *
     * Communication structures are
     * <ul>
     *     <li> Relations, corresponding to Avatar relations </li>
     *     <li> Channels declarations (a channel is an inSignal/outSignal connection inside a relation) </li>
     *     <li> Binding of block signals to channels, to complete channel's characterization </li>
     *     <li> in/out Messages (structures) declarations, linking them to channels. This providing signals with profiles </li>
     * </ul>
     */
    private void communications2SysML(){
        int nbfiforelNumber = 0;
        int bfiforelNumber = 0;
        int syncrelNumber = 0;

        if (avspec.getRelations() == null || avspec.getRelations().size() == 0) return;

        avsysml.append("\n" + indentation + "// COMMUNICATIONS $$$$$$$$$$$$$$$$$$$$$$$$\n");
        for(AvatarRelation ar: avspec.getRelations()) {

            // declare some parameters depending on relation type ==============
            int relationType; // SYNC, FIFO or BFIFO
            int relationNumber; // to distinguish relations with same characteristics
            String relationConstructor; // #Sync_Rel, #Bfifo_Rel, #NBfifo_Rel (c.f. Avatar SysML library)
            String relationParameters; // for the call of SysML constructors of relation parts
            String channelConstructor; // #Sync, #Bfifo, #NBfifo (c.f. Avatar SysML library)

            // compute parameters depending on relation type ==============
            // if (ar.isAMS()) ... TODO
            String blk1 = ar.getBlock1().getName();
            String blk2 = ar.getBlock2().getName();
            String blk1SysMLname = blockSysMLname(blk1);
            String blk2SysMLname = blockSysMLname(blk2);
            relationParameters = "('@block1' = " + blk1SysMLname + ", '@block2' = " + blk2SysMLname;
            if (! ar.isAsynchronous()) { // sync relation
                relationType = SYNC;
                relationNumber = syncrelNumber++;
                relationConstructor = "'#Sync_Rel'";
                channelConstructor = "'#Sync'";
                if(ar.isPrivate()) relationParameters += ", '@private'=true)"; // default is false in Avatar SysML library
            }
            else { // fifo relation
                if(ar.isPrivate()) // default: false in Avatar SysML library
                    if(ar.isLossy()) // default: false in Avatar SysML library
                        if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library
                            relationParameters += ", '@private'=true, '@lossy'=true, '@max_size'=" + ar.getSizeOfFIFO() + ")";
                        else
                            relationParameters += ", '@private'=true, '@lossy'=true)";
                    else
                        if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library
                            relationParameters += ", '@private'=true, '@max_size'=" + ar.getSizeOfFIFO() + ")";
                        else
                            relationParameters += ", '@private'=true)";
                else
                    if(ar.isLossy()) // default: false in Avatar SysML library
                        if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library
                            relationParameters += ", '@lossy'=true, '@max_size'=" + ar.getSizeOfFIFO() + ")";
                        else
                            relationParameters += ", '@lossy'=true)";
                    else
                        if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library
                            relationParameters += ", '@max_size'=" + ar.getSizeOfFIFO() + ")";

                if (ar.isBlocking()) { // blocking fifo relation
                    relationType = BFIFO;
                    relationNumber = bfiforelNumber++;
                    relationConstructor = "'#Bfifo_Rel'";
                    channelConstructor = "'#Bfifo'";
                }
                else { // non blocking fifo relation
                    relationType = NBFIFO;
                    relationNumber = nbfiforelNumber++;
                    relationConstructor = "'#NBfifo_Rel'";
                    channelConstructor = "'#NBfifo'";
                }
            }

            // general generation (using parameters above) : put declaration to avsysml ==============

            String relationSYSMLname = relationSysMLname(blk1, blk2, relationType, relationNumber);

            // relation declaration --------------------

            avsysml.append("\n" + indentation + "// Relation " + relationSYSMLname + "=============\n");
            avsysml.append(indentation + "part " +  relationSYSMLname + ": " + relationConstructor);
            if(relationParameters != null)
                 avsysml.append(" = " + relationConstructor + relationParameters);
            avsysml.append(";\n");

            // Channels and associated Messages declarations ----------------

            int relationSize = ar.getSignals1().size();
            for(int i=0; i<relationSize; i++) { // iterate on signal connections
                AvatarSignal sig1 = ar.getSignals1().get(i);
                AvatarSignal sig2 = ar.getSignals2().get(i);
                boolean out2in = (sig1.getInOut() == AvatarSignal.OUT);

                String channelSYSMLname = channelSysMLname(channelName(blk1, blk2, sig1.getName(), sig2.getName(), out2in), relationType);
                String sig1SYSMLname = signalSysMLname(sig1.getName());
                String sig2SYSMLname = signalSysMLname(sig2.getName());
                String message1SYSMLname = messageSysMLname(blk1,sig1.getName());
                String message2SYSMLname = messageSysMLname(blk2,sig2.getName());
                MethodInfo sig1Info = new MethodInfo(sig1SYSMLname, message1SYSMLname); // to collect info
                MethodInfo sig2Info = new MethodInfo(sig2SYSMLname, message2SYSMLname); // to collect info

                // Channel declaration .........................

                avsysml.append("\n" + indentation + "// Channel " + channelSYSMLname + "-------------\n");
                avsysml.append(indentation + "part " + channelSYSMLname + " : " + channelConstructor + " = "
                        + channelConstructor + "('@relation' = " + relationSYSMLname + ");\n");

                // Binding block signals to the channel ................

                if (out2in) { // depends on direction. symetric.
                    avsysml.append(indentation + "binding : '#OutSignalBinding' bind " + blk1SysMLname + "." + sig1SYSMLname + " = " + channelSYSMLname +
                            ";\n");
                    avsysml.append(indentation + "binding : '#InSignalBinding' bind " + blk2SysMLname + "." + sig2SYSMLname + " = " + channelSYSMLname +
                            ";\n");
                } else {
                    avsysml.append(indentation + "binding : '#InSignalBinding' bind " + blk1SysMLname + "." + sig1SYSMLname + " = " + channelSYSMLname + ";\n");
                    avsysml.append(indentation + "binding : '#OutSignalBinding' bind " + blk2SysMLname + "." + sig2SYSMLname + " = " + channelSYSMLname + ";\n");
                }

                // Message declarations .........................
                // one Message per direction, parallel build in two buffers msg1Buf and msg2Buf

                // put declaration headers
                StringBuffer msg1Buf = new StringBuffer(indentation + "\n// Message of signal " + blk1SysMLname + "." + sig1SYSMLname + "............\n");
                StringBuffer msg2Buf = new StringBuffer(indentation + "\n// Message of signal " + blk2SysMLname  + "." + sig2SYSMLname + "............\n");
                if (out2in) {
                    msg1Buf.append(indentation + "part def " + message1SYSMLname + " :> '#OutMessage', " + message2SYSMLname + " {\n");
                    msg2Buf.append(indentation + "part def " + message2SYSMLname + " :> '#InMessage' {\n" +
                            indentation + indentStep + "private part '@channel' : '#Channel' = " + channelSYSMLname + ";\n");
                } else {
                    msg1Buf.append(indentation + "part def " + message1SYSMLname + " :> '#InMessage' {\n" +
                            indentation + indentStep + "private part '@channel' : '#Channel' = " + channelSYSMLname + ";\n");
                    msg2Buf.append(indentation + "part def " + message2SYSMLname + " :> '#OutMessage', " + message1SYSMLname +  " {\n");
                }
                indent(1);

                // handle message fields. in-message fields redefine out-message fields
                int nbFields = sig1.getListOfOriginalAttributes().size();
                // parameters for handling a field
                AvatarType type;
                String attributeType; // SysML name associated to type
                String fieldName1;
                String fieldName2;
                int j=0;
                while (j < nbFields ) { // browse channels' fielsd
                    AvatarAttribute aa1 = sig1.getListOfOriginalAttributes().get(j);

                    // compute parameters for handling this field

                    // compute field type parameters
                    AvatarDataType dataType = null;
                    if (aa1.isDataType()) {
                        dataType = aa1.getDataType();
                        attributeType = dataTypeSysMLname(dataType.getName()) + ";\n";
                        type = AvatarType.UNDEFINED;
                    }
                    else if (aa1.isBool()) {
                        attributeType = "Boolean;\n";
                        type = AvatarType.BOOLEAN;
                    }
                    else if (aa1.isInt()) {
                        attributeType = "Integer;\n";
                        type = AvatarType.INTEGER;
                    } else { attributeType = "Integer;\n"; type = AvatarType.INTEGER; } // TODO : error: SHOULD NOT BE TIMER

                    // compute field names
                    fieldName1 = fieldSysMLname(aa1.getName());
                    fieldName2 = fieldSysMLname(sig2.getListOfOriginalAttributes().get(j).getName());

                    // put field declaration
                    if (out2in) { // depends on direction. symetric.
                        msg1Buf.append(indentation + "attribute " + fieldName1 + " redefines " + fieldName2 + ";\n");
                        msg2Buf.append(indentation + "attribute " + fieldName2 + " : " + attributeType);
                    } else {
                        msg1Buf.append(indentation + "attribute " + fieldName1 + " : " + attributeType);
                        msg2Buf.append(indentation + "attribute " + fieldName2 + " redefines " + fieldName1 + ";\n");
                    }

                    // collect info to be used later
                    sig1Info.addField(fieldName1, type, dataType);
                    sig2Info.addField(fieldName2, type, dataType);

                    j++;
                }
                unindent(1);
                msg1Buf.append(indentation + "}\n");
                msg2Buf.append(indentation + "}\n");

                // put declarations to avsysml
                if (out2in) { // order only relevant for the aesthetics
                    avsysml.append(msg2Buf);
                    avsysml.append(msg1Buf);
                } else {
                    avsysml.append(msg1Buf);
                    avsysml.append(msg2Buf);
                }

                // memorize collected info
                methodMap.put(sig1,sig1Info);
                methodMap.put(sig2,sig2Info);
            }
        }
    }
    /** generates SysML declarations for block methods.
     * To be called while generating block declarations. Directly put generated text in avsysml.
     */
    private void method2SysML(AvatarMethod am){

        MethodInfo methodInfo; // to collect info

        // compute return type (null for void methods)....................

        String returnType; // SysML name of return type, or null
        List<AvatarAttribute> returns = am.getListOfOriginalReturnAttributes(); // size must be <= 1
        if(returns.size() == 0) { // void method
            returnType = null;
            methodInfo = new MethodInfo(methodSysMLname(am.getName()));
        }
        else {
            if (returns.get(0).isDataType()) {
                AvatarDataType returnDataType = returns.get(0).getDataType();
                returnType = dataTypeSysMLname(returns.get(0).getDataType().getName());
                methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.UNDEFINED, returnDataType);

            }
            else if (returns.get(0).isBool()) {
                    returnType = "Boolean";
                    methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.BOOLEAN, null);
            } else if (returns.get(0).isInt()) {
                    returnType = "Integer";
                    methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.INTEGER, null);
            } else { // ERROR : TIMER, should not happen
                    returnType = "Integer";
                    methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.INTEGER, null);
            }
        }

        // put header (different for void and non-void methods).....................

        if (returnType == null)
            avsysml.append(indentation + "action " + methodSysMLname(am.getName()) +  ": '#AvatarVoidMethod'");
        else
            avsysml.append(indentation + "calc " + methodSysMLname(am.getName()) +  ": '#AvatarCalcMethod'");

        // put parameters and return type as fields .......................
        // collect corresponding info

        int nbFields = am.getListOfOriginalAttributes().size();
        if (nbFields == 0 && returnType == null)
            avsysml.append(";\n");
        else {
            avsysml.append(" {\n");
            indent(1);

            int j = 0;
            // iterate on method parameters, put corresponding fields
            while (j < nbFields) {
                AvatarAttribute aa = am.getListOfOriginalAttributes().get(j);
                if (aa.isDataType()) {
                    avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName())
                            + " : " + dataTypeSysMLname(aa.getDataType().getName()) + ";\n");
                    methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.UNDEFINED, aa.getDataType());
                } else if (aa.isInt()) {
                    avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName()) + " : Integer;\n");
                    methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.INTEGER, null);
                } else if (aa.isBool()) {
                    avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName()) + " : Boolean;\n");
                    methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.BOOLEAN, null);
                } else { // ERROR should not happen
                    avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName()) + " : Integer;\n");
                    methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.INTEGER, null);
                }
                j++;
            }
            // put return field for non-void methods
            if (returnType != null)
                avsysml.append(indentation + "return : " + returnType + ";\n");

            unindent(1);
            avsysml.append(indentation + "}\n");
        }
        methodMap.put(am, methodInfo); // memorize collected info
    }

    /** generates SysML declarations for block signals.
     * To be called while generating block declarations. Directly put generated text in avsysml. Signals used in AvatarQueryOnSignal elements must
     * be typed as fifos (other ones are not constrained). Thus, fifoSet must have been computed before calling this method.
     */
    private void signal2SysML(AvatarSignal as){
        avsysml.append(indentation + "part " + methodMap.get(as).getName() + " : " +
                (fifoSet.contains(as) ? "'#Fifo'" : "'#Channel'") + ";\n");
    }

    /** generates SysML declarations for all blocks: iterate on specification block list */
    private void blocks2SysML(){
        avsysml.append("\n" + indentation + "// BLOCKS $$$$$$$$$$$$$$$$$$$$$$$$\n");
        for(AvatarBlock block: avspec.getListOfBlocks())
            if (block.getFather() == null) block2SysML(block);
    }

    /** generates SysML declarations for a blocks and direct access to inner blocks through their name (not their full path) */
    private void block2SysML(AvatarBlock block){
        String blockSYSMLname = blockSysMLname(block.getName());
        // to memorize timers while iterating on attributes (delayed declarations)
        ArrayList<AvatarAttribute> timerList = new ArrayList<AvatarAttribute>();
        chainBlock(blockSYSMLname);
        blocklink(); // to directly access inner blocks through their (short) name

        // put block header --------------------
        avsysml.append("\n" + indentation + "// Block " + blockSYSMLname + "=============\n");
        avsysml.append(indentation + "part " + blockSYSMLname + " : '#AvatarBlock' {\n");
        indent(1);

        // put attribute declarations ----------------------------
        // no default value in SysML, thus default values are set explicitly
        // timer attributes are memorized to be declared later

        if (block.getAttributes() != null && block.getAttributes().size() != 0) {
            avsysml.append("\n" + indentation + "// Attributes ---------------------\n");
            int j = 0;
            int nbAttributes = block.getOriginalAttributes().size();
            while(j < nbAttributes)  { // iterate on block's attributes
                AvatarAttribute aa = block.getOriginalAttributes().get(j);
                if (aa.isDataType()) {
                    avsysml.append(indentation + "attribute " + attributeSysMLname(aa.getName()) +
                            " : " + dataTypeSysMLname(aa.getDataType().getName()) + ";\n");
                }
                else if (aa.isInt()) {
                    avsysml.append(indentation + "attribute " + attributeSysMLname(aa.getName()) + " : Integer");
                    if (aa.hasInitialValue())
                        avsysml.append(" := " + aa.getInitialValue().trim());
                    else
                        avsysml.append(" default := " + AvatarType.INTEGER.getDefaultInitialValue().trim());
                    avsysml.append(";\n");
                }
                else if (aa.isBool()) {
                    avsysml.append(indentation + "attribute " + attributeSysMLname(aa.getName()) + " : Boolean");
                    if (aa.hasInitialValue())
                        avsysml.append(" := " + aa.getInitialValue().trim());
                    else
                        avsysml.append(" default := " + AvatarType.BOOLEAN.getDefaultInitialValue().trim());
                    avsysml.append(";\n");
                }
                else if (aa.isTimer()) {
                    timerList.add(aa);
                } else {} // TODO : should not happen. to remove if not possible
                j++;
            }
        }

        // put constant declarations --------------------
        if (block.getConstants() != null && block.getConstants().size() != 0) {
            avsysml.append("\n" + indentation + "// Constants ---------------------\n");
            int j = 0;
            int nbConstants = block.getConstants().size();
            while(j < nbConstants)  { // iterate on block's constants
                AvatarAttribute aa = block.getOriginalAttributes().get(j);
                if (aa.isDataType()) {
                    avsysml.append(indentation + "readolny attribute " + attributeSysMLname(aa.getName()) +
                            " : " + dataTypeSysMLname(aa.getDataType().getName()) + ";\n");
                }
                else if (aa.isInt()) {
                    avsysml.append(indentation + "readolny attribute " + attributeSysMLname(aa.getName()) + " : Integer");
                    if (aa.hasInitialValue())
                        avsysml.append(" := " + aa.getInitialValue().trim());
                    else
                        avsysml.append(" default := " + AvatarType.INTEGER.getDefaultInitialValue().trim());
                    avsysml.append(";\n");
                }
                else if (aa.isBool()) {
                    avsysml.append(indentation + "readolny attribute " + attributeSysMLname(aa.getName()) + " : Boolean");
                    if (aa.hasInitialValue())
                        avsysml.append(" := " + aa.getInitialValue().trim());
                    else
                        avsysml.append(" default := " + AvatarType.BOOLEAN.getDefaultInitialValue().trim());
                    avsysml.append(";\n");
                }
                else {} // TODO ? error should not happen (timers arn't constants...)
                j++;
            }
        }

        // put method declarations --------------------
        if (block.getMethods() != null && block.getMethods().size() != 0) {
            avsysml.append("\n" + indentation + "// Methods ---------------------\n");
            for (AvatarMethod am : block.getMethods()) method2SysML(am);
        }

        // put signal declarations --------------------
        if (block.getSignals() != null && block.getSignals().size() != 0) {
            avsysml.append("\n" + indentation + "// Signals ---------------------\n");
            for (AvatarSignal as : block.getSignals()) signal2SysML(as);
        }

        // put timer declarations -------------------
        if (timerList != null && timerList.size() != 0) {
            avsysml.append("\n" + indentation + "// Timers ---------------------\n");
            for (AvatarAttribute timer : timerList)
                avsysml.append(indentation + "part " + timerBlockSysMLname(timer.getName()) + ": '#AvatarTimer' = '#AvatarTimer'();\n");
        }

        // put state-machine description --------------------
        avsysml.append("\n" + indentation + "// state-machine -------------------\n");
        statemachine2SysML(block.getStateMachine());

        // sub-blocks -------------------
        List<AvatarBlock> subBlockList = new ArrayList<AvatarBlock>();
        for(AvatarBlock blk: avspec.getListOfBlocks()) { if (blk.getFather() == block) subBlockList.add(blk); }
        if (subBlockList.size() != 0) {
            avsysml.append("\n" + indentation + "// Sub-Blocks øøøøøøøøøøøøøøøøøøøøøøø\n");
            for (AvatarBlock blk : subBlockList) block2SysML(blk);
        }

        unindent(1);
        avsysml.append(indentation + "}\n");
        unchainBlock();
    }


    /** generates state information for each state machine element of the specification and technically
     * added states (before some communication elements). Put this information in a map which is returned.
     */
    private HashMap<AvatarStateMachineElement, StateInfo> computeStateInfo(AvatarStateMachine asm) {

        // the map to fill and return
        HashMap<AvatarStateMachineElement, StateInfo> stateMap = new HashMap<AvatarStateMachineElement, StateInfo>();

        // associate names to future SysML states.........................

        // number to identify states in an unambigous way
        int randomNumber = 0;
        int countNumber = 0;
        int sendNumber = 0;
        int receiveNumber = 0;
        int setNumber = 0;
        int resetNumber = 0;
        int expireNumber = 0;

        // produces and memorize stateinfo structure (couple name, prename) for all state machine element
        for (AvatarStateMachineElement asme : asm.getListOfElements()) {
            StateInfo stateinfo;
            // states without communication hav just a name.
            if (asme instanceof AvatarStartState)
                stateinfo = new StateInfo(startStateSysMLname());
            else if (asme instanceof AvatarStopState)
                stateinfo = new StateInfo(stopStateSysMLname());
            else if (asme instanceof AvatarState)
                stateinfo = new StateInfo(standardStateSysMLname(asme.getName()));
            else if (asme instanceof AvatarQueryOnSignal)
                stateinfo = new StateInfo(countStateSysMLname(((AvatarQueryOnSignal) asme).getSignal().getName(), countNumber++));
            else if (asme instanceof AvatarRandom)
                stateinfo = new StateInfo(randomStateSysMLname(randomNumber++));
                // next cases rely on communication and thus, are subject to the introduction of an additional "preceding" state.
            else if (asme instanceof AvatarActionOnSignal) {
                if (((AvatarActionOnSignal) asme).isSending())
                    stateinfo = new StateInfo(
                            sendStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), sendNumber),
                            presendStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), sendNumber++));
                else
                    stateinfo = new StateInfo(receiveStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), receiveNumber),
                            prereceiveStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), receiveNumber++));
            } else if (asme instanceof AvatarSetTimer) {
                stateinfo = new StateInfo(setTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), setNumber),
                        presetTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), setNumber++));
            } else if (asme instanceof AvatarResetTimer) {
                stateinfo = new StateInfo(resetTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), resetNumber),
                        preresetTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), resetNumber++));
            } else if (asme instanceof AvatarExpireTimer) {
                stateinfo = new StateInfo(expireTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), expireNumber),
                        preexpireTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), expireNumber++));

            } else continue; // TODO ? curious states "AvatarEmpty" not handled
            stateMap.put(asme, stateinfo);
        }
        return stateMap;
    }

    /** generates SysML description of state machine */
    private void statemachine2SysML(AvatarStateMachine asm) {
        HashMap<AvatarStateMachineElement, StateInfo> stateMap = computeStateInfo(asm);

        // put state-machine header to avsysml.............................
        avsysml.append(indentation + "exhibit state '@statemachine' : '#AvatarStateMachine' {\n");
        indent(1);

        // put SysML states descriptions with associated transition descriptions.........
        stateMap.forEach(new State2SysML(stateMap));

        unindent(1);
        avsysml.append(indentation + "}\n");
    }

    /** buffer to build SysML declarations of outgoing transitions from one state(filled by method transitionsAndRequests) */
    private StringBuffer sysMLtransitions = new StringBuffer();
    /** buffer to build in-a-state requests associated to the outgoing transitions of this state (filled by method transitionsAndRequests) */
    private StringBuffer sysMLrequests = new StringBuffer();
    /** communication (and timer) states that require an added state before (as they have incomming transitions with action, from the currently
     * handled state) (filled by method transitionsAndRequests) */
    private List<AvatarStateMachineElement> requirePreCom = new ArrayList<AvatarStateMachineElement>();

    /** to iterate on the state machine elements of one block. An iteration puts a state declaration in avsysml, followed by the declarations of
     * its outgoing transitions. If the needed technically added pre-communication are detected while handling these transitions. The declarations
     * of these added states are added after the transition declarations, followed by their own single outgoing transition.
     */
    private class State2SysML implements BiConsumer<AvatarStateMachineElement, StateInfo> {
        /** provides all states with name information */
        HashMap<AvatarStateMachineElement, StateInfo> stateMap;

        /** false untill stopState has been encountered (as it can be encountered several times but generated only once) */
        Boolean stopStateDone;

        public State2SysML(HashMap<AvatarStateMachineElement, StateInfo> _stateMap) {
            stateMap = _stateMap;
            stopStateDone = false;
        }

        /** puts a state declaration in avsysml, followed by the declarations of
         * its outgoing transitions. If the needed technically added pre-communication are detected while handling these transitions. The declarations
         * of these added states are added after the transition declarations, followed by their own single outgoing transition.
         */
        public void accept(AvatarStateMachineElement asme, StateInfo info) {
            // compute transition declarations and request descriptions for the outgoing transitions of the state
            // identify states that require an added pre-communication state
            indent(1);
            transitionsAndRequests(info.getName(), asme.getNexts(), ((asme instanceof AvatarState) ? "'@pool'" : "'@request'"), stateMap);
            unindent(1);

            String requests = sysMLrequests.toString(); // to put after state declaration

            // put state Descriptions ................................
            // depending on state type

            if(asme instanceof AvatarState){
                avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarStandardState'");
                if (requests.equals(""))
                    avsysml.append(";\n");
                else
                    avsysml.append(" = '#AvatarStandardState'(\n" + requests + "\n" + indentation + ");\n");
            } else

            if(asme instanceof AvatarRandom){ // specific: assign a random value to a variable
                avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarRandomState' = '#AvatarRandomState'(\n");
                if (!requests.equals("")) avsysml.append(requests + ",\n"); // put request description (none means default request)
                indent(1);
                avsysml.append(indentation + "'@state_action'  = '#Assignment'(\n");
                indent(1);
                // the variable to be assigned
                avsysml.append(indentation + "'@target' = " + attributeSysMLname(((AvatarRandom)asme).getVariable()) + ",\n");
                // the random value to assign
                avsysml.append(indentation + "'@value' = '#bound_random'(" +
                        expr2SysML(((AvatarRandom)asme).getMinValue().replaceAll("__",".")) + ", " +
                        expr2SysML(((AvatarRandom)asme).getMaxValue().replaceAll("__",".")) +")\n");
                unindent(1);
                avsysml.append(indentation + ")\n");
                unindent(1);
                avsysml.append(indentation + ");\n");
            } else

            if(asme instanceof AvatarQueryOnSignal){ // specific: assign the content size of a signal to a variable
                avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarCountState' = '#AvatarCountState'(\n");
                if (!requests.equals("")) avsysml.append(requests + ",\n"); // put request description (none means default request)
                indent(1);
                avsysml.append(indentation + "'@state_action'  = '#Assignment'(\n");
                indent(1);
                // the variable to assign
                avsysml.append(indentation + "'@target' = " + attributeSysMLname(((AvatarQueryOnSignal)asme).getAttribute().getName()) + ",\n");
                // the size of the content of the concerned signal
                avsysml.append(indentation + "'@value' = " + methodMap.get(((AvatarQueryOnSignal)asme).getSignal()).getName() + ".'@amount'()\n");
                unindent(1);
                avsysml.append(indentation + ")\n");
                unindent(1);
                avsysml.append(indentation + ");\n");
            } else

            if(asme instanceof AvatarActionOnSignal){
                if(((AvatarActionOnSignal)asme).isSending()) {
                    avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarSendState'");
                    if (requests.equals(""))  // default request
                        avsysml.append(";\n");
                    else // put requests description
                        avsysml.append(" = '#AvatarSendState'(\n" + requests + "\n" + indentation + ");\n");
                } else {
                    avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarReceiveState'");
                    if (requests.equals(""))  // default request
                        avsysml.append(";\n");
                    else
                        avsysml.append(" = '#AvatarReceiveState'(\n" + requests + "\n" + indentation + ");\n");
                }
            } else

            if(asme instanceof AvatarStartState){
                avsysml.append("\n" + indentation + "entry action " + info.getName() + " :'#AvatarStartState'");
                if (requests.equals(""))  // default request
                    avsysml.append(";\n");
                else // put requests description
                    avsysml.append(" = '#AvatarStartState'(\n" + requests + "\n" + indentation + ");\n");
            } else

            if(asme instanceof AvatarStopState){ // specific mandatory empty request list (implicite)
                if(!stopStateDone)
                    avsysml.append("\n" + indentation + "exit action " + info.getName() + " :'#AvatarStopState';\n");
                stopStateDone = true;
            } else

            if(asme instanceof AvatarSetTimer){
                avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarSetTimerState'");
                if (requests.equals(""))  // default request
                    avsysml.append(";\n");
                else  // put requests description
                    avsysml.append(" = '#AvatarSetTimerState'(\n" + requests + "\n" + indentation + ");\n");
            } else

            if(asme instanceof AvatarResetTimer){
                avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarResetTimerState'");
                if (requests.equals(""))  // default request
                    avsysml.append(";\n");
                else  // put requests description
                    avsysml.append(" = '#AvatarResetTimerState'(\n" + requests + "\n" + indentation + ");\n");
            } else

            if(asme instanceof AvatarExpireTimer){
                avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarExpireTimerState'");
                if (requests.equals(""))  // default request
                    avsysml.append(";\n");
                else  // put requests description
                    avsysml.append(" = '#AvatarExpireTimerState'(\n" + requests + "\n" + indentation + ");\n");
            }

            // put transition declarations .................................................
            avsysml.append(sysMLtransitions);

            // put pre-communication states to add and their single outgoing transition
            for (AvatarStateMachineElement aos: requirePreCom) {
                // the name of an added states is obtained by StateInfo.getPreName()
                // the target of its outgoing transition is the communication state itself (StateInfo.getName())
                // endTransition adds distribution law information at the end of the transition declarations
                StateInfo stateinfo = stateMap.get(aos);
                if (aos instanceof AvatarActionOnSignal) {
                    MethodInfo signalinfo = methodMap.get(((AvatarActionOnSignal)aos).getSignal());

                    if (((AvatarActionOnSignal)aos).isSending()) {
                        // put pre-communication State
                        avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreSendState' = '#AvatarPreSendState' (\n");
                        indent(1);
                        avsysml.append(indentation + "'@request' = {\n");
                        indent(1);
                        // sending request from pre-send state to send state
                        avsysml.append(sendRequest2SysML(1, "0", "0", signalinfo, ((AvatarActionOnSignal)aos).getOriginalValues()) + "\n");
                        unindent(1);
                        avsysml.append(indentation + "}\n");
                        unindent(1);
                        avsysml.append(indentation + ");\n");
                        // transition from pre-send state to send state
                        avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() +
                                " then " + stateinfo.getName() +
                                endTransition(0,"","", 1));
                    } else {
                        // put pre-communication State
                        avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreReceiveState' = '#AvatarPreReceiveState' (\n");
                        indent(1);
                        avsysml.append(indentation + "'@request' = {\n");
                        indent(1);
                        // receiving request from pre-receive state to receive state
                        avsysml.append(receiveRequest2SysML(1, "0", "0", signalinfo.getName()) + "\n");
                        unindent(1);
                        avsysml.append(indentation + "}\n");
                        unindent(1);
                        avsysml.append(indentation + ");\n");
                        // transition from pre-receive state to receive state
                        avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() + "\n");
                        indent(1);
                        // updating target variables with received values
                        String doAction = receiveActions2SysML(signalinfo, ((AvatarActionOnSignal)aos).getOriginalValues());
                        if (doAction.length() == 0) // receive simple signal (without carried value)
                            avsysml.append(indentation + "then " + stateinfo.getName() +
                                    endTransition(0,"","", 1));
                        else { // put updating action
                            avsysml.append(doAction);
                            avsysml.append(" then " + stateinfo.getName() +
                                    endTransition(0,"","", 1));
                        }
                        unindent(1);
                    }
                } else if (aos instanceof AvatarSetTimer) { // a special kind of send request
                    // put pre-communication State
                    avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreSendState' = '#AvatarPreSendState' (\n");
                    indent(1);
                    avsysml.append(indentation + "'@request' = {\n");
                    indent(1);
                    // put the specific sending request, carrying a value
                    avsysml.append(setTimerRequest2SysML(1, "0", "0",
                            timerBlockSysMLname(((AvatarSetTimer) aos).getTimer().getName()) ,
                            ((AvatarSetTimer)aos).getTimerValue()) + " }\n");
                    unindent(2);
                    avsysml.append(indentation + ");\n");
                    // put the associated transition
                    avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() +
                            " then " + stateinfo.getName() +
                            endTransition(0,"","", 1));

                } else if (aos instanceof AvatarResetTimer) { // a special kind of send request
                    // put pre-communication State
                    avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreSendState' = '#AvatarPreSendState' (\n");
                    indent(1);
                    avsysml.append(indentation + "'@request' = {\n");
                    indent(1);
                    // put the specific sending request, carrying no value
                    avsysml.append(resetTimerRequest2SysML(1, "0", "0",
                            timerBlockSysMLname(((AvatarResetTimer) aos).getTimer().getName())) + " }\n");
                    unindent(2);
                    avsysml.append(indentation + ");\n");
                    // put the associated transition
                    avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() +
                            " then " + stateinfo.getName() +
                            endTransition(0,"","", 1));

                } else if (aos instanceof AvatarExpireTimer) { // a special kind of send request
                    // put pre-communication State
                    avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreReceiveState' = '#AvatarPreReceiveState' (\n");
                    indent(1);
                    avsysml.append(indentation + "'@request' = {\n");
                    indent(1);
                    // put the specific receiving request, carrying no value
                    avsysml.append(expireTimerRequest2SysML(1, "0", "0",
                            timerBlockSysMLname(((AvatarExpireTimer) aos).getTimer().getName())) + " }\n");
                    unindent(2);
                    avsysml.append(indentation + ");\n");
                    // put the associated transition
                    avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() +
                            endTransition(0,"","", 1));
                    indent(1);
                    avsysml.append(indentation + "then " + stateinfo.getName() + ";\n");
                    unindent(1);
                }
            }

        }
    }

    /** distribution law information, to put  at the end of associated the transition declarations */
    private String endTransition(int delayDistributionLaw, String delayExtra1, String delayExtra2, double probability){

        if (delayDistributionLaw == DELAY_UNIFORM_LAW && probability == 1) return ";\n"; // nothing because default

        StringBuffer result = new StringBuffer(" {\n");
        indent(1);

        // put distribution law
        result.append(indentation + "attribute '@delayDistributionLaw' : String = \"" + DISTRIBUTION_LAWS[delayDistributionLaw] + "\";\n");

        // put distribution law first parameter (if exists)
        if (NB_OF_EXTRA_ATTRIBUTES[delayDistributionLaw] > 0)
            result.append(indentation + "attribute '" + LABELS_OF_EXTRA_ATTRIBUTES_1[delayDistributionLaw] + "' : String = \""
                + delayExtra1 + "\";\n");

        // put distribution law second parameter (if exists)
        if (NB_OF_EXTRA_ATTRIBUTES[delayDistributionLaw] > 1)
            result.append(indentation + "attribute '" + LABELS_OF_EXTRA_ATTRIBUTES_2[delayDistributionLaw] + "' : String = \""
                + delayExtra2 + "\";\n");

        // put probability (if different from the default one)
        if (probability != DEFAULT_PROBABILITY)
            result.append(indentation + "attribute '@weight' : Real = " + probability + ";\n");

        unindent(1);
        result.append(indentation + "}\n");
        return result.toString();
    }

    /** computes transition declarations and request descriptions for the outgoing transitions of the state. Put the corresponding
     * texts in the sysMLtransitions and sysMLrequests StringBuffers. While doing this, identify communication states that require
     * an added pre-communication state.
     * @param srcName name of the source state
     * @param nexts outgoing transitions from the source state
     * @param poolName "pool" or "request" depending on the number of allowed outgoing transitions
     * @param stateMap to find state names
     */
    private void transitionsAndRequests(String srcName, List<AvatarStateMachineElement> nexts, String poolName,
                                        HashMap<AvatarStateMachineElement, StateInfo> stateMap) {
        // initialization
        requirePreCom.clear();
        sysMLtransitions.delete(0, sysMLtransitions.length());
        sysMLrequests.delete(0, sysMLrequests.length());

        int nb = nexts.size();
        if (nb == 0) { // no transition
            sysMLrequests.append(indentation + poolName + " = { null }\n");
            return;
        }
        if (nb == 1) { // no parenthesis required
            indent(1);
            // handle the single transition
            transitionAndRequest(srcName, (AvatarTransition)nexts.get(0), 0, stateMap);
            unindent(1);

            if(sysMLrequests.toString().trim().equals("'#immediate_request'")) { // remove because it is the default
                sysMLrequests.delete(0, sysMLrequests.length());
                return;
            }
            // wrap without parenthesis
            sysMLrequests.insert(0, indentation + poolName + " = {\n");
            sysMLrequests.append("\n" + indentation + "}");
            return;
        }
        // more than one request -> list between parenthesis
        indent(1);
        // put list
        for(int i=0; i<nb; i++){
            transitionAndRequest(srcName, (AvatarTransition)nexts.get(i), i+1, stateMap);
            if(i != nb-1)
                sysMLrequests.append(",\n");
            else
                 sysMLrequests.append("\n");
        }
        unindent(1);
        // wrap with parenthesis
        sysMLrequests.insert(0, indentation + poolName + " = {(\n");
        sysMLrequests.append(indentation + ")}");
    }

    /** remove wrapping brackets of a guard (if exist)*/
    private String clean_guard(String _guard) {
        int open = _guard.indexOf('[');
        int close = _guard.lastIndexOf(']');
        if (open != -1 && close != -1 && close > open)
            return _guard.substring(open+1, close);
        else return _guard;
    }

    /** add a transition declaration in sysMLtransitions and the associated request description in sysMLrequests. Ii the the
     * transition has actions and the transition's target is a communication, then this target is identified as requiring an
     * added pre-communication state (added to requirePreCom). In the produced SysML model, transitions are ordered with indexes
     * beginning at 1.
     *
     * @param srcName source of the transition
     * @param at the transition to handle
     * @param index index of the transition, or 0 if transition is alone (real index is 1)
     * @param stateMap to make state names available
     */
    private void transitionAndRequest(String srcName, AvatarTransition at, int index, HashMap<AvatarStateMachineElement, StateInfo> stateMap){
        int transindex = ((index == 0) ? 1 : index); // true index

        // identify cases and instantiate case specific parameters for the parametrized handling that follows ................

        // declare parameters
        boolean guarded = !at.hasNonDeterministicGuard();
        AvatarStateMachineElement target = at.getNext(0);
        String tgtName; // SysML name of transition's target (in SysML model, may differ from Avatar model, due to added states)
        int requestType = 0; // 0:trivial, 1:Send, 2:Receive, 3:SetTimer, 4: ResetTimer, 5:ExpireTimer

        // computes parameters
        if((at.getActions()!=null && at.getActions().size()!=0) &&
                (target instanceof AvatarActionOnSignal || target instanceof AvatarTimerOperator)) {
            // added pre-communication state required before target (in avatar) which is communication. Type: trivial.
            requirePreCom.add(target);
            tgtName = stateMap.get(target).getPreName(); // transition's target is the added state
        }
        else {
            tgtName = stateMap.get(target).getName(); // transition's target is the same as in Avatar

            if(target instanceof AvatarActionOnSignal){ // communication
                if (((AvatarActionOnSignal)target).isSending())
                    requestType = 1; // Send
                else
                    requestType = 2; // Receive
            }
            else if (target instanceof AvatarSetTimer)
                requestType = 3; // SetTimer
            else if (target instanceof AvatarResetTimer)
                requestType = 4; // ResetTimer
            else if (target instanceof AvatarExpireTimer)
                requestType = 5; // ExpireTimer
        }

        // put transition's request respecting parameters.....................................

        if (guarded) {
            sysMLrequests.append(indentation + "if " +
                    expr2SysML(clean_guard((at).getOriginalGuard())) + " ?\n");
            indent(1);
        }
        // delay don't depend on parameters
        String minDelay = ( at.getOriginalMinDelay().length()==0 ? "0" : expr2SysML(at.getOriginalMinDelay()) );
        String maxDelay = ( at.getOriginalMaxDelay().length()==0 ? "0" : expr2SysML(at.getOriginalMaxDelay()) );

        // call the type specific compute-request method and put request
        if(requestType == 0) // Trivial
            sysMLrequests.append(trivialRequest2SysML(transindex,
                    minDelay,
                    maxDelay));
        else if (requestType == 1) // Send
            sysMLrequests.append(sendRequest2SysML(transindex,
                    minDelay,
                    maxDelay,
                     methodMap.get(((AvatarActionOnSignal)target).getSignal()),
                    ((AvatarActionOnSignal)target).getOriginalValues()));
        else if (requestType == 2) // Receive
            sysMLrequests.append(receiveRequest2SysML(transindex,
                    minDelay,
                    maxDelay,
                    methodMap.get(((AvatarActionOnSignal)target).getSignal()).getName()));
        else if (requestType == 3) // Set
            sysMLrequests.append(setTimerRequest2SysML(transindex,
                    minDelay,
                    maxDelay,
                    timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()),
                    ((AvatarSetTimer) target).getTimerValue()));
        else  if (requestType == 4) // Reset
            sysMLrequests.append(resetTimerRequest2SysML(transindex,
                    minDelay,
                    maxDelay,
                    timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName())));
        else // Expire
            sysMLrequests.append(expireTimerRequest2SysML(transindex,
                    minDelay,
                    maxDelay,
                    timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName())));

        if(guarded) {
            unindent(1);
            sysMLrequests.append("\n" + indentation + "else '#nok_request'(" + transindex + ")");
        }

        // put transition's declaration ........................................

        unindent(2); // to reach indent level of request declaration

        // compute transition'actions
        String doAction; // the SysML action part of the transition
        indent(1);
        if(requestType == 2) // compute receive specific actions (variable update). No other transition actions thanks to added states.
            doAction = receiveActions2SysML(methodMap.get(((AvatarActionOnSignal)target).getSignal()),
                    ((AvatarActionOnSignal)target).getOriginalValues());
        else // computes actions from the Avatar transition's action list
            doAction = transitionActions2SysML(at);
        unindent(1);

        // put transition declaration
        // header
        sysMLtransitions.append("\n" + indentation + "transition : '#AvatarTransition' first " + srcName);
        // index
        if(index > 0) // not default thus explicit index
            sysMLtransitions.append(" if '@index' == " + index + "\n");
        else // default thus implicit index
            sysMLtransitions.append("\n");
        // actions
        indent(1);
        if (doAction == null || doAction.length() == 0) // no action, put target and distribution law information
            sysMLtransitions.append(indentation + "then " + tgtName +
                    endTransition(at.getDelayDistributionLaw(), at.getDelayExtra1(), at.getDelayExtra2(), at.getProbability()));
        else { // put actions, target and distribution law information
            sysMLtransitions.append(doAction);
            sysMLtransitions.append(" then " + tgtName +
                    endTransition(at.getDelayDistributionLaw(),at.getDelayExtra1(),at.getDelayExtra2(), at.getProbability()));
        }
        indent(1);
    }

    /** computes request to non-communication state
     *
     * @param index request index
     * @param min minimal delay. not null and not empty (must be set to "0" in these cases)
     * @param max maximal delay. not null and not empty (must be set to "0" in these cases)
     * @return text of the relevant SysML call to the relevant SysML constructor
     */
    private String trivialRequest2SysML(int index, String min, String max) {
        if (max.equals("0")) // no delay or simple delay
            if (min.equals("0")) // no delay
                if (index == 1) // default thus implicit
                    return indentation + "'#immediate_request'";
                else // explicit
                    return indentation + "'#TrivialRequest'('@index' =  " + index + ")";
            else // simple delay
                if (index == 1) // default thus implicit
                    return indentation + "'#TrivialRequest'('@delay' = " + min + ")";
                else // explicit
                    return indentation + "'#TrivialRequest'('@index' =  " + index + ", '@delay' = " + min + ")";
        else if (max.trim().equals(min.trim())) // simple delay (equal bounds)
            if(index == 1) // default thus implicit
                return indentation + "'#TrivialRequest'('@delay' = " + min + ")";
            else // explicit
                return indentation + "'#TrivialRequest'('@index' =  " + index + ", '@delay' = " + min + ")";
        else // range delay
            if(index == 1) // default thus implicit
                return indentation + "'#TrivialRequest'('@delay' = '#bound_random'(" + min + ", " + max + "))";
            else // explicit
                return indentation + "'#TrivialRequest'('@index' =  " + index + ", '@delay' = '#bound_random'(" + min + ", " + max + "))";
    }

    /** computes request to send state
     *
     * @param index request index
     * @param min minimal delay. not null and not empty (must be set to "0" in these cases)
     * @param max maximal delay. not null and not empty (must be set to "0" in these cases)
     * @param signalInfo name and profile information about the concerned signal
     * @param values strings denoting send parameter values
     * @return text of the relevant SysML call to the relevant SysML constructor
     */
    private String sendRequest2SysML(int index, String min, String max, MethodInfo signalInfo, List<String> values) {
        // request Header (SysML constructor)
        StringBuffer result = new StringBuffer(indentation + "'#SendRequest'(\n");
        indent(1);

        // index
        if (index != 1) // not default thus explicit
            result.append(indentation + "'@index' =  " + index + ",\n");

        // signal (equivalent to channel in SysML)
        result.append(indentation + "'@channel'= " + signalInfo.getName() + ",\n");

        // delay
        if (max.equals("0")) { // no delay or simple delay
            if (!min.equals("0")) // simple delay
                result.append(indentation + "'@delay' = " + min + ",\n");
        }
        else if (max.trim().equals(min.trim())) // simple delay (equal bounds)
            result.append(indentation + "'@delay' = " + min + ",\n");
        else // range delay
            result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n");

        // payload
        int nbFields = signalInfo.getArity();
        if (nbFields == 0) // simple SysML constructor call
            result.append(indentation + "'@payload' = " + signalInfo.getMessageType() + "()\n");
        else { // SysML constructor call with parameters
            result.append(indentation + "'@payload' = " + signalInfo.getMessageType() + "(\n");
            indent(1);
            int j = 0;
            while (j < nbFields) { // iterate on parameters
                result.append(indentation + expr2SysML(values.get(j)) + ",\n");
                j++;
            }
            result.replace(result.length()-2, result.length(), " )\n");
            unindent(1);
        }

        unindent(1);
        result.append(indentation + ")");
        return result.toString();
    }


    /** computes request to set-timer state
     *
     * @param index request index
     * @param min minimal delay. not null and not empty (must be set to "0" in these cases)
     * @param max maximal delay. not null and not empty (must be set to "0" in these cases)
     * @param timer name of the concerned timer
     * @param value string denoting the duration until timer expiration (countdown)
     * @return text of the relevant SysML call to the relevant SysML constructor
     */
    private String setTimerRequest2SysML(int index, String min, String max, String timer, String value) {
        // request Header (SysML constructor)
        StringBuffer result = new StringBuffer(indentation + "'#AvatarSetTimerRequest'(\n");
        indent(1);

        // index
        if (index != 1) // not default thus explicit
            result.append(indentation + "'@index' =  " + index + ",\n");

        // setting a timer is sending a message on a dedicated "@set" channel of the timer block
        result.append(indentation + "'@channel'= " + timer + ".'@set'" + ",\n");

        // delay
        if (max.equals("0")) { // no delay or simple delay
            if (!min.equals("0")) // simple delay
                result.append(indentation + "'@delay' = " + min + ",\n");
        }
        else if (max.equals(min))  // simple delay (equal bounds)
            result.append(indentation + "'@delay' = " + min + ",\n");
        else // range delay
            result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n");

        // payload
        result.append(indentation + "'@payload' = '#TimerSetMsg'(" + value + ")\n");

        unindent(1);
        result.append(indentation + ")");
        return result.toString();
    }

    /** computes request to reset-timer state
     *
     * @param index request index
     * @param min minimal delay. not null and not empty (must be set to "0" in these cases)
     * @param max maximal delay. not null and not empty (must be set to "0" in these cases)
     * @param timer name of the concerned timer
     * @return text of the relevant SysML call to the relevant SysML constructor
     */
    private String resetTimerRequest2SysML(int index, String min, String max, String timer) {
        // request Header (SysML constructor)
        StringBuffer result = new StringBuffer(indentation + "'#AvatarResetTimerRequest'(\n");
        indent(1);

        //index
        if (index != 1) // not default thus explicit
            result.append(indentation + "'@index' =  " + index + ",\n");

        // resetting a timer is sending a message on a dedicated "@reset" channel of the timer block
        result.append(indentation + "'@channel'= " + timer + ".'@reset'" + ",\n");

        // delay
        if (max.equals("0")) { // no delay or simple delay
            if (!min.equals("0")) // simple delay
                result.append(indentation + "'@delay' = " + min + ",\n");
        }
        else if (max.equals(min)) // simple delay (equal bounds)
            result.append(indentation + "'@delay' = " + min + ",\n");
        else // range delay
            result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n");

        // payload (simple message without value)
        result.append(indentation + "'@payload' = '#TimerResetMsg'()\n");

        unindent(1);
        result.append(indentation + ")");
        return result.toString();
    }

    /** computes request to send state
     *
     * @param index request index
     * @param min minimal delay. not null and not empty (must be set to "0" in these cases)
     * @param max maximal delay. not null and not empty (must be set to "0" in these cases)
     * @param signalName name of the concerned signal
     * @return text of the relevant SysML call to the relevant SysML constructor
     */
    private String receiveRequest2SysML(int index, String min, String max, String signalName) {
        // request Header (SysML constructor)
        StringBuffer result = new StringBuffer(indentation + "'#ReceiveRequest'(\n");
        indent(1);

        // index
        if (index != 1) // not default thus explicit
            result.append(indentation + "'@index' =  " + index + ",\n");

        // signal (equivalent to channel in SysML)
        result.append(indentation + "'@channel'= " + signalName);

        // delay
        if (max.equals("0")) // no delay or simple delay
            if (!min.equals("0")) // simple delay
                result.append(",\n" + indentation + "'@delay' = " + min + "\n");
            else // no delay
                result.append("\n");
        else if (max.equals(min)) // simple delay (equal bounds)
            result.append(indentation + "'@delay' = " + min + ",\n");
        else // range delay
            result.append(",\n" + indentation + "'@delay' = '#bound_random'(" + min + ", " + max + ")\n");

        unindent(1);
        result.append(indentation + ")");
        return result.toString();
    }

    private String expireTimerRequest2SysML(int index, String min, String max, String chname) {
        // request Header (SysML constructor)
        StringBuffer result = new StringBuffer(indentation + "'#AvatarExpireTimerRequest'(\n");
        indent(1);

        // index
        if (index != 1) // not default thus explicit
            result.append(indentation + "'@index' =  " + index + ",\n");

        // timer expiration is detected by receiving a message on a dedicated "@expire" channel of the timer block
        result.append(indentation + "'@channel'= " + chname + ".'@expire'");

        // delay
        if (max.equals("0")) // no delay or simple delay
            if (!min.equals("0")) // simple delay
                result.append(",\n" + indentation + "'@delay' = " + min + "\n");
            else // no delay
                result.append("\n");
        else if (max.equals(min)) // simple delay (equal bounds)
            result.append(indentation + "'@delay' = " + min + ",\n");
        else // range delay
            result.append(",\n" + indentation + "'@delay' = '#bound_random'(" + min + ", " + max + ")\n");

        unindent(1);
        result.append(indentation + ")");
        return result.toString();
    }

    /** computes the action part of the SysML transition associated to an Avatar transition */
    String transitionActions2SysML(AvatarTransition at) {
        StringBuffer result;

        int size = at.getNbOfAction();
        if (size == 0) return null;

        // header
        result = new StringBuffer(indentation + "do action : '#TransitionAction' {\n" + indentation + indentStep + "first start;\n");
        indent(1);
        for(int i = 0; i < size; i++) { // iterate on actions of the Avatar transition
            String ac = at.getOriginalAction(i);
            int eq = ac.indexOf("=");
            if(eq != -1) { // assignment action
                String lh = leftHandSysMLname(ac.substring(0,eq).trim()); // lefthand
                String rh = expr2SysML(ac.substring(eq+1).trim()); // righthand
                result.append(indentation + "then assign " + lh + ":= " + rh + ";\n");
            }
            else { // method call
                result.append(indentation + "then action = " + expr2SysML(ac) + ";\n");
            }
        }
        result.append(indentation + "then done;\n");

        unindent(1);
        result.append(indentation + "}");
        return result.toString();
    }

    /** computes the action part of the SysML transition associated to an Avatar transition leading to a receive state. Due to the introduction of
     * pre-communication states (when required), there are no other actions than the updating of variables w.r.t. the received message */
    String receiveActions2SysML(MethodInfo signalInfo, List<String> values) {
        if (values == null || values.size() == 0) return ""; // simple signal without value

        StringBuffer result = new StringBuffer(indentation + "do action : '#ReceiveAction' {\n");
        indent(1);

        // put access to the received message
        result.append(indentation + "item '@msg' : " + signalInfo.getMessageType() + " = '@payload' as " + signalInfo.getMessageType() +
                ";\n" + indentation + "first start;\n");

        int nbFields = signalInfo.getArity();
        int j = 0;
        while (j < nbFields) { // iterate on message fields
           result.append(indentation + "then assign " + leftHandSysMLname(values.get(j)) +
                                " := '@msg'." + signalInfo.getFieldName(j) + ";\n");
           j++;
        }
        result.append(indentation + "then done;\n");

        unindent(1);
        result.append(indentation + "}");
        return result.toString();
    }
}