#! /usr/bin/env python
"""
testgenie - A simple templating kim test creation tool

In order to use the testgenie, you need to specify a folder (default:
test_template in the TD directory) that serves as a template for your test, as
well as a file containing json dictionaries that will be passed in to generate
all of your tests.

Test generation takes place in steps.

  1. Grab the dictionary defined in a line of the test_generator.json file
  2. Add all global variables, if any defined.
  3. Process the kimspec.edn file as a jinja template with those variables defined.
  4. Add all of the variables defined in the kimspec to the local dictionary
  5. Set the folder name from the TEST_NAME attribute in the kimspec file
  6. For each remaining file, first check if a:
        {# FILENAME = desired_file_name_with_{{ variables }} #}
      directives exist to fix the filename
  7. Remove prefixes or extensions as necessary
  8. Process the rest of the file as a jinja template.
  9. Move folders to the given destination (or test dir in repository by default)

For convenience, if the --random-kimnums flag is given, the templating dictionaries will
also contain a random 12 digit integer for use as the center of a kimcode.

Note also, that the script expects the path for the test-generator and
global-variable files to be one directory up from the test template folder.  By default
'TEST_DRIVER_NAME' will be the same as <source> and 'version' will be 0, or whatever is specified
with the command line argument

Program outputs a list of all generated test folders, this is useful to catch in
order to remove the folders later if necessary. e.g.
  testgenie TDNAME > generated_tests

Then they can be removed
  cat generated_tests | xargs rm -r

Or you can verify them all
  cut -d / -f 6 generated_tests | xargs -n 1 pipeline_verify

"""

import argparse
VERSION = 0.1

import kimquery
import kimobjects
import database
from config import *
from logger import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("testgenie")

import os
import ConfigParser
import jinja2
import tempfile
import shutil
import re
import random
import json
import sys
import clj

TEDIR = os.path.join(KIM_REPOSITORY_DIR,"te")

###############################
# testfactory
###############################

"""
Based on a set of templates,
create lots of tests
"""

FILENAME_REGEX = re.compile(r"""
    FILENAME    #magic word
    \s*         # whitespace
    =           # equal character
    \s*         # whitespace
    (\S+)       # name
    """, re.VERBOSE)

template_environment = jinja2.Environment(loader=jinja2.FileSystemLoader('/'))
template_environment.globals.update({
    "query": kimquery.query, "stripversion": database.strip_version
})

def maketest(folder, variable_dict, dest=TEDIR, dry_run=False,
        filename_prefix=None,
        filename_extension=None):
    """ Make a test given the input folder """
    tempdir = tempfile.mkdtemp()
    # First parse the kimspec file
    CONFIG_FILE_TEMP = CONFIG_FILE + ".genie"

    file_newconf = os.path.join(folder, CONFIG_FILE_TEMP)
    kimspec_template = template_environment.get_template(file_newconf)
    kimspec = kimspec_template.render(**variable_dict)

    with open(os.path.join(tempdir, CONFIG_FILE), 'w') as f:
        f.write(kimspec)

    #extend variable_dict with everything defined in kimspec
    kimspeckeys = clj.loads(kimspec)
    variable_dict.update({'kimspec': kimspeckeys})

    # test name from kimspec
    test_name = variable_dict['kimspec']['extended-id']

    #make tmp directory
    tmp_test_dir = os.path.join(tempdir, test_name)
    shutil.copytree(folder,tmp_test_dir)
    logger.info("BUILDING: %r @ %r", test_name, tmp_test_dir)
    logger.debug("Variable_dict: %r", variable_dict)

    for (basepath, folders, files) in os.walk(tmp_test_dir):
        for fl in files:
            logger.debug("processing %r", fl)
            filepath = os.path.join(basepath,fl)
            with open(filepath) as f:
                contents = f.read()
            #copy original mode so we can chmod at end
            original_mode = os.stat(filepath).st_mode
            filename = fl
            if filename_prefix:
                if not filename.startswith(filename_prefix):
                    os.remove(filepath)
                    continue
                else:
                    filename = filename[len(filename_prefix):]
            if filename_extension:
                if not filename.endswith(filename_extension):
                    os.remove(filepath)
                    continue
                else:
                    filename = filename[:-len(filename_extension)-1]
            filename_search = FILENAME_REGEX.search(contents)
            if filename_search:
                filename_template = FILENAME_REGEX.search(contents).group(1)
                filename = jinja2.Template(filename_template).render(**variable_dict)

            logger.debug("new filename: %r", filename)
            template = template_environment.get_template(filepath)
            new_contents = template.render(**variable_dict)

            newfilepath = os.path.join(basepath, filename)
            os.remove(filepath)

            with open(newfilepath,'w') as g:
                g.write(new_contents)
            #set mode
            os.chmod(newfilepath, original_mode)

    if not dry_run:
        #now move to its home
        logger.info("Moving to %r", os.path.join(dest,test_name))
        shutil.move(tmp_test_dir, os.path.join(dest,test_name))
        #cleanup
        shutil.rmtree(tempdir)

        return os.path.join(dest, test_name)

    return tempdir

##############################
# Setup argparse
##############################

parser = argparse.ArgumentParser(description="KIM test generation genie",
        epilog=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter,)
parser.add_argument('source', type=str,
        help="kimcode for test driver, or complete path to test-gen folder")
parser.add_argument('--generator-file', type=str,
        default='test_generator.json',
        help="generator file [default: test_generator.json]")

parser.add_argument('--global-variables', type=str,
        help="Additional json of global variables, as file or string")

parser.add_argument('--dry-run', action='store_true',
        help="Don't write the tests out, just show what would be generated")

parser.add_argument('--destination', type=str,
        default=TEDIR,
        help="destination folder for tests [default: {}]".format(TEDIR))

parser.add_argument('--random-kimnums', action="store_true",
        help="Use randomly generated kimid numbers, provided as 'kimnum' field")

parser.add_argument('--verbose', action="store_true",
        help="Show debugging messages")

parser.add_argument('--quiet', action="store_true",
        help="Hide all logging messages")

parser.add_argument('--filename-prefix', type=str,
        help="Filename prefix, only files with such prefix are processed.")
parser.add_argument('--filename-extension', type=str,
        help="Filename extension, only files with such extension are processed.")

# parser.add_argument('--verify', action="store_true",
#         help="Run verification tests on generated tests only works if not a dry-run")

parser.add_argument("--version", type=int,
        default=0,
        help="parameter to pass as 'version' to global dictionary")

args = vars(parser.parse_args())

#Set verbosity level
if args['verbose']:
    logger.setLevel(logging.DEBUG)
if args['quiet']:
    logger.setLevel(logging.ERROR)

#Destination directory
dest = args['destination']
dry_run = args['dry_run']
random_codes = args['random_kimnums']
generator_file = args['generator_file']
filename_prefix = args['filename_prefix']
filename_extension = args['filename_extension']

#find test-template directory
try:
    tdpath = os.path.join(kimobjects.TestDriver(args['source']).path,'test_template')
except Exception:
    tdpath = args['source']

global_vars = {"TEST_DRIVER_NAME": args['source']}
global_vars['version'] = "{:03d}".format(args['version'])

#Get global vars
if args['global_variables']:
    global_path = args['global_variables']
    try:
        with open(os.path.abspath(os.path.join(tdpath,'..',global_path))) as f:
            global_vars = json.loads(f.read())
    except Exception:
        global_vars = json.loads(global_path)

#generate tests
gentests = []
with open(os.path.abspath(os.path.join(tdpath,'..',generator_file))) as f:
    for line in f:
        variable_dict = global_vars.copy()
        variable_dict.update(json.loads(line))
        if random_codes:
            #supply kimnum
            variable_dict['kimnum'] = "%012d" % random.randint(0,1e12-1)
        newtestpath =  maketest(tdpath, variable_dict = variable_dict, dest=dest, dry_run=dry_run,
                filename_prefix =filename_prefix,
                filename_extension = filename_extension )
        gentests.append(newtestpath)
        print newtestpath




