#!/usr/bin/env python
"""Test suite for :py:mod:`plastid.bin.crossmap`"""
import tempfile
import os
import subprocess

from nose.plugins.attrib import attr
from pkg_resources import resource_filename, cleanup_resources
from plastid.test.functional.base import execute_helper
from plastid.test.ref_files import (
    RPATH,
    REF_FILES,
    COUNT_OPTIONS,
    ANNOTATION_OPTIONS,
    MASK_OPTIONS,
)

from plastid.bin.crossmap import main
from plastid.util.services.decorators import catch_stderr

#===============================================================================
# INDEX: global constants used by tests
#===============================================================================

test_info = {
    "test_method": catch_stderr()(main),
    "module_name": "plastid.bin.crossmap",
    "ref_file_path": resource_filename("plastid", "test/data/command_line"),
    "temp_file_path": tempfile.mkdtemp(prefix="crossmap"),
}
"""Dictionary of global variables used in this module"""

# path to bowtie index, which will be built
_ebwt = os.path.join(test_info["temp_file_path"], "yeast_ebwt")

# equality options used by tests
_eq_ops = "--no_header --sort_keys 3"

# command-line string used to execute crossmap script
# substitute: ebwt, outbase, sequence file, offset, mismatches
_exec_str = "%s %s --sequence_file %s --offset %s --mismatches %s -k 26 -p %s --save_kmers"

# basename for output files for a test run
# substitute: offset, mismatches
_outbase_str = "yeast_crossmap_o%s"

# kmer file basename, generated by first test and used by remaining tests
_kmers = os.path.join(test_info["temp_file_path"], ((_outbase_str % 0) + "_26_0"))

#===============================================================================
# INDEX: definition of tests
#        The first we specify explicitly,
#        the remaining we generate programmatically
#
# There are four tests total:
# 
#    1. 0 0 mismatches, 0 offset, k=26, single process, and save k-mers
#    2. 0 mismatches, 12 offset, k=26, single process, use pre-existing k-mers
#    3. 2 mismatches, 12 offset, k=26, single process, use pre-existing k-mers
#    4. 0 mismatches, 12 offset, k=26, four processes, use pre-existing k-mers
#===============================================================================

# first test generates kmer file
offset = mismatches = 0
outbase = _outbase_str % offset
crossmap_tests = [
    (
        _exec_str % (
            _ebwt, os.path.join(test_info["temp_file_path"], outbase), REF_FILES["yeast_fasta"],
            offset, mismatches, 1
        ), [REF_FILES[outbase + "_26_%s" % mismatches]],
        [os.path.join(test_info["temp_file_path"], outbase + ("_26_%s_crossmap.bed" % mismatches))],
        [_eq_ops]
    ),
]
"""Tests to execute, as 4-tuples of:

    - command-line arguments to pass to :py:mod:`plastid.bin.crossmap`
    - a list of reference files corresponding to reference output
    - a list of output files made by running :py:mod:`plastid.bin.crossmap`
    - a list of equality options under which comparisons between the output
      and reference files should be evaluated
"""

# mismatch, offset pairs for remaining tests
_tuples = [(0, 12), (2, 12)]

# generate remaining tests programmatically using kmer file from first run
processes = 1
for mismatches, offset in _tuples:
    outbase = _outbase_str % offset
    test_str = (_exec_str + " --have_kmers") % (
        os.path.join(test_info["temp_file_path"], _ebwt),
        os.path.join(test_info["temp_file_path"], outbase),
        os.path.join(test_info["temp_file_path"], _kmers), offset, mismatches, processes
    )
    ref_file = REF_FILES[outbase + "_26_%s" % mismatches]
    test_file = os.path.join(
        test_info["temp_file_path"], outbase + ("_26_%s_crossmap.bed" % mismatches)
    )
    crossmap_tests.append([test_str, [ref_file], [test_file], [_eq_ops]])

# multiprocess test
mismatches = 0
processes = 4
test_str = (_exec_str + " --have_kmers") % (
    os.path.join(test_info["temp_file_path"], _ebwt),
    os.path.join(test_info["temp_file_path"], outbase + "_p4"),
    os.path.join(test_info["temp_file_path"], _kmers), offset, mismatches, processes
)
ref_file = REF_FILES[outbase + "_26_%s" % mismatches]
test_file = os.path.join(
    test_info["temp_file_path"], outbase + "_p4" + ("_26_%s_crossmap.bed" % mismatches)
)
crossmap_tests.append([test_str, [ref_file], [test_file], [_eq_ops]])

#===============================================================================
# INDEX: Helper functions to run tests
#===============================================================================


@attr(test="functional")
@attr(speed="slow")
def do_test():
    """Perform functional test for plastid.bin.crossmap"""
    for x in execute_helper(test_info, crossmap_tests):
        yield x


def setUpModule():
    """Create bowtie index before running tests"""
    args = ["bowtie-build", REF_FILES["yeast_fasta"], _ebwt]
    with open(os.devnull, "a") as null:
        try:
            subprocess.check_call(args, stdout=null, stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as e:
            print("Could not build bowtie index for crossmap test.")
            raise e
