text: """
  .. See the NOTICE file distributed with this work for additional information
     regarding copyright ownership.
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  """
  from __future__ import print_function
  
  import os
  import shlex
  import subprocess
  import sys
  
  from utils import logger
  
  try:
      if hasattr(sys, '_run_from_cmdl') is True:
          raise ImportError
      from pycompss.api.parameter import FILE_IN, FILE_OUT, IN
      from pycompss.api.task import task
      from pycompss.api.constraint import constraint
      from pycompss.api.api import compss_wait_on, compss_open, compss_delete_file
  except ImportError:
      logger.warn("[Warning] Cannot import \"pycompss\" API packages.")
      logger.warn("          Using mock decorators.")
  
      from utils.dummy_pycompss import FILE_IN, FILE_OUT, IN  # pylint: disable=ungrouped-imports
      from utils.dummy_pycompss import task, constraint  # pylint: disable=ungrouped-imports
      from utils.dummy_pycompss import compss_wait_on, compss_open, compss_delete_file  # pylint: disable=ungrouped-imports
  
  from basic_modules.metadata import Metadata
  from basic_modules.tool import Tool
  from tool.bam_utils import bamUtilsTask
  from tool.common import common
  
  
  # ------------------------------------------------------------------------------
  
  class macs2(Tool):  # pylint: disable=invalid-name
      """
      Tool for peak calling for ChIP-seq data
      """
  
      def __init__(self, configuration=None):
          """
          Init function
          """
          logger.info("MACS2 Peak Caller")
          Tool.__init__(self)
  
          if configuration is None:
              configuration = {}
  
          self.configuration.update(configuration)
  
      @staticmethod
      def _macs2_runner(  # pylint: disable=too-many-locals,too-many-statements,too-many-statements,too-many-arguments
              name, bam_file, bai_file, macs_params,
              narrowpeak, summits_bed, broadpeak, gappedpeak,
              bdg_control_lambda=None, bdg_treat_pileup=None, chromosome=None,
              bam_file_bgd=None, bai_file_bgd=None, bedpe=False):
          """
          Function to run MACS2 for peak calling on aligned sequence files and
          normalised against a provided background set of alignments.
  
          Parameters
          ----------
          name : str
              Name to be used to identify the files
          bam_file : str
              Location of the aligned FASTQ files as a bam file
          bai_file : str
              Location of the bam index file
          narrowpeak : str
              Location of the output narrowpeak file
          summits_bed : str
              Location of the output summits bed file
          broadpeak : str
              Location of the output broadpeak file
          gappedpeak : str
              Location of the output gappedpeak file
          chromosome : str
              If the tool is to be run over a single chromosome the matching
              chromosome name should be specified. If None then the whole bam file
              is analysed
          bam_file_bgd : str
              Location of the aligned FASTQ files as a bam file representing
              background values for the cell
          bai_file_bgd : str
              Location of the background bam index file
  
          Returns
          -------
          narrowPeak : file
              BED6+4 file - ideal for transcription factor binding site
              identification
          summitPeak : file
              BED4+1 file - Contains the peak summit locations for everypeak
          broadPeak : file
              BED6+3 file - ideal for histone binding site identification
          gappedPeak : file
              BED12+3 file - Contains a merged set of the broad and narrow peak
              files
  
          Definitions defined for each of these files have come from the MACS2
          documentation described in the docs at https://github.com/taoliu/MACS
          """
          od_list = bam_file.split("/")
          output_dir = "/".join(od_list[0:-1])
  
          from tool.bam_utils import bamUtils
  
          bam_tmp_file = bam_file.replace(".bam", "." + str(chromosome) + ".bam")
          bam_utils_handle = bamUtils()
          bam_utils_handle.bam_split(bam_file, bai_file, chromosome, bam_tmp_file)
          common_handle = common()
  
          # Test to see if the bam file contains paired end reads
          if bam_utils_handle.bam_paired_reads(bam_file):
              if bedpe:
                  bed_tmp_file = bam_file.replace(".bam", "." + str(chromosome) + ".bed")
                  bam_utils_handle.bam_to_bed(bam_tmp_file, bed_tmp_file)
                  macs_params.append("--format BEDPE")
              else:
                  macs_params.append("--format BAMPE")
  
          command_param = [
              'macs2 callpeak',
              " ".join(macs_params),
              '-t', bam_tmp_file,
              '-n', name
          ]
          if bam_file_bgd is not None:
              bam_bgd_tmp_file = bam_file_bgd.replace(".bam", "." + str(chromosome) + ".bam")
              bam_utils_handle.bam_split(bam_file_bgd, bai_file_bgd, chromosome, bam_bgd_tmp_file)
  
              bgd_command = '-c ' + bam_bgd_tmp_file
              command_param.append(bgd_command)
  
          command_param.append('--outdir ' + output_dir)
          command_line = ' '.join(command_param)
  
          aligned_count = bam_utils_handle.bam_count_reads(bam_tmp_file, aligned=True)
          if int(aligned_count) > 0:
              try:
                  args = shlex.split(command_line)
                  process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                  proc_out, proc_err = process.communicate()
              except (IOError, OSError) as msg:
                  logger.fatal("I/O error({0}): {1}\n{2}".format(
                      msg.errno, msg.strerror, command_line))
                  return False
  
              if process.returncode is not 0:
                  logger.fatal("MACS2 ERROR: " + str(process.returncode))
                  logger.fatal(
                      "MACS2 ERROR: BAM counts: " + str(
                          bam_utils_handle.bam_count_reads(bam_tmp_file, aligned=True)
                      )
                  )
                  logger.fatal("\n\nMACS2 ERROR - out:\n\t" + str(proc_out))
                  logger.fatal("\n\nMACS2 ERROR - err:\n\t" + str(proc_err))
  
              logger.info('Process Results 1:', process)
  
          logger.info('LIST DIR 1:', os.listdir(output_dir))
  
          output_tmp = output_dir + '/{}_{}'
          common_handle.to_output_file(output_tmp.format(name, 'peaks.narrowPeak'), narrowpeak)
          common_handle.to_output_file(output_tmp.format(name, 'peaks.broadPeak'), broadpeak)
          common_handle.to_output_file(output_tmp.format(name, 'peaks.gappedPeak'), gappedpeak)
          common_handle.to_output_file(output_tmp.format(name, 'summits.bed'), summits_bed)
          common_handle.to_output_file(
              output_tmp.format(name, 'control_lambda.bdg'), bdg_control_lambda)
          common_handle.to_output_file(output_tmp.format(name, 'treat_pileup.bdg'), bdg_treat_pileup)
  
          return True
  
      @constraint(ComputingUnits="1")
      @task(
          returns=bool,
          name=IN,
          bam_file=FILE_IN,
          bai_file=FILE_IN,
          bam_file_bgd=FILE_IN,
          bai_file_bgd=FILE_IN,
          macs_params=IN,
          narrowpeak=FILE_OUT,
          summits_bed=FILE_OUT,
          broadpeak=FILE_OUT,
          gappedpeak=FILE_OUT,
          rscript=FILE_OUT,
          bdg_control_lambda=FILE_OUT,
          bdg_treat_pileup=FILE_OUT,
          chromosome=IN,
          bedpe=IN,
          isModifier=False)
      def macs2_peak_calling(  # pylint: disable=no-self-use,too-many-arguments
              self, name, bam_file, bai_file, bam_file_bgd, bai_file_bgd, macs_params,
              narrowpeak, summits_bed, broadpeak, gappedpeak,
              bdg_control_lambda, bdg_treat_pileup, chromosome, bedpe):  # pylint: disable=unused-argument
          """
          Function to run MACS2 for peak calling on aligned sequence files and
          normalised against a provided background set of alignments.
  
          Parameters
          ----------
          name : str
              Name to be used to identify the files
          bam_file : str
              Location of the aligned FASTQ files as a bam file
          bai_file : str
              Location of the bam index file
          bam_file_bgd : str
              Location of the aligned FASTQ files as a bam file representing
              background values for the cell
          bai_file_bgd : str
              Location of the background bam index file
          narrowpeak : str
              Location of the output narrowpeak file
          summits_bed : str
              Location of the output summits bed file
          broadpeak : str
              Location of the output broadpeak file
          gappedpeak : str
              Location of the output gappedpeak file
          chromosome : str
              If the tool is to be run over a single chromosome the matching
              chromosome name should be specified. If None then the whole bam file
              is analysed
  
          Returns
          -------
          narrowPeak : file
              BED6+4 file - ideal for transcription factor binding site
              identification
          summitPeak : file
              BED4+1 file - Contains the peak summit locations for everypeak
          broadPeak : file
              BED6+3 file - ideal for histone binding site identification
          gappedPeak : file
              BED12+3 file - Contains a merged set of the broad and narrow peak
              files
  
          Definitions defined for each of these files have come from the MACS2
          documentation described in the docs at https://github.com/taoliu/MACS
          """
  
          self._macs2_runner(
              name, bam_file, bai_file, macs_params,
              narrowpeak, summits_bed, broadpeak, gappedpeak,
              bdg_control_lambda=bdg_control_lambda, bdg_treat_pileup=bdg_treat_pileup,
              chromosome=chromosome, bam_file_bgd=bam_file_bgd,
              bai_file_bgd=bai_file_bgd, bedpe=bedpe)
  
          return True
  
      @constraint(ComputingUnits="1")
      @task(
          returns=bool,
          name=IN,
          bam_file=FILE_IN,
          bai_file=FILE_IN,
          macs_params=IN,
          narrowpeak=FILE_OUT,
          summits_bed=FILE_OUT,
          broadpeak=FILE_OUT,
          gappedpeak=FILE_OUT,
          bdg_control_lambda=FILE_OUT,
          bdg_treat_pileup=FILE_OUT,
          chromosome=IN,
          bedpe=IN,
          isModifier=False)
      def macs2_peak_calling_nobgd(  # pylint: disable=too-many-arguments,no-self-use,too-many-branches
              self, name, bam_file, bai_file, macs_params,
              narrowpeak, summits_bed, broadpeak, gappedpeak,
              bdg_control_lambda, bdg_treat_pileup, chromosome, bedpe):  # pylint: disable=unused-argument
          """
          Function to run MACS2 for peak calling on aligned sequence files without
          a background dataset for normalisation.
  
          Parameters
          ----------
          name : str
              Name to be used to identify the files
          bam_file : str
              Location of the aligned FASTQ files as a bam file
          bai_file : str
              Location of the bam index file
          narrowpeak : str
              Location of the output narrowpeak file
          summits_bed : str
              Location of the output summits bed file
          broadpeak : str
              Location of the output broadpeak file
          gappedpeak : str
              Location of the output gappedpeak file
          chromosome : str
              If the tool is to be run over a single chromosome the matching
              chromosome name should be specified. If None then the whole bam file
              is analysed
  
          Returns
          -------
          narrowPeak : file
              BED6+4 file - ideal for transcription factor binding site
              identification
          summitPeak : file
              BED4+1 file - Contains the peak summit locations for everypeak
          broadPeak : file
              BED6+3 file - ideal for histone binding site identification
          gappedPeak : file
              BED12+3 file - Contains a merged set of the broad and narrow peak
              files
  
          Definitions defined for each of these files have come from the MACS2
          documentation described in the docs at https://github.com/taoliu/MACS
          """
          self._macs2_runner(
              name, bam_file, bai_file, macs_params,
              narrowpeak, summits_bed, broadpeak, gappedpeak,
              bdg_control_lambda=bdg_control_lambda, bdg_treat_pileup=bdg_treat_pileup,
              chromosome=chromosome, bedpe=bedpe)
  
          return True
  
      @staticmethod
      def get_macs2_params(params):
          """
          Function to handle to extraction of commandline parameters and formatting
          them for use in the aligner for BWA ALN
  
          Parameters
          ----------
          params : dict
  
          Returns
          -------
          list
          """
          command_params = []
  
          command_parameters = {
              "macs_format_param": ["--format", True],
              "macs_gsize_param": ["--gsize", True],
              "macs_tsize_param": ["--tsize", True],
              "macs_bw_param": ["--bw", True],
              "macs_qvalue_param": ["--qvalue", True],
              "macs_pvalue_param": ["--pvalue", True],
              "macs_mfold_param": ["--mfold", True],
              "macs_nolambda_param": ["--nolambda", False],
              "macs_slocal_param": ["--slocal", True],
              "macs_llocal_param": ["--llocal", True],
              "macs_fix-bimodal_param": ["--fix-bimodal", False],
              "macs_nomodel_param": ["--nomodel", False],
              "macs_extsize_param": ["--extsize", True],
              "macs_shift_param": ["--shift", True],
              "macs_keep-dup_param": ["--keep-dup", True],
              "macs_broad_param": ["--broad", False],
              "macs_broad-cutoff_param": ["--broad-cutoff", True],
              "macs_to-large_param": ["--to-large", False],
              "macs_down-sample_param": ["--down-sample", False],
              "macs_bdg_param": ["--bdg", False],
              "macs_call-summits_param": ["--call-summits", True],
          }
  
          for param in params:
              if param in command_parameters:
                  if command_parameters[param][1] and params[param] != "":
                      command_params = command_params + [
                          command_parameters[param][0], params[param]
                      ]
                  else:
                      if command_parameters[param][0] and params[param] is not False:
                          command_params.append(command_parameters[param][0])
  
          return command_params
  
      def run(self, input_files, input_metadata, output_files):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
          """
          The main function to run MACS 2 for peak calling over a given BAM file
          and matching background BAM file.
  
          Parameters
          ----------
          input_files : dict
              List of input bam file locations where 0 is the bam data file and 1
              is the matching background bam file
          metadata : dict
  
  
          Returns
          -------
          output_files : dict
              List of locations for the output files.
          output_metadata : dict
              List of matching metadata dict objects
  
          """
          root_name = os.path.split(input_files['bam'])
          name = root_name[1].replace('.bam', '')
  
          # input and output share most metadata
          output_bed_types = {
              'narrow_peak': "bed4+1",
              'summits': "bed6+4",
              'broad_peak': "bed6+3",
              'gapped_peak': "bed12+3"
          }
  
          command_params = self.get_macs2_params(self.configuration)
  
          bam_utils_handle = bamUtilsTask()
          bam_utils_handle.bam_index(
              input_files['bam'],
              input_files['bam'] + '.bai'
          )
          if 'bam_bg' in input_files:
              bam_utils_handle.bam_index(
                  input_files['bam_bg'],
                  input_files['bam_bg'] + '.bai'
              )
  
          chr_list = bam_utils_handle.bam_list_chromosomes(input_files['bam'])
          chr_list = compss_wait_on(chr_list)
  
          logger.info("MACS2 COMMAND PARAMS: " + ", ".join(command_params))
  
          bedpe_param = False
          if 'macs2_bedpe' in self.configuration:
              bedpe_param = True
  
          for chromosome in chr_list:
              if 'bam_bg' in input_files:
                  result = self.macs2_peak_calling(
                      name,
                      str(input_files['bam']), str(input_files['bam']) + '.bai',
                      str(input_files['bam_bg']), str(input_files['bam_bg']) + '.bai',
                      command_params,
                      str(output_files['narrow_peak']) + "." + str(chromosome),
                      str(output_files['summits']) + "." + str(chromosome),
                      str(output_files['broad_peak']) + "." + str(chromosome),
                      str(output_files['gapped_peak']) + "." + str(chromosome),
                      os.path.join(root_name[0], name + "_control_lambda.bdg"),
                      os.path.join(root_name[0], name + "_treat_pvalue.bdg"),
                      chromosome,
                      bedpe_param)
              else:
                  result = self.macs2_peak_calling_nobgd(
                      name,
                      str(input_files['bam']), str(input_files['bam']) + '.bai',
                      command_params,
                      str(output_files['narrow_peak']) + "." + str(chromosome),
                      str(output_files['summits']) + "." + str(chromosome),
                      str(output_files['broad_peak']) + "." + str(chromosome),
                      str(output_files['gapped_peak']) + "." + str(chromosome),
                      os.path.join(root_name[0], name + "_control_lambda.bdg"),
                      os.path.join(root_name[0], name + "_treat_pvalue.bdg"),
                      chromosome,
                      bedpe_param)
  
              if result is False:
                  logger.fatal("MACS2: Something went wrong with the peak calling")
  
          # Merge the results files into single files.
          with open(output_files['narrow_peak'], 'wb') as file_np_handle:
              with open(output_files['summits'], 'wb') as file_s_handle:
                  with open(output_files['broad_peak'], 'wb') as file_bp_handle:
                      with open(output_files['gapped_peak'], 'wb') as file_gp_handle:
                          for chromosome in chr_list:
                              np_file_chr = "{}.{}".format(output_files['narrow_peak'], chromosome)
                              s_file_chr = "{}.{}".format(output_files['summits'], chromosome)
                              bp_file_chr = "{}.{}".format(output_files['broad_peak'], chromosome)
                              gp_file_chr = "{}.{}".format(output_files['gapped_peak'], chromosome)
                              if hasattr(sys, '_run_from_cmdl') is True:
                                  with open(np_file_chr, 'rb') as file_in_handle:
                                      file_np_handle.write(file_in_handle.read())
                                  with open(s_file_chr, 'rb') as file_in_handle:
                                      file_s_handle.write(file_in_handle.read())
                                  with open(bp_file_chr, 'rb') as file_in_handle:
                                      file_bp_handle.write(file_in_handle.read())
                                  with open(gp_file_chr, 'rb') as file_in_handle:
                                      file_gp_handle.write(file_in_handle.read())
                              else:
                                  with compss_open(np_file_chr, 'rb') as file_in_handle:
                                      file_np_handle.write(file_in_handle.read())
                                  with compss_open(s_file_chr, 'rb') as file_in_handle:
                                      file_s_handle.write(file_in_handle.read())
                                  with compss_open(bp_file_chr, 'rb') as file_in_handle:
                                      file_bp_handle.write(file_in_handle.read())
                                  with compss_open(gp_file_chr, 'rb') as file_in_handle:
                                      file_gp_handle.write(file_in_handle.read())
                                  compss_delete_file(np_file_chr)
                                  compss_delete_file(s_file_chr)
                                  compss_delete_file(bp_file_chr)
                                  compss_delete_file(gp_file_chr)
  
          output_files_created = {}
          output_metadata = {}
          for result_file in output_files:
              if (
                      os.path.isfile(output_files[result_file]) is True
                      and os.path.getsize(output_files[result_file]) > 0
              ):
                  output_files_created[result_file] = output_files[result_file]
  
                  sources = [input_metadata["bam"].file_path]
                  if 'bam_bg' in input_files:
                      sources.append(input_metadata["bam_bg"].file_path)
  
                  output_metadata[result_file] = Metadata(
                      data_type="data_chip_seq",
                      file_type="BED",
                      file_path=output_files[result_file],
                      sources=sources,
                      taxon_id=input_metadata["bam"].taxon_id,
                      meta_data={
                          "assembly": input_metadata["bam"].meta_data["assembly"],
                          "tool": "macs2",
                          "bed_type": output_bed_types[result_file],
                          "parameters": command_params
                      }
                  )
              else:
                  try:
                      os.remove(output_files[result_file])
                  except (OSError, IOError) as error:
                      logger.warn("MACS2: I/O error({0}): {1}\nMissing file: {2}".format(
                          error.errno, error.strerror, output_files[result_file]))
  
          bdg_files = {
              "control_lambda": os.path.join(root_name[0], name + "_control_lambda.bdg"),
              "treat_pvalue": os.path.join(root_name[0], name + "_treat_pvalue.bdg")
          }
          for result_file in bdg_files:
              if (
                      os.path.isfile(bdg_files[result_file]) is True
                      and os.path.getsize(bdg_files[result_file]) > 0
              ):
                  output_files_created[result_file] = bdg_files[result_file]
  
                  sources = [input_metadata["bam"].file_path]
                  if 'bam_bg' in input_files:
                      sources.append(input_metadata["bam_bg"].file_path)
  
                  output_metadata[result_file] = Metadata(
                      data_type="data_chip_seq",
                      file_type="BEDGRAPH",
                      file_path=bdg_files[result_file],
                      sources=sources,
                      taxon_id=input_metadata["bam"].taxon_id,
                      meta_data={
                          "assembly": input_metadata["bam"].meta_data["assembly"],
                          "tool": "macs2",
                          "parameters": command_params
                      }
                  )
              else:
                  try:
                      os.remove(bdg_files[result_file])
                  except (OSError, IOError) as error:
                      logger.warn("MACS2: I/O error({0}): {1}\nMissing file: {2}".format(
                          error.errno, error.strerror, bdg_files[result_file]))
  
          logger.info('MACS2: GENERATED FILES:', output_files)
  
          return (output_files_created, output_metadata)
  
  # ------------------------------------------------------------------------------
  