# Path: lib/log.py
# class Logger:
#     """Common logging functions."""
# 
#     def __init__(self, log_file, log_level):
#         self.log_file = log_file
#         self.log_level = LEVEL[log_level]
#         self.file_handle = open(log_file, 'a') if log_file else None
# 
#     def __del__(self):
#         if self.file_handle:
#             self.file_handle.close()
# 
#     def header(self):
#         """Log header information."""
#         self.info('#' * 80)
#         self.info('aTRAM version: {}'.format(db.ATRAM_VERSION))
#         self.info('Python version: {}'.format(' '.join(sys.version.split())))
#         self.info(' '.join(sys.argv[:]))
# 
#     def subcommand(self, cmd, temp_dir, timeout=None):
#         """
#         Call a subprocess and log the output.
# 
#         Note: stdout=PIPE is blocking and large logs cause a hang.
#         So we don't use it.
#         """
#         self.debug(cmd)
# 
#         error = None
# 
#         with tempfile.NamedTemporaryFile(mode='w', dir=temp_dir) as log_output:
#             with subprocess.Popen(
#                     cmd, shell=True, stdout=log_output, stderr=log_output) as proc:
#                 try:
#                     proc.communicate(timeout=timeout)
# 
#                 # Catch any error and kill all child processes
#                 except Exception as err:  # pylint: disable=broad-except
#                     error = err
#                     self.error('Exception: {}'.format(err))
# 
#                 # On success or failure log what we can
#                 finally:
#                     wait = 5
# 
#                     killed, alive = util.kill_proc_tree(proc.pid, timeout=wait)
# 
#                     if killed:
#                         self.error('Killed {} processes with SIGTERM.'.format(killed))
# 
#                     if alive > 0:
#                         self.error('{} alive processes after SIGTERM'.format(alive))
# 
#                         killed, alive = util.kill_proc_tree(
#                             proc.pid, timeout=wait, sig=signal.SIGKILL)
#                         self.error('Killed {} processes with SIGKILL.'.format(killed))
#                         self.error('{} alive processes after SIGKILL'.format(alive))
# 
#                     with open(log_output.name) as log_input:
#                         for line in log_input:
#                             line = line.strip()
#                             if line:
#                                 self.debug(line)
#                     if error:
#                         raise error
# 
#     def _output(self, msg, level):
#         timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
#         msg = '({:8d}) {} {:<5}: {}'.format(os.getpid(), timestamp, level, msg)
#         print(msg)
#         if self.file_handle:
#             self.file_handle.write(msg)
#             self.file_handle.write('\n')
#             self.file_handle.flush()
# 
#     def debug(self, msg):
#         """Log an info message."""
#         if self.log_level <= DEBUG:
#             self._output(msg, 'DEBUG')
# 
#     def info(self, msg):
#         """Log an info message."""
#         if self.log_level <= INFO:
#             self._output(msg, 'INFO')
# 
#     def error(self, msg):
#         """Log an error message."""
#         if self.log_level <= ERROR:
#             self._output(msg, 'ERROR')
# 
#     def fatal(self, msg):
#         """Log an error message and exit."""
#         if self.log_level <= FATAL:
#             self._output(msg, 'FATAL')
#         sys.exit(1)


import os
import re
from multiprocessing import Pool
from os.path import basename, join, split, splitext
from subprocess import TimeoutExpired
from Bio import SeqIO
from . import assembler as assembly, bio, blast, db, db_atram, util
from .log import Logger


def insert_blast_results(all_shards, args, state, log):
    """Add all blast results to the auxiliary  database."""
    with db.connect(state['blast_db']) as cxn:
        db.aux_db(
            cxn,
            args['temp_dir'],
            state['blast_db'],
            state['query_target'])

        for shard in all_shards:
            shard = basename(shard)

            batch = []
            output_file = blast.output_file_name(state['iter_dir'], shard)

            hits = blast.hits(log, output_file)
            is_single_end = db.is_single_end(cxn)
            for hit in hits:
                seq_name, seq_end = blast.parse_blast_title(
                    hit['title'], is_single_end)
                batch.append((state['iteration'], seq_end, seq_name, shard))
            db_atram.insert_blast_hit_batch(cxn, batch)

        db.aux_detach(cxn)


def blast_query_against_one_shard(args, state, shard):
    """Blast the query against one blast DB shard."""

# Path: lib/log.py
# class Logger:
#     """Common logging functions."""
# 
#     def __init__(self, log_file, log_level):
#         self.log_file = log_file
#         self.log_level = LEVEL[log_level]
#         self.file_handle = open(log_file, 'a') if log_file else None
# 
#     def __del__(self):
#         if self.file_handle:
#             self.file_handle.close()
# 
#     def header(self):
#         """Log header information."""
#         self.info('#' * 80)
#         self.info('aTRAM version: {}'.format(db.ATRAM_VERSION))
#         self.info('Python version: {}'.format(' '.join(sys.version.split())))
#         self.info(' '.join(sys.argv[:]))
# 
#     def subcommand(self, cmd, temp_dir, timeout=None):
#         """
#         Call a subprocess and log the output.
# 
#         Note: stdout=PIPE is blocking and large logs cause a hang.
#         So we don't use it.
#         """
#         self.debug(cmd)
# 
#         error = None
# 
#         with tempfile.NamedTemporaryFile(mode='w', dir=temp_dir) as log_output:
#             with subprocess.Popen(
#                     cmd, shell=True, stdout=log_output, stderr=log_output) as proc:
#                 try:
#                     proc.communicate(timeout=timeout)
# 
#                 # Catch any error and kill all child processes
#                 except Exception as err:  # pylint: disable=broad-except
#                     error = err
#                     self.error('Exception: {}'.format(err))
# 
#                 # On success or failure log what we can
#                 finally:
#                     wait = 5
# 
#                     killed, alive = util.kill_proc_tree(proc.pid, timeout=wait)
# 
#                     if killed:
#                         self.error('Killed {} processes with SIGTERM.'.format(killed))
# 
#                     if alive > 0:
#                         self.error('{} alive processes after SIGTERM'.format(alive))
# 
#                         killed, alive = util.kill_proc_tree(
#                             proc.pid, timeout=wait, sig=signal.SIGKILL)
#                         self.error('Killed {} processes with SIGKILL.'.format(killed))
#                         self.error('{} alive processes after SIGKILL'.format(alive))
# 
#                     with open(log_output.name) as log_input:
#                         for line in log_input:
#                             line = line.strip()
#                             if line:
#                                 self.debug(line)
#                     if error:
#                         raise error
# 
#     def _output(self, msg, level):
#         timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
#         msg = '({:8d}) {} {:<5}: {}'.format(os.getpid(), timestamp, level, msg)
#         print(msg)
#         if self.file_handle:
#             self.file_handle.write(msg)
#             self.file_handle.write('\n')
#             self.file_handle.flush()
# 
#     def debug(self, msg):
#         """Log an info message."""
#         if self.log_level <= DEBUG:
#             self._output(msg, 'DEBUG')
# 
#     def info(self, msg):
#         """Log an info message."""
#         if self.log_level <= INFO:
#             self._output(msg, 'INFO')
# 
#     def error(self, msg):
#         """Log an error message."""
#         if self.log_level <= ERROR:
#             self._output(msg, 'ERROR')
# 
#     def fatal(self, msg):
#         """Log an error message and exit."""
#         if self.log_level <= FATAL:
#             self._output(msg, 'FATAL')
#         sys.exit(1)


# Path: lib/core_atram.py
import os
import re
from multiprocessing import Pool
from os.path import basename, join, split, splitext
from subprocess import TimeoutExpired
from Bio import SeqIO
from . import assembler as assembly, bio, blast, db, db_atram, util
from .log import Logger



def insert_blast_results(all_shards, args, state, log):
    """Add all blast results to the auxiliary  database."""
    with db.connect(state['blast_db']) as cxn:
        db.aux_db(
            cxn,
            args['temp_dir'],
            state['blast_db'],
            state['query_target'])

        for shard in all_shards:
            shard = basename(shard)

            batch = []
            output_file = blast.output_file_name(state['iter_dir'], shard)

            hits = blast.hits(log, output_file)
            is_single_end = db.is_single_end(cxn)
            for hit in hits:
                seq_name, seq_end = blast.parse_blast_title(
                    hit['title'], is_single_end)
                batch.append((state['iteration'], seq_end, seq_name, shard))
            db_atram.insert_blast_hit_batch(cxn, batch)

        db.aux_detach(cxn)


def blast_query_against_one_shard(args, state, shard):
    """Blast the query against one blast DB shard."""

    log = Logger(args['log_file'], args['log_level'])