/*
 * Created on 16 fevr. 07
 */
package com.dassault.cecilia.lib.mbsa.stepuser.seqgen;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.zip.ZipInputStream;

import com.dassault.cecilia.DocProperty;
import com.dassault.cecilia.lib.distrib.JXTAProperties;
import com.dassault.cecilia.lib.distrib.entityinfo.InfoAppList;
import com.dassault.cecilia.lib.distrib.entityinfo.InfoPeerApp;
import com.dassault.cecilia.lib.distrib.entityinfo.InfoServerApp;
import com.dassault.cecilia.lib.distrib.jxtautility.JXTARuntimeException;
import com.dassault.cecilia.lib.distrib.jxtautility.Ressources;
import com.dassault.cecilia.lib.distrib.peers.CECILIAClient;
import com.dassault.cecilia.lib.distrib.request.ReqAutomatonClient;
import com.dassault.cecilia.lib.distrib.request.ReqState;
import com.dassault.cecilia.lib.distrib.request.Request;
import com.dassault.cecilia.lib.mbsa.StepperException;
import com.dassault.cecilia.lib.mbsa.stepper.BanStepper;
import com.dassault.cecilia.lib.mbsa.stepuser.MsgStepUser;
import com.dassault.cecilia.lib.mbsa.stepuser.StepperInterruptException;
import com.dassault.cecilia.lib.mbsa.stepuser.error.LogGenComputeRecord;
import com.dassault.cecilia.lib.mbsa.stepuser.error.LogGenLayoutXML;
import com.dassault.cecilia.lib.mbsa.stepuser.error.LogGenRecord;
import com.dassault.cecilia.lib.mbsa.stepuser.model.ModelTrans;
import com.dassault.cecilia.lib.mbsa.translator.model.IndentWriter;
import com.dassault.cecilia.lib.util.ControllableAction;

public class SeqGeneratorDistributed extends SeqGenAbstract {

  private static final int MAX_GAME_NUMBER = Integer.MAX_VALUE;

  @DocProperty(def="false", dynamic=true, 
      oldNames="dassault.altalib.seqgen.distrib.internal",
      comment="Lors de la generation de sequences en mode distribue, utilise la meme JVM que le serveur de calcul.")
  static final String PROP_SEQGEN_DISTRIB_INTERNAL = DocProperty.PROP_MBSA + ".seqgen.distrib.internal";
  
  @DocProperty(def="false", dynamic=true, 
      oldNames="dassault.altalib.seqgen.distrib.keepresults",
      comment="Concerve les fichiers temporaires des resultats lors de la generation de sequences en mode distribue.")
  static final String PROP_SEQGEN_DISTRIB_KEEP_RESULTS = DocProperty.PROP_MBSA + ".seqgen.distrib.keepresults";

  @DocProperty(def="false", dynamic=true, 
      oldNames="dassault.altalib.seqgen.distrib.keeprecords",
      comment="Concerve les fichiers temporaires des erreurs lors de la generation de sequences en mode distribue.")
  static final String PROP_SEQGEN_DISTRIB_KEEP_RECORDS = DocProperty.PROP_MBSA + ".seqgen.distrib.keeprecords";

  private BanStepper _srcStepper;
  private SeqGenerator _srcGenerator;

  /** Le fichier script ban qui sera envoye sur tous les serveurs*/
  private File _subSriptFile;
  /** Liste de fichier resultat intermediaire*/
  private ArrayList<File> _subResults;	// Warning size = _nbTrans
  /** Liste de fichier de log genere sur les serveurs*/
  private ArrayList<File> _subRecords;
  /** Liste des targets : PTG - Support multi-target **/
  private Collection<SeqGenTarget> _targets;
  private int _nbTrans;
  //final File BASEDIR = new File(System.getProperty(DocProperty.PROP_SYST_TMPDIR));
  private char[] _states;
  private int _nbrMerged, _nbrFinnished, _nbrServer;
  private JXTARuntimeException _mergeException;
  private Request[] _requests;

  /** Nombre d'erreur*/
  private int _result = 0;

  /** Generation de sequence en plusieur morceaux.
   *  Un generation de sequence a l'ordre N d'un model avec T transition,
   *  est transformee en T generation a l'ordre N-1. 
   *  Chaque sous-generation commence avec une liste de transition initiale "initlist" 
   *  qui dans notre cas est la transtion numero t. Chaque sous-generation
   *  dispo d'un ensemble de transition tirable "playlist" qui est egale a l'ensemble 
   *  de toutes les transitions moins la transition t.
   *  On obtient T fichiers resultats qui seront merge's
   *   */

  public SeqGeneratorDistributed(SeqGenerator srcGen, BanStepper stepper) throws IOException {
    _srcGenerator = srcGen;
    _targets = new ArrayList<SeqGenTarget>();
    _srcGenerator.getTarget().getTargets(_targets);

    _srcStepper = stepper; 
    _nbTrans = _srcStepper.getNbrTransition();

    _subSriptFile = File.createTempFile("tmp", ".subscript");
    _subSriptFile.deleteOnExit();
    _subResults = new ArrayList<File>();
    _subRecords = new ArrayList<File>();
  }

  private CECILIAClient _distributedClient;

  
  private void verifyServers() {
    _distributedClient = CECILIAClient.getCECILIAClient(true);
    _nbrServer = _distributedClient.numberServer(); 
    
    if (_nbrServer == 0) {
      _distributedClient.refreshNetwork();
      _nbrServer = _distributedClient.numberServer(); 
      if (_nbrServer == 0) {
        int nbrRefusedServeur = _distributedClient.getConnexionRefusedPeer().size();
        if (nbrRefusedServeur>0)
          throw new JXTARuntimeException("No compatible server available");
        throw new JXTARuntimeException("No server available");
      }
    }
    
    
    boolean internalBan = Boolean.getBoolean(PROP_SEQGEN_DISTRIB_INTERNAL);
    _nbrServer = 0;
    InfoAppList infoApps = _distributedClient.getRemoteAppList();
    Iterator<? extends InfoPeerApp> iter = infoApps.iterator();
    while (iter.hasNext()) {
      InfoPeerApp app = iter.next();
      if (!(app instanceof InfoServerApp)) continue;
      InfoServerApp srv = (InfoServerApp)app;
      if (!srv.hasEngine(internalBan ? Ressources.ENGINEBAN_INTERNAL : Ressources.ENGINEBAN_EXTERNAL))
        continue;
      if (srv.getVersion().equals("1.8.1")) {
        srv.setToBeUSed(false);
        continue;
      }
      if (srv.getVersion().equals("1.8.0")) {
        srv.setToBeUSed(false);
        continue;
      }
      if (!srv.canBeUsedForComputation())
        continue;
      _nbrServer++;
    }
    
    if (_nbrServer == 0) 
      throw new JXTARuntimeException("No compatible server available (version 1.8.2 or more)");
  }
  
  
  @Override
  public int execute() throws IOException, StepperException {
    // Verification du stepper - Nombre de transition 
    if (_nbTrans == 0)
      throw new StepperException("0 transition");

    try {
      // Verification du stepper - Presence du fichier compile
      File stepperJarFile = _srcStepper.getSrcFile();
      if (!stepperJarFile.isFile() || !stepperJarFile.canRead())
        throw new StepperException("Compiled stepper file not accessible !!");

      verifyServers();
      
      createSubScriptSeqGenStepper();
      executeSubScriptBanRemote();
      return _result;
    } finally {
      trashTemp();
      _distributedClient = null;
    }
  }

  @Override
  public void callbackRequestSuspend() {
    if (_distributedClient!=null)
      _distributedClient.pauseDistribution();
  }
  @Override
  public void callbackRequestResume() {
    if (_distributedClient!=null)
      _distributedClient.resumeDistribution();
  }

  /** Creation du script generique de sous-generation a l'ordre N-1.
   *  C'est le meme script pour les T generations.
   *  */
  private void createSubScriptSeqGenStepper() throws IOException {

    StringBuffer sb = new StringBuffer();
    IndentWriter writer = new IndentWriter(sb,"\t");

    writer.write("<?xml version=\"1.0\" encoding=\"iso-8859-15\"?>\n");
    writer.write("<process name=\"Distrib.Process\" basedir=\".\">\n");
    writer.addIndent(1);

    //writer.write("<echo message=\"Jar : ${dban.basedir}/${dban.stepper.jarsrc}\"/>\n");

    writer.write("<stepper class=\"");
    writer.write(_srcStepper.getSrcReader().getClass().getName());
    writer.write("\"\n");
    writer.addIndent(1);
    writer.write(" srcfile=\"${dban.basedir}/${dban.stepper.jarsrc}\"\n");
    writer.write(" id=\"distribStepper\">\n");
    Map<String, String> stepperParams = _srcStepper.getSrcParams();
    for (Map.Entry<String, String> entry : stepperParams.entrySet()) {
      writer.write("<param name=\"");
      writer.write(entry.getKey());
      writer.write("\" value=\"");
      writer.write(entry.getValue());
      writer.write("\"/>\n");
    }
    writer.addIndent(-1);
    writer.write("</stepper>\n");

    for (int i=0; i<_targets.size(); i++) {
      writer.write("<set name=\"temp.seqgen.output.");
      writer.write(Integer.toString(i));
      writer.write("\"> <ftemp id=\"temp.seqgen.id.");
      writer.write(Integer.toString(i));
      writer.write("\" prefix=\"dban_seqgen\" suffix=\".res\"/> </set>\n");
    }

    writer.write("<set name=\"seqgen.result\">\n");
    writer.addIndent(1);
    writer.write("<seqgenstepper stepper=\"");
    writer.write("distribStepper");
    writer.write("\" initlist=\"${dban.seqgen.initlist}\" playlist=\"${dban.seqgen.playlist}\">\n"); 
    writer.addIndent(1);

    int cptTgt = 0;
    for (SeqGenTarget tgt : _targets) {
      writer.write("<target name=\"");
      writer.write(tgt.getName());
      writer.write("\" value=\"");
      writer.write(tgt.getValue());
      writer.write("\" fileref=\"temp.seqgen.id."+cptTgt+"\">\n");
      writer.addIndent(1);

      // ecriture des parametres des targets (modification de l'ordre)
      /*
            Map<String, String> params = tgt.getParams();
            for (Map.Entry<String, String> param : params.entrySet()) {
                writer.write("<param name=\"");
                writer.write(param.getKey());
                writer.write("\" value=\"");
                writer.write(param.getValue());
                writer.write("\"/>\n");
			}*/

      writer.addIndent(-1);
      writer.write("</target>\n");
      cptTgt++;
    }
    //ecriture des parametres du secgenManager
    Map<String, String> seqgenparams = _srcGenerator.getParams().getParams();
    String finder = seqgenparams.get("finder");
    for (Map.Entry<String, String> param : seqgenparams.entrySet()) {
      String key = param.getKey();
      if (key.equals("distrib"))
        continue; //le sous-script ne doit pas etre distribue
      writer.write("<param name=\"");
      writer.write(param.getKey());
      writer.write("\" value=\"");
      if (key.equals(finder+".order")) {
        int oldOder = Integer.valueOf(param.getValue());
        if (_nbTrans > 1)
          writer.write(String.valueOf(oldOder-1));
        else
          writer.write(String.valueOf(oldOder));
      }
      else writer.write(param.getValue());
      writer.write("\"/>\n");
    }
    writer.write("<destination file=\"${dban.basedir}/${dban.stepper.record}\"/>\n");

    writer.addIndent(-1);
    writer.write("</seqgenstepper>\n");
    writer.addIndent(-1);
    writer.write("</set>\n");

    writer.write("<zip destfile=\"${dban.basedir}/${dban.seqgen.results}\"" +
        " comment=\"Generate by Ban\">\n");
    writer.addIndent(+1);
    for (int i=0; i<_targets.size(); i++) {
      writer.write("<zipfileset file=\"%{temp.seqgen.output.");
      writer.write(Integer.toString(i));
      writer.write("}\" fullpath=\"ResultTarget");
      writer.write(Integer.toString(i));
      writer.write(".res\"/>\n");
    }
    writer.addIndent(-1);
    writer.write("</zip>\n");

    writer.addIndent(-1);
    writer.write("</process>\n");

    if (MsgStepUser.LOG.isLoggable(Level.FINEST))
      MsgStepUser.LOG.finest(sb.toString());
    FileWriter fw = new FileWriter(_subSriptFile);
    fw.write(sb.toString());
    fw.flush();
    fw.close();
  }

  /** 
   * execution distante des N sous-generation
   * */
  private void executeSubScriptBanRemote() throws IOException, StepperException {

    _srcGenerator.addRecord(new LogGenRecord(
        MsgStepUser.MSG_CMD_COMPUTE_LINE));
    _srcGenerator.addRecord(new LogGenRecord(
        MsgStepUser.MSG_CMD_COMPUTE_TARGET, _srcGenerator.getTarget().toString()));
    _srcGenerator._seqFinder.printHeader();
    _srcGenerator.addRecord(new LogGenRecord(
        MsgStepUser.MSG_CMD_COMPUTE_LINE2));

    CECILIAClient client = CECILIAClient.getCECILIAClient();
    for (int i = 0; i < _nbTrans; i++) {
      {
        File subRes = File.createTempFile("distribRemote_Tr"+i+"_", ".subresult");
        subRes.deleteOnExit();
        _subResults.add(subRes);
      }
      {
        File subRec = File.createTempFile("distribRemote_Tr"+i+"_", ".subrecord");
        subRec.deleteOnExit();
        _subRecords.add(subRec);
      }
    }

    // diffusion du fichier script et jar qui est commun aux _nbTrans generation
    File stepperJarFile = _srcStepper.getSrcFile();
    String jarUniqueName = client.makeUniqueFileID(stepperJarFile.getAbsolutePath(), true);
    client.diffuseFile(stepperJarFile, jarUniqueName);
    String subScriptUniqueName = client.makeUniqueFileID(_subSriptFile.getAbsolutePath(), true);
    client.diffuseFile(_subSriptFile, subScriptUniqueName);

    // creation des _nbTrans requetes avec les bons arguments
    int nbGames = _srcGenerator._seqFinder.getNbGame(1);
    if (nbGames > MAX_GAME_NUMBER){
      //TODO CXU gerer les modeles trop gros
    }
    _requests = new Request[1];
    if (_nbTrans > 1){
      Iterator<SeqGame> games = _srcGenerator._seqFinder.getGames(1);
      int index = 0;
      ArrayList<Request> requests = new ArrayList<Request>(_nbTrans);
      while (games.hasNext()) {
        SeqGame game = games.next();

        Request req = createBanReq (client, index, 
            subScriptUniqueName, jarUniqueName,
            game.getPlayString(),
            game.getInitString());
        req.setPriority(Integer.getInteger(JXTAProperties.PROP_PRIO_DELTA_SEQ, 0));
        //                client.addRequestToStock(req);
        requests.add(req);
        //                _requests[index] = req;
        index++;
      }
      _requests = requests.toArray(_requests);
    } else {
      //gestion du cas particulier avec une seule transition
      Request req = createBanReq (client, 0, 
          subScriptUniqueName, jarUniqueName,
          createFullTransListString(),
          "\t");

      //            client.addRequestToStock(req);
      _requests[0] = req;
    }
    _states = new char[_requests.length];
    for (int i = 0; i < _states.length; i++) 
      _states[i] = 0;
    _nbrMerged = _nbrFinnished = 0;
    //        _requests = new Request[_nbTrans];


    long timer = (new Date()).getTime();
    Thread finnishThread = new Thread("SeqGeneratorDistrib-Finnish"){
      @Override
      public void run() {
        try {
          while (_nbrFinnished != _states.length) {
            for (int i = 0; i < _states.length; i++) {
              if (_states[i] != 1) continue;
              if (!_requests[i].isFinished()) continue;
              _nbrFinnished++;
              _states[i]++;
              requestStop();
            }
            sleep(100);
            requestStop();
          }
        } catch (StepperInterruptException sie) {
        } catch (InterruptedException e) {
          _mergeException = new JXTARuntimeException("Merging Error :" + e.getMessage(), e);
        } catch (Exception e) {
          _mergeException = new JXTARuntimeException("Merging Exception :" + e.getMessage(), e);
        }
      }
    };
    finnishThread.setPriority(Thread.MIN_PRIORITY);
    finnishThread.start();

    // mergeThread merge les fichiers resultats au fur et a mesure qu'il arrivent
    Thread mergeThread = new Thread("SeqGeneratorDistrib-Merge"){
      @Override
      public void run() {
        try {
          ArrayList<ModelTrans> transs = _srcGenerator.getTranss();
          while (_nbrMerged != _states.length) {
            for (int i = 0; i < _requests.length; i++) {
              if (_states[i] != 2) continue;
              if (!_requests[i].isFinished()) continue;

              if (!hasLoadableResult(_requests[i]))
                throw new JXTARuntimeException("Request ["+i+"] don't finnish with success : " + _requests[i].getState());
              { // lecture du fichier de result
                File subFile = _subResults.get(i);
                if (!subFile.canRead())
                  throw new IOException("SeqGen result file not be loadable for distrib resquest " + i);
                ZipInputStream stream = null;
                try {
                  stream = new ZipInputStream(new FileInputStream(subFile));
                  int nbrResult = _srcGenerator.loadResultSet(stream);
                  if (nbrResult != _targets.size()) 
                    MsgStepUser.LOG.warning("NbrResult error : " + nbrResult);
                } finally {
                  if (stream != null) 
                    stream.close();
                }
                if (!Boolean.getBoolean(PROP_SEQGEN_DISTRIB_KEEP_RESULTS))
                  subFile.delete();
              }
              try { // lecture du fichier de log
                File recordFile = _subRecords.get(i);
                if (!recordFile.canRead() || recordFile.length() == 0)
                  continue;

                InfoPeerApp remoteInfoPeerApp = client.getRemoteAppList().getRemoteInfoPeerApp(_requests[i].getServerID());
                String serveur = (remoteInfoPeerApp != null ? remoteInfoPeerApp.getName(): "");

                String nameTrans = "{}";
                if (i<transs.size())
                  nameTrans = transs.get(i).getName();
                _srcGenerator.addRecord(new LogGenComputeRecord(
                    MsgStepUser.MSG_CMD_SEQ_DISTRIB, _requests[i].getShortNumber(), nameTrans, serveur));
                if (_requests[i].getState() == ReqState.FINISHED_SUCCESFULY){
                  //pas utile de pourrir les logs si c'est OK
                } else {
                  List<LogGenRecord> records = LogGenLayoutXML.parseLogFile(recordFile);
                  for (LogGenRecord logGenRecord : records) {
                    switch (logGenRecord.getType()) {
                    case ERR : case EXC :
                      _srcGenerator.addError(logGenRecord);
                      break;
                    }
                  }
                }
              } catch (StepperInterruptInternalException siie) {
              }
              _states[i]++;
              _nbrMerged++;

              requestStop();
            }

            sleep(100);
            requestStop();
          }
        } catch (StepperInterruptException sie) {
        } catch (InterruptedException e) {
          _mergeException = new JXTARuntimeException("Merging Error :" + e.getMessage(), e);
        } catch (IOException ioe) {
          _mergeException = new JXTARuntimeException("Merging IOError :" + ioe.getMessage(), ioe);
        } catch (Exception e) {
          _mergeException = new JXTARuntimeException("Merging Exception :" + e.getMessage(), e);
        }
      }
    };
    mergeThread.setPriority(Thread.MIN_PRIORITY);
    mergeThread.start();

    try {
      int curIdxDistribReq = 0;
      int maxDistribReq = _nbrServer * 10;
      while (_nbrMerged != _states.length) {
        if (_mergeException != null)
          throw _mergeException;

        while (curIdxDistribReq>=0 && (curIdxDistribReq-_nbrFinnished) < maxDistribReq) {
          client.addRequestToStock(_requests[curIdxDistribReq]);
          _states[curIdxDistribReq]++;
          curIdxDistribReq++;
          if (curIdxDistribReq>=_requests.length) 
            curIdxDistribReq = -1;
        }

        // Mise en attente de l'envoie de nouvelles requetes si pause
        requestStopOrResume();
        Thread.sleep(100);
      }
    } catch (StepperInterruptInternalException siie) {
      Request reqTmp = null;
      while ((reqTmp = client.getRequestStock().getNextRequest()) != null) {
        ReqAutomatonClient.fireEvent(reqTmp, ReqAutomatonClient.EvtClient.CANCEL_BY_USER);
      }
      client.remoteStopRequests();
    } catch (StepperInterruptException sie) {
      Request reqTmp = null;
      while ((reqTmp = client.getRequestStock().getNextRequest()) != null) {
        ReqAutomatonClient.fireEvent(reqTmp, ReqAutomatonClient.EvtClient.CANCEL_BY_USER);
      }
      client.remoteStopRequests();
      throw sie;
    } catch (JXTARuntimeException jre) {
      Request reqTmp = null;
      while ((reqTmp = client.getRequestStock().getNextRequest()) != null) {
        ReqAutomatonClient.fireEvent(reqTmp, ReqAutomatonClient.EvtClient.CANCEL_BY_SOFTWARE);
      }
      client.remoteStopRequests();
      throw jre;
    } catch (InterruptedException e) {
    }

    client.removeDiffusedFiles(jarUniqueName);
    client.removeDiffusedFiles(subScriptUniqueName);

    if (_srcGenerator.getNbrError() < _srcGenerator.getMaxError()) {
      //recuperation des erreurs JXTA
      StringBuffer sb = new StringBuffer(128);
      int nbrFatalError = 0;
      for (Request req : _requests) {
        if (hasRealError(req)) {
          if (nbrFatalError == 0) {
            _srcGenerator.addRecord(new LogGenRecord(
                MsgStepUser.MSG_CMD_SEQ_FINDER_LN));
            _srcGenerator.addRecord(new LogGenRecord(
                MsgStepUser.MSG_CMD_COMPUTE_LINE2));
          }
          nbrFatalError++;
          if (nbrFatalError<10) {
            sb.append("\nRequest Error ").append(nbrFatalError);
            sb.append("\n----------------------------\n");
            sb.append(req.getHistoString(client.getPeerJXTA()));
            sb.append("----------------------------\n");
          }
        }
      }
      if (nbrFatalError>0) {
        sb.append("Fatal Error : error(s)=").append(nbrFatalError).append("\n");
        throw new JXTARuntimeException(sb.toString());
      }
    }

    _result = _srcGenerator.getNbrError();
    /* Les erreurs etant globalisee, plus besoin de recuperer le nombre d'erreur de chaque requete
        //recuperation des erreurs BAN normales de chaque serveur
        for (Request req : _requests) {
            _result += req.getNumberOfErr();
        }
     */
    timer = (new Date().getTime()) - timer;

    _srcGenerator.addRecord(new LogGenRecord(
        MsgStepUser.MSG_CMD_SEQ_FINDER_LN));
    _srcGenerator.addRecord(new LogGenRecord(
        MsgStepUser.MSG_CMD_COMPUTE_LINE2));
    _srcGenerator.addRecord(new LogGenRecord( 
        MsgStepUser.MSG_CMD_SEQ_FINDER_ENDAT, new Date()));
    {
      _srcGenerator.getTarget().setTransitionsFlag(true);
      int nbrTransUsed = 0;
      for (ModelTrans tr : _srcGenerator.getTranss()) {
        if (tr._flag) nbrTransUsed++;
      }
      _srcGenerator.addRecord(new LogGenRecord( 
          MsgStepUser.MSG_CMD_SEQ_FINDER_NBRTRANS, nbrTransUsed));
    }
    _srcGenerator.addRecord(new LogGenRecord(
        MsgStepUser.MSG_CMD_COMPUTE_LINE));        
    SeqFinder.displayTime(timer, MsgStepUser.MSG_CMD_SEQ_FINDER_DELAY, _srcGenerator);
    _srcGenerator.addRecord(new LogGenRecord(
        MsgStepUser.MSG_CMD_SEQ_FINDER_LN));
  }

  private static boolean hasRealError(Request req) {
    if (req.getState() == ReqState.FINISHED_SUCCESFULY)
      return false;
    if (req.getState() == ReqState.CANCEL_BY_USER)
      return false;
    if (req.getState() == ReqState.TOO_MANY_ERRORS)
      return !isGoodEngineError(req);

    return true;
  }
  private static boolean hasLoadableResult(Request req){
    if (req.getState() == ReqState.FINISHED_SUCCESFULY)
      return true;
    return isGoodEngineError(req);
  }
  private static final String BAN_ERRHEADER = "Ban computor Exception : ";
  private static boolean isGoodEngineError(Request req) {
    if (req.getState() == ReqState.TOO_MANY_ERRORS){
      int histoSize = req.getHisto().size();
      ReqState prevState = req.getHisto().getIemeState(histoSize-2);
      if (prevState == ReqState.ENGINE_ERROR){
        if (req.getInfo().contains(BAN_ERRHEADER)) 
          return false; 
        if (req.getNumberOfErr() > 0)
          return true;
      }
    }
    return false;
  }

  private Request createBanReq(CECILIAClient client, int idxReq, 
      String subScriptUniqueName, String jarUniqueName,
      String playList, String initList){
    Request req = client.getNewRequest();
    if (Boolean.getBoolean(PROP_SEQGEN_DISTRIB_INTERNAL))
      req.setEngine(Ressources.ENGINEBAN_INTERNAL);
    else
      req.setEngine(Ressources.ENGINEBAN_EXTERNAL);
    StringBuffer userProps = new StringBuffer();
    userProps.append(Ressources.COMMAND).append("=");
    userProps.append(subScriptUniqueName).append(",");
    userProps.append("dban.seqgen.playlist=");
    userProps.append(playList).append(",");
    userProps.append("dban.seqgen.initlist=");
    userProps.append(initList).append(",");
    userProps.append("dban.stepper.jarsrc=");
    userProps.append(jarUniqueName).append(",");
    {	// Traitement des fichiers de resultats
      File subFileResult = _subResults.get(idxReq);
      String subresult = client.makeUniqueFileID(subFileResult.getAbsolutePath(), false);
      req.addResultFile(subresult);
      userProps.append("dban.seqgen.results=");
      userProps.append(subresult).append(",");
    }
    {	// Traitement du fichier de trace
      File subFileRecord = _subRecords.get(idxReq);
      String subrecord = client.makeUniqueFileID(subFileRecord.getAbsolutePath(), false);
      req.addResultFile(subrecord);
      userProps.append("dban.stepper.record=");
      userProps.append(subrecord);
    }
    req.setEngineOptions(userProps.toString());
    return req;
  }

  private void trashTemp(){
    if (!Boolean.getBoolean(PROP_SEQGEN_DISTRIB_KEEP_RESULTS)) {
      for (File subResult : _subResults) {
        subResult.delete();
      }
    }
    if (!Boolean.getBoolean(PROP_SEQGEN_DISTRIB_KEEP_RECORDS)) {
      for (File subRecord : _subRecords) {
        subRecord.delete();
      }
    }
    _subSriptFile.delete();
  }


  //    private List<Integer> createFullTransList() {
  //        ArrayList<Integer> list = new ArrayList<Integer>();
  //        for (int i = 1; i <= _nbTrans; i++) {
  //            list.add(i);
  //        }
  //        return list;
  //    }
  private String createFullTransListString() {
    StringBuffer sb = new StringBuffer();
    for (int i = 1; i <= _nbTrans; i++) {
      if (i != 1)
        sb.append("\t");
      sb.append(String.valueOf(i));
    }
    return sb.toString();
  }

  @Override
  public String getInfo(String key) {
    if (_states == null) return null;
    if (key.equals(ControllableAction.INFO_PROCESS_MSG_HIGH)) {
      StringBuffer msg = new StringBuffer(10);
      msg.append(_nbrMerged);
      msg.append("/");
      msg.append(_states.length);
      return msg.toString();
    }
    if (key.equals(ControllableAction.INFO_PROCESS_P1000))
      return Integer.toString((int) (
          ((double)_nbrMerged)/_states.length*1000) );
    return null;
  }

  @Override
  protected void requestStop() throws StepperInterruptException {
    super.requestStop();
    if (_srcGenerator.getNbrError()>=_srcGenerator.getMaxError())
      throw new StepperInterruptInternalException();
  }

  @Override
  protected void requestStopOrResume() throws StepperInterruptException {
    super.requestStopOrResume();
    if (_srcGenerator.getNbrError()>=_srcGenerator.getMaxError())
      throw new StepperInterruptInternalException();
  }
}
