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

from gevo import __version__
from gevo.evolve import evolution

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Evolve CUDA kernel function",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-P', '--profile_file', type=str, required=True, default=argparse.SUPPRESS,
        help="Specify the profile file that contains all application execution and testing information")
    parser.add_argument('-r', '--resume', type=int, default=-1, metavar="GEN_NUM",
        help="Resume the process from genetating the population by reading \"stage/<GEN_NUM>.json\".\
              No resume (start from scratch) if omit or value is smaller than 0")
    parser.add_argument('--pop_size', type=int, default=128, metavar="N",
        help="Number of individual in the population. Default is 128.")
    parser.add_argument('-t', '--timeout', type=int, default=30, metavar="SEC",
        help="The timeout period to evaluate the CUDA application")
    parser.add_argument('-fitf', '--fitness_function', type=str, default='time',
        choices=['time', 'kernel_time', 'power'],
        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', metavar="RATE", help="Cross rate")
    parser.add_argument('--mupb', type=float, default='0.1', metavar="RATE", help="Mutation rate")
    parser.add_argument('--err_rate', type=str, default='0.01', metavar="RATE",
        help="Allowed maximum relative error generate from mutant comparing to the origin.\
             Range from 0 to 1. Using '|' at the end to indicate absolute error.")
    parser.add_argument('--random_seed', type=int, default=None, 
        help="Assign the random seed. Also make the entire evolution deterministic.")
    # cut, replace, insert, swap, move, operand replace, cache
    parser.add_argument('--mutop', type=str, default='c,r,i,s,m,p', metavar="OP_STR",
        help="Specify mutation operations to be enabled during evolution. Operation are separated by comma.")
    parser.add_argument('--version', action='version', version='gevo-' + __version__)
    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,
        global_seed=args.random_seed)

    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))
    if args.random_seed is not None:
        print("              Random Seed: {}".format(args.random_seed))

    try:
        evo.evolve(args.resume)
    except KeyboardInterrupt:
        evo.stop_evolve()
        