#!/usr/bin/env python

import os
from subprocess import check_call, CalledProcessError

import click
import sys

from mutmut import mutate, ALL, count_mutations, mutate_file
from shutil import move


# this function is stolen and modified from tqdm
def status_printer(file):
    """
    Manage the printing and in-place updating of a line of characters.
    Note that if the string is longer than a line, then in-place
    updating may not work (it will print a new line at each refresh).
    """
    fp = file

    last_len = [0]

    def print_status(s):
        len_s = len(s)
        fp.write('\r' + s + (' ' * max(last_len[0] - len_s, 0)))
        fp.flush()
        last_len[0] = len_s
    return print_status

print_status = status_printer(sys.stdout)


@click.command()
@click.argument('paths_to_mutate', nargs=-1)
@click.option('--apply', help='apply the mutation to the given file. Must be used in combination with --mutation_number', is_flag=True)
@click.option('--backup/--no-backup', default=False)
@click.option('--mutation', type=click.INT)
@click.option('--runner', default='py.test')
@click.option('--testsdir', default='tests/')
def main(paths_to_mutate, apply, mutation, backup, runner, testsdir):
    if apply:
        assert mutation is not None
        assert len(paths_to_mutate) == 1
        mutations_performed = mutate_file(backup, mutation, paths_to_mutate[0])
        if mutations_performed == 0:
            print 'ERROR: no mutations performed. Are you sure the index is not too big?'
        return

    mutations_by_file = {}

    for path in paths_to_mutate:
        for filename in python_source_files(path):
            mutations_by_file[filename] = count_mutations(open(filename).read())

    total = sum(mutations_by_file.values())

    print '--- starting mutation ---'
    progress = 0
    for filename, mutations in mutations_by_file.items():
        # print filename
        for mutation_index in range(mutations):
            if mutation is not None and mutation != mutation_index:
                continue
            progress += 1
            print_status('%s out of %s' % (progress, total))
            # result = check_call('py.test tests/ --mutate="%s:%s" -s' % (filename, mutation_index), shell=True)  # TODO: fix plugin, then use this instead
            try:
                assert mutate_file(backup=True, mutation=mutation_index, filename=filename)
                try:
                    check_call('%s %s' % (runner, testsdir), shell=True, stdout=open('/dev/null', 'w'), stderr=open('/dev/null', 'w'))
                    print_status('')
                    print '\rFAILED: mutmut %s --mutation %s --apply' % (filename, mutation_index)
                except CalledProcessError:
                    pass
                os.remove(filename)
                try:
                    os.remove(filename+'c')  # remove .pyc file
                except OSError:
                    pass
            finally:
                move(filename+'.bak', filename)


def python_source_files(path):
    for root, dirs, files in os.walk(path):
        for filename in files:
            if filename.endswith('.py'):
                yield os.path.join(root, filename)


def number_of_mutations(path):
    total = 0
    for filename in python_source_files(path):
        _, c = mutate(open(filename).read(), ALL)
        total += c
    return total

if __name__ == '__main__':
    main()
