/* 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 myutil.Conversion;
import myutil.FileException;
import myutil.FileUtils;
import myutil.TraceManager;

import java.io.BufferedReader;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

/**
 * Class TMLArchiTextSpecification
 * Import and export of TML architecture textual specifications
 * Creation: 21/09/2007
 *
 * @author Ludovic APVRILLE
 * @version 1.0 21/09/2007
 */
public class TMLMappingTextSpecification<E> {
    public final static String CR = "\n";
    public final static String SP = " ";
    public final static String CR2 = "\n\n";
    public final static String SC = ";";

    private TMLTextSpecification<E> tmlmtxt;
    private TMLArchiTextSpecification tmlatxt;
    private String spec;
    private String title;

    private TMLMapping<E> tmlmap;
    private ArrayList<TMLTXTError> errors;
    private ArrayList<TMLTXTError> warnings;

    private String keywords[] = {"MAP", "SET", "TMLMAPPING", "ENDTMLMAPPING", "TMLSPEC", "TMLARCHI", "ENDTMLSPEC", "ENDTMLARCHI"};
    private String beginArray[] = {"TMLSPEC", "TMLARCHI", "TMLMAPPING"};
    private String endArray[] = {"ENDTMLSPEC", "ENDTMLARCHI", "ENDTMLMAPPING"};

    private String taskparameters[] = {"PRIORITY"};


    public TMLMappingTextSpecification(String _title) {
        title = _title;
    }

    public void saveFile(String path, String filename) throws FileException {
        File dir = new File(path);
        if (!dir.exists())
            dir.mkdir();

        if (tmlmtxt != null) {
            tmlmtxt.saveFile(path, filename + ".tml");
        }
        if (tmlatxt != null) {
            tmlatxt.saveFile(path, filename + ".tarchi");
        }

        //TraceManager.addDev("Saving mapping files in " + path + filename + ".tmap");
        String header = makeHeader(filename);
        FileUtils.saveFile(path + filename + ".tmap", indent(header + spec));
    }

    public TMLMapping<E> getTMLMapping() {
        return tmlmap;
    }

    public String getSpec() {
        return spec;
    }

    public String indent(String _toIndent) {
        return indent(4, _toIndent);
    }

    public String indent(int _nbDec, String _toIndent) {
        int dec = 0;
        int indexEnd;
        String output = "";
        String tmp;
        int nbOpen = 0;
        int nbClose = 0;

        while ((indexEnd = _toIndent.indexOf('\n')) > -1) {
            tmp = _toIndent.substring(0, indexEnd + 1);
            try {
                _toIndent = _toIndent.substring(indexEnd + 1, _toIndent.length());
            } catch (Exception e) {
                _toIndent = "";
            }
            nbOpen = nbOfOpen(tmp);
            nbClose = nbOfClose(tmp);
            dec -= nbClose * _nbDec;
            tmp = Conversion.addHead(tmp.trim(), ' ', dec);
            dec += nbOpen * _nbDec;
            //TraceManager.addDev("dec=" + dec);
            output += tmp + "\n";
        }
        _toIndent = output;
        return _toIndent;
    }

    private int nbOfOpen(String tmp) {
        return nbOf(tmp, beginArray);
    }

    private int nbOfClose(String tmp) {
        return nbOf(tmp, endArray);
    }

    private int nbOf(String _tmp, String[] array) {
        String tmp;
        //int size;

        for (int i = 0; i < array.length; i++) {
            if (_tmp.startsWith(array[i])) {
                tmp = _tmp.substring(array[i].length(), _tmp.length());
                //TraceManager.addDev("tmp=" + tmp + " _tmp" + _tmp + " array=" + array[i]);
                if ((tmp.length() == 0) || (tmp.charAt(0) == ' ') || (tmp.charAt(0) == '(') || (tmp.charAt(0) == '\n')) {
                    //TraceManager.addDev("Returning 1!!");
                    return 1;
                }
            }
        }
        return 0;
    }

    public String toString() {
        return spec;
    }

    public String toTextFormat(TMLMapping<E> _tmlmap) {
        tmlmap = _tmlmap;
        tmlmap.handleCPs();
        makeTML(tmlmap);
        makeArchi(tmlmap);
        makeMapping(tmlmap);
        return spec;
        //indent();
    }

    public void makeTML(TMLMapping<E> tmlmap) {
        tmlmtxt = new TMLTextSpecification<>(title);
        tmlmtxt.toTextFormat(tmlmap.getTMLModeling());
    }

    public void makeArchi(TMLMapping<E> tmlmap) {
        tmlatxt = new TMLArchiTextSpecification(/*title*/);
        tmlatxt.toTextFormat(tmlmap.getTMLArchitecture());
    }

    public void makeMapping(TMLMapping<E> tmlmap) {
        spec = CR;
        spec += "TMLMAPPING" + CR;
        spec += makeMappingNodes(tmlmap);
        spec += makeMappingCommunicationNodes(tmlmap);
        spec += "ENDTMLMAPPING" + CR;
    }

    public String makeMappingNodes(TMLMapping<E> tmlmap) {
        String tmp = "";
        List<HwExecutionNode> nodes = tmlmap.getNodes();
        List<TMLTask> tasks = tmlmap.getMappedTasks();
        HwNode node;
        TMLTask task;

        for (int i = 0; i < nodes.size(); i++) {
            node = nodes.get(i);
            task = tasks.get(i);

            if ((node != null) && (task != null)) {
                tmp += "MAP " + prepareString(node.getName()) + " " + prepareString(task.getName()) + CR;
                tmp += "SET " + prepareString(task.getName()) + " priority " + task.getPriority() + CR;
            }
        }

        return tmp;
    }

    public String makeMappingCommunicationNodes(TMLMapping<E> tmlmap) {
        String tmp = "";
        List<HwCommunicationNode> nodes = tmlmap.getCommunicationNodes();
        List<TMLElement> elts = tmlmap.getMappedCommunicationElement();
        HwNode node;
        TMLElement elt;

        for (int i = 0; i < nodes.size(); i++) {
            node = nodes.get(i);
            elt = elts.get(i);

            if ((node != null) && (elt != null)) {
                tmp += "MAP " + prepareString(node.getName()) + " " + prepareString(elt.getName()) + CR;
                if (elt instanceof TMLChannel) {
                    tmp += "SET " + prepareString(elt.getName()) + " priority " + ((TMLChannel) (elt)).getPriority() + CR;
                }
                //tmp += "SET " + prepareString(task.getName()) +  " priority " + task.getPriority() + CR;
            }
        }

        return tmp;
    }

    public String makeHeader(String _filename) {
        String tmp = "";
        tmp += "TMLSPEC" + CR;
        tmp += "#include \"" + _filename + ".tml\"" + CR;
        tmp += "ENDTMLSPEC" + CR2;
        tmp += "TMLARCHI" + CR;
        tmp += "#include \"" + _filename + ".tarchi\"" + CR;
        tmp += "ENDTMLARCHI" + CR;
        return tmp;
    }

    /*public String makeNodes(TMLArchitecture tmla) {
      String code = "";
      String name;
      String set;
      ArrayList<HwNode> hwnodes = tmla.getHwNodes();

      for(HwNode node: hwnodes) {
      if (node instanceof HwCPU) {
      name = prepareString(node.getName());
      set = "SET " + name + " ";
      code += "NODE CPU " +  name;
      code += set + "byteDataSize " + node.byteDataSize;
      code += set + "pipelineSize " + node.pipelineSize;
      code += set + "goIdleTime " + node.goIdleTime;
      code += set + "taskSwitchingTime " + node.taskSwitchingTime;
      code += set + "branchingPredictionPenalty " + node.branchingPredictionPenalty;
      code += set + "schedulingPolicy " + node.schedulingPolicy;
      }
      }
      return code;
      }

      public String makeLinks(TMLArchitecture tmla) {
      String code = "";
      String name;
      String set;
      ArrayList<HwLink> hwlinks = tmla.getHwLinks();

      for(hwlinks link: hwlinks) {
      if (link instanceof HwLink) {
      name = prepareString(link.getName());
      set = "SET " + name + " ";
      code += "NODE LINK " +  name;
      code += set + "node " + prepareString(link.hwnode.getName());
      code += set + "bus " + prepareString(link.bus.getName());
      code += set + "priority " + link.getPriority;
      }
      }
      }*/

    /*public String makeDeclarations(TMLModeling tmlm) {
      int i;
      String sb = "";
      sb += "// TML Application - FORMAT 0.1" + CR;
      sb += "// Application: " + title + CR;
      sb += "// Generated: " + new Date().toString() + CR2;

      sb += "// Channels" + CR;
      for(TMLChannel ch:tmlm.getChannels()) {
      sb += "CHANNEL" + SP + ch.getName() + SP + TMLChannel.getStringType(ch.getType()) + SP + ch.getSize();
      if (!ch.isInfinite()) {
      sb += SP + ch.getMax();
      }
      sb += SP + ch.getOriginTask().getName() + SP + ch.getDestinationTask().getName() + CR;
      }
      sb+= CR;

      sb += "// Events" + CR;
      for(TMLEvent evt:tmlm.getEvents()) {
      sb += "EVENT" + SP + evt.getName() + "(";
      for(i=0; i<evt.getNbOfParams(); i++) {
      if (i != 0) {
      sb+= ", ";
      }
      sb += TMLType.getStringType(evt.getType(i).getType());
      }
      sb += ")";
      sb += SP + evt.getTypeTextFormat();
      if (!evt.isInfinite()) {
      sb += SP + evt.getMaxSize();
      }
      sb +=  SP + evt.getOriginTask().getName() + SP + evt.getDestinationTask().getName();

      sb+= CR;
      }
      sb+= CR;

      sb += "// Requests" + CR;
      for(TMLRequest request:tmlm.getRequests()) {
      sb += "REQUEST" + SP + request.getName() + "(";
      for(i=0; i<request.getNbOfParams(); i++) {
      if (i != 0) {
      sb+= ", ";
      }
      sb += TMLType.getStringType(request.getType(i).getType());
      }
      sb += ")";
      for(TMLTask t: request.getOriginTasks()) {
      sb+= SP + t.getName();
      }
      sb += SP + request.getDestinationTask().getName();
      sb+= CR;
      }
      sb+= CR;

      return sb;

      }

      public String makeTasks(TMLModeling tmlm) {
      String sb = "";
      for(TMLTask task: tmlm.getTasks()) {
      sb += "TASK" + SP + task.getName() + CR;
      sb += makeActivity(task);
      sb += "ENDTASK" + CR2;
      }
      return sb;
      }

      public String makeActivity(TMLTask task) {
      String sb = "";
      sb += "//Local variables" + CR;

      for(TMLAttribute attr: task.getAttributes()) {
      sb += TMLType.getStringType(attr.getType().getType()) + SP + attr.getName();
      if ((attr.getInitialValue() != null) && (attr.getInitialValue().length() > 0)){
      sb += " = " + attr.getInitialValue();
      }
      sb += CR;
      }

      sb += CR;
      sb += "//Behavior" + CR;
      sb += makeBehavior(task, task.getActivityDiagram().getFirst());

      return sb;
      }*/

    public boolean makeTMLMapping(String _spec, String path) {

        DIPLOElement.resetID();

        spec = _spec;
        //tmlmap = new TMLMappingodeling();
        errors = new ArrayList<TMLTXTError>();
        warnings = new ArrayList<TMLTXTError>();

        spec = Conversion.removeComments(spec);
        spec = applyInclude(spec, path);

        //TraceManager.addDev(spec);

        TMLModeling<E> tmlm = makeTMLModeling();
        //TraceManager.addDev("TML modeling:" + tmlm);

        TMLArchitecture tarchi = makeArchitectureModeling();

        if ((errors.size() != 0) || (tmlm == null) || (tarchi == null)) {
            return false;
        }

        tmlmap = new TMLMapping<>(tmlm, tarchi, false);

        TraceManager.addDev("Compiling mapping...");

        browseCode();

        return (errors.size() == 0);
    }


    public TMLModeling<E> makeTMLModeling() {
        TMLTextSpecification<E> t = new TMLTextSpecification<>("from file");

        // Import errors and warnings

        String spectml;
        int index0 = spec.indexOf("TMLSPEC");
        int index1 = spec.indexOf("ENDTMLSPEC");

        if ((index0 == -1) || (index1 == -1) || (index1 < index0)) {
            addError(0, 0, 0, "No TMLSPEC / ENDTMLSPEC directives", null);
            return null;
        }

        spectml = spec.substring(index0 + 7, index1);

        TraceManager.addDev("Compiling TML...");

        boolean ret = t.makeTMLModeling(spectml);
        //errors.addAll(t.getErrors());
        //warnings.addAll(t.getWarnings());
        TraceManager.addDev(t.printSummary());

        if (!ret) {
            return null;
        }

        return t.getTMLModeling();
    }

    public TMLArchitecture makeArchitectureModeling() {
        TMLArchiTextSpecification t = new TMLArchiTextSpecification(/*"from file"*/);

        // Import errors and warnings

        String spectml;
        int index0 = spec.indexOf("TMLARCHI");
        int index1 = spec.indexOf("ENDTMLARCHI");

        if ((index0 == -1) || (index1 == -1) || (index1 < index0)) {
            addError(0, 0, 0, "No TMLARCHI / ENDTMLARCHI directives", null);
            return null;
        }

        spectml = spec.substring(index0 + 8, index1);

        TraceManager.addDev("Compiling architecture...");

        boolean ret = t.makeTMLArchitecture(spectml);
        //errors.addAll(t.getErrors());
        //warnings.addAll(t.getWarnings());
        TraceManager.addDev(t.printSummary());

        if (!ret) {
            return null;
        }

        return t.getTMLArchitecture();
    }

    public String printErrors() {
        String ret = "";
        for (TMLTXTError error : errors) {
            ret += "ERROR at line " + error.lineNb + ": " + error.message + CR;
            try {
                if (error.lineString == null) {
                    ret += "->" + spec.split("\n")[error.lineNb] + CR2;
                } else {
                    ret += "->" + error.lineString + CR2;
                }
            } catch (Exception e) {
                ret += "(Code line not accessible)" + CR;
            }
        }
        return ret;
    }

    public String printWarnings() {
        String ret = "";
        for (TMLTXTError error : warnings) {
            ret += "WARNING at line " + error.lineNb + CR;
            ret += error.message + CR;
        }
        return ret;
    }

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

        return ret;
    }

    public void browseCode() {
        // Browse lines of code one after the other
        // Build accordinlgy the TMLModeling and updates errors and warnings
        // In case of fatal error, immedialty quit code bowsing


        String s;
        String s1;
        String[] split;
        int lineNb = 0;

        //    String instruction;

        String specarchi;
        int index0 = spec.indexOf("TMLMAPPING");
        int index1 = spec.indexOf("ENDTMLMAPPING");

        if ((index0 == -1) || (index1 == -1) || (index1 < index0)) {
            addError(0, 0, 0, "No TMLMAPPING / ENDTMLMAPPING directives", null);
            return;
        }

        specarchi = spec.substring(index0 + 10, index1);

        StringReader sr = new StringReader(specarchi);
        BufferedReader br = new BufferedReader(sr);

        try {
            while ((s = br.readLine()) != null) {
                if (s != null) {
                    s = s.trim();
                    //TraceManager.addDev("s=" + s);
                    s = removeUndesiredWhiteSpaces(s, lineNb);
                    s1 = Conversion.replaceAllString(s, "\t", " ");
                    s1 = Conversion.replaceRecursiveAllString(s1, "  ", " ");
                    //TraceManager.addDev("s1=" + s1);
                    if (s1 != null) {
                        split = s1.split("\\s");
                        if (split.length > 0) {
                            //TraceManager.addDev("analyse");
                            analyseInstruction(s, lineNb, split);
                            //TraceManager.addDev("end analyse");
                        }
                    }

                    lineNb++;
                }
            }
        } catch (Exception e) {
            TraceManager.addDev("Exception when reading specification: " + e.getMessage());
            addError(0, lineNb, 0, "Exception when reading specification", null);
        }
    }

    public void addError(int _type, int _lineNb, int _charNb, String _msg, String _lineString) {
        TMLTXTError error = new TMLTXTError(_type);
        error.lineNb = _lineNb;
        error.charNb = _charNb;
        error.message = _msg;
        error.lineString = _lineString;
        errors.add(error);
    }

    public int analyseInstruction(String _line, int _lineNb, String[] _split) {
        String error;
        //  String params;
        // String id;
        //int tmp, tmp0, tmp1, i;

        HwExecutionNode hwnode;
        TMLTask task;
        TMLChannel channel;
        HwCommunicationNode hwcommnode;
        TMLElement elt;

        // MAP
        if (isInstruction("MAP", _split[0])) {

            if (_split.length != 3) {
                error = "A MAP instruction must be used with 2 parameters, and not " + (_split.length - 1);
                addError(0, _lineNb, 0, error, _line);
                return -1;
            }

            if (!checkParameter("MAP", _split, 1, 0, _lineNb, _line)) {
                return -1;
            }

            if (!checkParameter("MAP", _split, 2, 0, _lineNb, _line)) {
                return -1;
            }

            hwnode = tmlmap.getHwExecutionNodeByName(_split[1]);

            if (hwnode == null) {
                hwcommnode = tmlmap.getHwCommunicationNodeByName(_split[1]);
                if (hwcommnode == null) {
                    error = "No node named " + _split[1];
                    addError(0, _lineNb, 0, error, _line);
                    return -1;
                } else {
                    elt = tmlmap.getCommunicationElementByName(_split[2]);
                    if (elt == null) {
                        error = "No communication element named " + _split[1];
                        addError(0, _lineNb, 0, error, _line);
                        return -1;
                    }
                    tmlmap.addCommToHwCommNode(elt, hwcommnode);
                }
            } else {
                task = tmlmap.getTaskByName(_split[2]);
                if (task == null) {
                    error = "Unknown task: " + _split[2];
                    addError(0, _lineNb, 0, error, _line);
                    return -1;
                }

                tmlmap.addTaskToHwExecutionNode(task, hwnode);
            }
        } // MAP


        // SET
        if (isInstruction("SET", _split[0])) {

            if (_split.length < 4) {
                error = "A set instruction must be used with at least 3 parameters, and not " + (_split.length - 1);
                addError(0, _lineNb, 0, error, _line);
                return -1;
            }

            if (!checkParameter("SET", _split, 1, 0, _lineNb, _line)) {
                return -1;
            }

            if (!checkParameter("SET", _split, 2, 3, _lineNb, _line)) {
                return -1;
            }

            if (!checkParameter("SET", _split, 3, 1, _lineNb, _line)) {
                return -1;
            }

            task = null;
            channel = null;

            task = tmlmap.getTaskByName(_split[1]);
            channel = tmlmap.getChannelByName(_split[1]);
            if ((task == null) && (channel == null)) {
                error = "Unknown task / channel: " + _split[1];
                addError(0, _lineNb, 0, error, _line);
                return -1;
            }

            if (_split[2].toUpperCase().equals("PRIORITY")) {
                if (task != null) {
                    task.setPriority(Integer.decode(_split[3]).intValue());
                } else {
                    if (channel != null) {
                        channel.setPriority(Integer.decode(_split[3]).intValue());
                    }
                }
            }

        } // SET

        // Other command
        if ((_split[0].length() > 0) && (!(isInstruction(_split[0])))) {
            error = "Syntax error in mapping information: unrecognized instruction: " + _split[0];
            addError(0, _lineNb, 0, error, _line);
            return -1;

        } // Other command

        return 0;
    }

    // Type 0: id
    // Type 1: numeral
    // Type 3: Task parameter
    // Type 5: '='
    // Type 6: attribute value
    // Type 7: id or numeral

    public boolean checkParameter(String _inst, String[] _split, int _parameter, int _type, int _lineNb, String _line) {
        boolean err = false;
        String error;

        if (_parameter < _split.length) {
            switch (_type) {
                case 0:
                    if (!isAValidId(_split[_parameter])) {
                        err = true;
                    }
                    break;
                case 1:
                    if (!isANumeral(_split[_parameter])) {
                        err = true;
                    }
                    break;
                case 3:
                    if (!isIncluded(_split[_parameter], taskparameters)) {
                        err = true;
                    }
                    break;
                case 4:
                    if (!isAValidId(getEvtId(_split[_parameter]))) {
                        err = true;
                        //TraceManager.addDev("Unvalid id");
                    } else if (!TMLEvent.isAValidListOfParams(getParams(_split[_parameter]))) {
                        //TraceManager.addDev("Unvalid param");
                        err = true;
                    }
                    break;
                case 5:
                    if (!(_split[_parameter].equals("="))) {
                        //TraceManager.addDev("Error of =");
                        err = true;
                    }
                    break;
                case 6:
                    if (_inst.equals("BOOL")) {
                        String tmp = _split[_parameter].toUpperCase();
                        if (!(tmp.equals("TRUE") || tmp.equals("FALSE"))) {
                            err = true;
                        }
                    } else {
                        if (!isANumeral(_split[_parameter])) {
                            err = true;
                        }
                    }
                    break;
                case 7:
                    if (!isAValidId(_split[_parameter]) && !isANumeral(_split[_parameter])) {
                        err = true;
                    }
                    break;
            }
        } else {
            err = true;
        }
        if (err) {
            error = "Unvalid parameter #" + _parameter + " ->" + _split[_parameter] + "<- in " + _inst + " instruction";
            addError(0, _lineNb, 0, error, _line);
            return false;
        }
        return true;
    }

    public boolean isInstruction(String instcode, String inst) {
        return (inst.toUpperCase().compareTo(instcode) == 0);
    }

    public boolean isInstruction(String instcode) {
        return (!checkKeywords(instcode));
    }

    public boolean isAValidId(String _id) {
        if ((_id == null) || (_id.length() == 0)) {
            return false;
        }

        boolean b1 = (_id.substring(0, 1)).matches("[a-zA-Z]");
        boolean b2 = _id.matches("\\w*");
        boolean b3 = checkKeywords(_id);

        return (b1 && b2 && b3);
    }

    public boolean isANumeral(String _num) {
        return _num.matches("\\d*");
    }

    public boolean checkKeywords(String _id) {
        String id = _id.toUpperCase();
        for (int i = 0; i < keywords.length; i++) {
            if (id.compareTo(keywords[i]) == 0) {
                return false;
            }
        }
        return true;
    }

    public boolean isIncluded(String _id, String[] _list) {
        String id = _id.toUpperCase();
        for (int i = 0; i < _list.length; i++) {
            if (id.compareTo(_list[i].toUpperCase()) == 0) {
                return true;
            }
        }
        return false;
    }

    public String removeUndesiredWhiteSpaces(String _input, int _lineNb) {
//        String error, tmp;
//        int index0, index1, index2;

        return _input;
    }

    private String getEvtId(String _input) {
        int index = _input.indexOf('(');
        if (index == -1) {
            return _input;
        }
        return _input.substring(0, index);
    }

    private String getParams(String _input) {
        //TraceManager.addDev("input=" + _input);
        int index0 = _input.indexOf('(');
        int index1 = _input.indexOf(')');
        if ((index0 == -1) || (index1 == -1)) {
            return _input;
        }
        return _input.substring(index0 + 1, index1);
    }

    private String prepareString(String s) {
        return s.replaceAll("\\s", "");
    }

    private String applyInclude(String _s, String path) {
        StringReader sr = new StringReader(spec);
        BufferedReader br = new BufferedReader(sr);
        String split[];
        String s;
        String s1;
        String output = "";
        String content;
        boolean found;
        int lineNb = 0;

        try {
            while ((s = br.readLine()) != null) {
                lineNb++;
                if (s != null) {
                    found = false;
                    s = s.trim();
                    s1 = Conversion.replaceAllString(s, "\t", " ");
                    s1 = Conversion.replaceRecursiveAllString(s1, "  ", " ");
                    if (s1 != null) {
                        split = s1.split("\\s");
                        if (split.length > 0) {
                            if ((split[0].equals("#include")) && (split.length == 2)) {
                                s1 = split[1];
                                if ((s1.charAt(0) == '\"') && (s1.charAt(s1.length() - 1) == '\"')) {
                                    s1 = s1.substring(1, s1.length() - 1).trim();
                                    //TraceManager.addDev("Loading file:" + path + s1);
                                    try {
                                        content = FileUtils.loadFile(path + s1);
                                        output += content + CR;
                                        found = true;
                                    } catch (FileException fe) {
                                        addError(0, lineNb, 0, "Could not include file:" + path + s1, null);
                                    }
                                }

                            }
                        }
                    }

                    if (!found) {
                        output += s + CR;
                    }

                }
            }
        } catch (Exception e) {
            TraceManager.addDev("Exception when reading specification: " + e.getMessage());
            addError(0, lineNb, 0, "Exception when reading specification", null);
        }

        return output;
    }
}