#!/usr/bin/env python3
import argparse
import json
import sys
import subprocess

from gevo.evolve import evolution

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Evolve CUDA kernel function")
    parser.add_argument('-P', '--profile_file', type=str, required=True,
        help="Specify the profile file that contains all application execution and testing information")
    # parser.add_argument('-k', '--kernel', type=str,
    #     help="Target kernel function of the given CUDA application. Use comma to separate kernels.")
    parser.add_argument('-r', '--resume', type=int, default=-1,
        help="Resume the process from genetating the population by reading stage/<RESUME>.json")
    parser.add_argument('--pop_size', type=int, default=128,
        help="Number of individual in the population. Default is 128.")
    parser.add_argument('-t', '--timeout', type=int, default=30,
        help="The timeout period to evaluate the CUDA application")
    parser.add_argument('-fitf', '--fitness_function', type=str, default='time',
        help="What is the target fitness for the evolution. Default ot execution time. Can be changed to power")
    parser.add_argument('--cxpb', type=float, default='0.8', help="Cross rate")
    parser.add_argument('--mupb', type=float, default='0.1', help="Mutation rate")
    parser.add_argument('--err_rate', type=float, default='0.01',
        help="Allowed maximum relative error generate from mutant comparing to the origin")
    # cut, replace, insert, swap, move, operand replace, cache
    parser.add_argument('--mutop', type=str, default='c,r,i,s,m,p',
        help="Specify mutation operation to be enabled during evolution. Operation are separated by comma.")
    # parser.add_argument('binary',help="Binary of the CUDA application", nargs='?', default='a.out')
    # parser.add_argument('args',help="arguments for the application binary", nargs=argparse.REMAINDER)
    args = parser.parse_args()

    try:
        profile = json.load(open(args.profile_file))
    except:
        print(sys.exc_info())
        exit(-1)

    evo = evolution(
        kernel=profile['kernels'],
        bin=profile['binary'],
        profile=profile,
        timeout=args.timeout,
        fitness=args.fitness_function,
        popsize=args.pop_size,
        CXPB=args.cxpb,
        MUPB=args.mupb,
        err_rate=args.err_rate,
        mutop=args.mutop)

    print("      Target CUDA program: {}".format(profile['binary']))
    print("Args for the CUDA program:")
    for tc in evo.testcase:
        print("\t{}".format(" ".join(tc.args)))
    print("           Target kernels: {}".format(" ".join(profile['kernels'])))
    print("       Evaluation Timeout: {}".format(args.timeout))
    print("         Fitness function: {}".format(args.fitness_function))
    print("               Cross Rate: {}".format(args.cxpb))
    print("            Mutation Rate: {}".format(args.mupb))
    print("      Tolerate Error Rate: {}".format(args.err_rate))
    print("   Enabled Mut Operations: {}".format(args.mutop))

    try:
        evo.evolve(args.resume)
    except KeyboardInterrupt:
        subprocess.run(['killall', profile['binary']])
        print("   Valid variant: {}".format(evo.mutStats['valid']))
        print(" Invalid variant: {}".format(evo.mutStats['invalid']))
        print("Infinite variant: {}".format(evo.mutStats['infinite']))
        evo.mutLog()
        if evo.generation > 0:
            evo.presentation.save('progress.pptx')