/**
   * Uncompress a file to a temporary file.
   *
   * @param context Step context
   * @param file file to uncompress
   * @return the path to the uncompressed file
   * @throws IOException if an error occurs while creating the uncompressed file
   */
  private File uncompressFile(final TaskContext context, final DataFile file) throws IOException {

    checkNotNull(file, "file argument cannot be null");

    final DataFile realFile;
    final DataProtocol protocol = file.getProtocol();

    // Get the underlying file if the file protocol is a storage protocol
    if (protocol instanceof StorageDataProtocol) {

      realFile = ((StorageDataProtocol) protocol).getUnderLyingData(file);
    } else {
      realFile = file;
    }

    final File outputFile =
        Files.createTempFile(
                context.getLocalTempDirectory().toPath(),
                MODULE_NAME + "-",
                realFile.getExtension())
            .toFile();

    context.getLogger().fine("Uncompress/copy " + realFile + " to " + outputFile);

    DataFiles.copy(realFile, new DataFile(outputFile));

    return outputFile;
  }
  /**
   * Set the "gtf.feature.exon" and "gtf.tag.exon.parent.transcript" parameter from the expression
   * step parameters.
   *
   * @param context the context of the task
   * @throws EoulsanException if more than one expression step exists
   */
  private void searchExpressionStepParameters(final TaskContext context) throws EoulsanException {

    int count = 0;

    for (Step step : context.getWorkflow().getSteps()) {

      if (AbstractExpressionModule.MODULE_NAME.equals(step.getModuleName())) {

        for (Parameter p : step.getParameters()) {

          switch (p.getName()) {
            case AbstractExpressionModule.OLD_GENOMIC_TYPE_PARAMETER_NAME:
            case AbstractExpressionModule.GENOMIC_TYPE_PARAMETER_NAME:
              gtfFeatureExon = p.getStringValue();
              break;

            case AbstractExpressionModule.OLD_ATTRIBUTE_ID_PARAMETER_NAME:
            case AbstractExpressionModule.ATTRIBUTE_ID_PARAMETER_NAME:
              gtfTagExonParentTranscript = p.getStringValue();
              break;

            default:
              break;
          }
        }
        count++;
      }
    }

    if (count == 0) {
      throw new EoulsanException("No expression step found in the workflow");
    }

    if (count > 1) {
      throw new EoulsanException("Found more than one expression step in the workflow");
    }
  }
  @Override
  public TaskResult execute(final TaskContext context, final TaskStatus status) {

    try {

      final StringBuilder additionalArguments = new StringBuilder();
      final Map<String, String> additionalDescription = new HashMap<>();
      final List<File> temporaryFiles = new ArrayList<>();

      // Search expression parameter is needed
      if (this.useExpressionStepParameters) {
        searchExpressionStepParameters(context);
      }

      if (this.gtfFile) {

        // Get the annotation data
        final Data annotationData =
            context.getInputData(this.gtfFormat ? ANNOTATION_GTF : ANNOTATION_GFF);

        // Get the annotation DataFile
        final DataFile gffFile = annotationData.getDataFile();
        final File gffFilePath = uncompressFileIfNecessary(context, temporaryFiles, gffFile);

        additionalArguments.append("--sjdbGTFfile");
        additionalArguments.append(' ');
        additionalArguments.append(gffFilePath.getAbsolutePath());
        additionalArguments.append(' ');
        additionalDescription.put("sjdbGTFfile", computeMD5SumFile(gffFilePath));
      }

      if (this.overhang != null) {

        additionalArguments.append("--sjdbOverhang");
        additionalArguments.append(' ');
        additionalArguments.append(this.overhang.toString());
        additionalArguments.append(' ');
        additionalDescription.put("sjdbOverhang", this.overhang.toString());
      }

      if (this.gtfTagExonParentTranscript != null) {

        additionalArguments.append("--sjdbGTFtagExonParentTranscript");
        additionalArguments.append(' ');
        additionalArguments.append(this.gtfTagExonParentTranscript);
        additionalArguments.append(' ');
        additionalDescription.put(
            "sjdbGTFtagExonParentTranscript", this.gtfTagExonParentTranscript);
      }

      if (this.gtfFeatureExon != null) {

        additionalArguments.append("--sjdbGTFfeatureExon");
        additionalArguments.append(' ');
        additionalArguments.append(this.gtfFeatureExon);
        additionalArguments.append(' ');
        additionalDescription.put("sjdbGTFfeatureExon", this.gtfFeatureExon);
      }

      if (this.chrStartEndFilename != null) {

        DataFile chrStartEndFile = new DataFile(this.chrStartEndFilename);

        if (!chrStartEndFile.exists()) {
          throw new IOException("Unable to read chromosome startend file: " + chrStartEndFile);
        }

        final File chrStartEndFilePath =
            uncompressFileIfNecessary(context, temporaryFiles, chrStartEndFile);

        additionalArguments.append("--sjdbFileChrStartEnd");
        additionalArguments.append(' ');
        additionalArguments.append(chrStartEndFilePath.getAbsolutePath());
        additionalArguments.append(' ');
        additionalDescription.put("sjdbFileChrStartEnd", computeMD5SumFile(chrStartEndFilePath));
      }

      if (this.genomeSAindexNbases != null) {

        additionalArguments.append("--genomeSAindexNbases");
        additionalArguments.append(' ');
        additionalArguments.append(this.genomeSAindexNbases.toString());
        additionalArguments.append(' ');
        additionalDescription.put("genomeSAindexNbases", this.genomeSAindexNbases.toString());
      }

      if (this.genomeChrBinNbits != null) {
        additionalArguments.append("--genomeChrBinNbits");
        additionalArguments.append(' ');
        additionalArguments.append(this.genomeChrBinNbits.toString());
        additionalArguments.append(' ');
        additionalDescription.put("genomeChrBinNbits", this.genomeChrBinNbits.toString());
      }

      status.setProgressMessage(this.mapper.getMapperName() + " index creation");

      // Create the index
      GenomeMapperIndexGeneratorModule.execute(
          this.mapper,
          context,
          additionalArguments.toString(),
          additionalDescription,
          Common.getThreadsNumber(this.localThreads, this.maxLocalThreads));

      // Remove temporary files
      for (File temporaryFile : temporaryFiles) {

        if (!temporaryFile.delete()) {
          context.getLogger().warning("Cannot remove temporary file: " + temporaryFile);
        }
      }

    } catch (IOException | EoulsanException e) {

      return status.createTaskResult(e);
    }

    return status.createTaskResult();
  }