from __future__ import print_function
from __future__ import absolute_import
from __future__ import unicode_literals
#
# This file is part of khmer, https://github.com/dib-lab/khmer/, and is
# Copyright (C) Michigan State University, 2009-2015. It is licensed under
# the three-clause BSD license; see LICENSE.
# Contact: khmer-project@idyll.org
# pylint: disable=missing-docstring,invalid-name,unused-variable

import os
import shutil
import threading
import io

import screed
import khmer

from . import khmer_tst_utils as utils
from nose.plugins.attrib import attr
from .test_scripts import _make_counting


def test_normalize_by_median_indent():
    infile = utils.get_test_data('paired-mixed.fa.pe')
    hashfile = utils.get_test_data('normC20k20.ct')
    outfile = utils.get_temp_filename('paired-mixed.fa.pe.keep')
    script = 'normalize-by-median.py'
    args = ['--loadgraph', hashfile, '-o', outfile, infile]
    (status, out, err) = utils.runscript(script, args)
    assert status == 0, (out, err)
    assert os.path.exists(outfile)


def test_normalize_by_median_loadgraph_with_args():
    infile = utils.get_test_data("test-abund-read-2.fa")
    tablefile = utils.get_temp_filename("table")
    in_dir = os.path.dirname(tablefile)

    script = "load-into-counting.py"
    args = [tablefile, infile]
    (status, out, err) = utils.runscript(script, args)

    script = "normalize-by-median.py"
    args = ["--ksize", "7", "--loadgraph", tablefile, infile]
    (status, out, err) = utils.runscript(script, args, in_dir)
    assert 'WARNING: You are loading a saved k-mer countgraph from' in err, err


def test_normalize_by_median_empty_file():
    infile = utils.get_temp_filename('empty')
    shutil.copyfile(utils.get_test_data('empty-file'), infile)
    script = 'normalize-by-median.py'
    in_dir = os.path.dirname(infile)

    args = [infile]
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert 'WARNING:' in err, err
    assert 'is empty' in err, err
    assert 'SKIPPED' in err, err


def test_normalize_by_median():
    CUTOFF = '1'

    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)

    shutil.copyfile(utils.get_test_data('test-abund-read-2.fa'), infile)

    script = 'normalize-by-median.py'
    args = ['-C', CUTOFF, '-k', '17', infile]
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert 'Total number of unique k-mers: 98' in err, err

    outfile = infile + '.keep'
    assert os.path.exists(outfile), outfile

    seqs = [r.sequence for r in screed.open(outfile)]
    assert len(seqs) == 1, seqs
    assert seqs[0].startswith('GGTTGACGGGGCTCAGGGGG'), seqs
    assert "I/O Errors" not in err


def test_normalize_by_median_quiet():
    CUTOFF = '1'

    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)

    shutil.copyfile(utils.get_test_data('test-abund-read-2.fa'), infile)

    script = 'normalize-by-median.py'
    args = ['-C', CUTOFF, '-k', '17', '--quiet', '-M', '2e6', infile]
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert len(out) == 0, out
    assert len(err) == 0, err

    outfile = infile + '.keep'
    assert os.path.exists(outfile), outfile

    seqs = [r.sequence for r in screed.open(outfile)]
    assert len(seqs) == 1, seqs
    assert seqs[0].startswith('GGTTGACGGGGCTCAGGGGG'), seqs
    assert "I/O Errors" not in err


def test_normalize_by_median_unpaired_final_read():
    CUTOFF = '1'

    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)

    shutil.copyfile(utils.get_test_data('single-read.fq'), infile)

    script = 'normalize-by-median.py'
    args = ['-C', CUTOFF, '-k', '17', '-p', infile]
    (status, out, err) = utils.runscript(script, args, in_dir, fail_ok=True)
    assert status != 0
    assert "ERROR: Unpaired reads when require_paired" in err, err


def test_normalize_by_median_sanity_check_0():
    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)

    shutil.copyfile(utils.get_test_data('single-read.fq'), infile)

    script = 'normalize-by-median.py'
    args = ['-U', '1024', '--max-mem', '60', infile]
    (status, out, err) = utils.runscript(script, args, in_dir, fail_ok=True)
    assert status != 0, status
    assert "recommended false positive ceiling of 0.1!" in err, err


def test_normalize_by_median_sanity_check_1():
    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)

    shutil.copyfile(utils.get_test_data('test-filter-abund-Ns.fq'), infile)

    script = 'normalize-by-median.py'
    args = ['-U', '83', '--max-tablesize', '17', infile]
    (status, out, err) = utils.runscript(script, args, in_dir, fail_ok=True)
    assert status != 0
    assert "Warning: The given tablesize is too small!" in err, err


def test_normalize_by_median_sanity_check_2():
    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)

    shutil.copyfile(utils.get_test_data('test-filter-abund-Ns.fq'), infile)

    script = 'normalize-by-median.py'
    args = ['-U', '83', infile]
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert "*** INFO: set memory ceiling automatically." in err, err
    assert "*** Ceiling is: 1e+06 bytes" in err, err


def test_normalize_by_median_sanity_check_3():
    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)
    tablefile = utils.get_temp_filename('table', in_dir)

    shutil.copyfile(utils.get_test_data('test-filter-abund-Ns.fq'), infile)

    script = 'normalize-by-median.py'
    args = ['-s', tablefile, '-U', '83', '--fp-rate', '0.7', infile]
    (status, out, err) = utils.runscript(script, args, in_dir)
    assert "Overriding default fp 0.1 with new fp: 0.7" in err, err

    args = ['--loadgraph', tablefile, '-U', '83', infile]
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert "WARNING: You have asked that the graph size be auto" in err, err
    assert "NOT be set automatically" in err, err
    assert "loading an existing graph" in err, err


def test_normalize_by_median_unforced_badfile():
    CUTOFF = '1'

    infile = utils.get_temp_filename("potatoes")
    outfile = infile + '.keep'
    in_dir = os.path.dirname(infile)
    script = 'normalize-by-median.py'
    args = ['-C', CUTOFF, '-k', '17', infile]
    (status, out, err) = utils.runscript(script, args, in_dir, fail_ok=True)
    assert status != 0
    assert "ERROR: [Errno 2] No such file or directory:" in err, err

    if os.path.exists(outfile):
        assert False, '.keep file should have been removed: '


def test_normalize_by_median_contradictory_args():
    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)
    outfile = utils.get_temp_filename('report.out')

    shutil.copyfile(utils.get_test_data('test-large.fa'), infile)

    script = 'normalize-by-median.py'
    args = ['-C', '1', '-k', '17', '--force-single', '-p', '-R',
            outfile, infile]
    (status, out, err) = utils.runscript(script, args, in_dir, fail_ok=True)
    assert status != 0
    assert "cannot both be set" in err, err


def test_normalize_by_median_stdout_3():
    CUTOFF = '1'

    infile = utils.get_temp_filename('test.fa')
    in_dir = os.path.dirname(infile)

    shutil.copyfile(utils.get_test_data('test-abund-read-2.fa'), infile)

    script = 'normalize-by-median.py'
    args = ['-C', CUTOFF, '-k', '17', infile, '--out', '-']
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert 'Total number of unique k-mers: 98' in err, err
    assert 'in block device' in err, err
    assert "I/O Errors" not in err


@attr('known_failing')
def test_normalize_by_median_known_good():
    CUTOFF = '2'

    infile = utils.get_temp_filename('test.fa.gz')
    in_dir = os.path.dirname(infile)
    shutil.copyfile(utils.get_test_data('100k-filtered.fa.gz'), infile)

    script = 'normalize-by-median.py'
    args = ['-C', CUTOFF, '-k', '20', '-x', '4e6', infile]
    (status, out, err) = utils.runscript(script, args, in_dir)

    outfile = infile + '.keep'
    assert os.path.exists(outfile), outfile
    iter_known = screed.open(utils.get_test_data('100k-filtered.fa.keep.gz'))
    iter_out = screed.open(outfile)
    try:
        for rknown, rout in zip(iter_known, iter_out):
            assert rknown.name == rout.name
    except Exception as e:
        print(e)
        assert False


def test_normalize_by_median_report_fp():
    # this tests basic reporting of diginorm stats => report.out, including
    # a test of aggregate stats for two input files.

    infile = utils.get_temp_filename('test.fa')
    shutil.copyfile(utils.get_test_data('test-abund-read-2.fa'), infile)
    infile2 = utils.get_temp_filename('test2.fa')
    shutil.copyfile(utils.get_test_data('test-abund-read-2.fa'), infile2)

    in_dir = os.path.dirname(infile)
    outfile = utils.get_temp_filename('report.out')

    script = 'normalize-by-median.py'
    args = ['-C', '1', '-k', '17', '-R', outfile, infile, infile2]
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert os.path.exists(outfile)
    report = open(outfile, 'r')
    line = report.readline().strip()
    assert line == 'total,kept,f_kept', line
    line = report.readline().strip()
    assert line == '1001,1,0.000999', line
    line = report.readline().strip()
    assert line == '2002,1,0.0004995', line


def test_normalize_by_median_report_fp_hifreq():
    # this tests high-frequency reporting of diginorm stats for a single
    # file => report.out.

    infile = utils.get_temp_filename('test.fa')
    shutil.copyfile(utils.get_test_data('test-abund-read-2.fa'), infile)

    in_dir = os.path.dirname(infile)
    outfile = utils.get_temp_filename('report.out')

    script = 'normalize-by-median.py'
    args = ['-C', '1', '-k', '17', '-R', outfile, infile,
            '--report-frequency', '100']
    (status, out, err) = utils.runscript(script, args, in_dir)

    assert os.path.exists(outfile)
    report = open(outfile, 'r')
    line = report.readline().strip()
    assert line == 'total,kept,f_kept', line
    line = report.readline().strip()
    assert line == '100,1,0.01', line
    line = report.readline().strip()