#!/usr/bin/env python

"""
This utility allows the CIME user to view a field in a env_*xml file via a commandline interface.

If this tool is missing any feature that you need, please go to github and create an issue (https://github.com/ESMCI/cime/issues). Detailed information is available at https://github.com/ESMCI/cime.


    AUTHOR AND CREDITS
        Authors: Jim Edwards , ....

        Based on legacy code with many contributers. (see ...)

    COPYRIGHT AND LICENSE

        This library is free software; you can redistribute it and/or modify
        it under the same terms as Python itself.
"""

from standard_script_setup import *

from CIME.case import Case
from CIME.utils import expect, convert_to_string

import textwrap, sys, re

logger = logging.getLogger("xmlquery")

###############################################################################
def parse_command_line(args):
###############################################################################
    parser = argparse.ArgumentParser(
usage="""%s <VAR> [<VAR> <VAR> ...]

Examples:

# Show NTASKS for all components
> %s NTASKS

# Show RUNDIR
> %s RUNDIR

# Show all vars containing the substring ROOT
> %s -p ROOT

# Show all vars in the case
> %s --listall

# Show everything in the run_pio subgroup
> %s --listall --subgroup=run_pio

# Show all vars containing the substring ROOT in subgroup build_def
> %s -p ROOT --subgroup build_def""" % ((os.path.basename(sys.argv[0]), ) * 7),

description="Query the xml files for attributes and return their values." ,
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent(__doc__))

    CIME.utils.setup_standard_logging_options(parser)

    # Set command line options
    parser.add_argument("variables", nargs="*"  ,
                        help="variable name in env_*.xml file ( <entry_id id='variable_name'>value</entry_id> )")

    parser.add_argument("-file" , "--file",
                        help="specify the file you want to query")

    parser.add_argument("-subgroup","--subgroup",
                        help="apply to this subgroup only")

    parser.add_argument("-caseroot" , "--caseroot", default=os.getcwd(),
                        help="Case directory to reference")

    parser.add_argument("-listall", "--listall" , default=False , action="store_true" ,
                        help="List all variables and their values")

    parser.add_argument("-no-resolve", "--no-resolve", action="store_true",
                        help="Do not resolve variable values")

    parser.add_argument("-p", "--partial-match", action="store_true",
                        help="Allow partial matches of variable names, treats args as regex.")

    group = parser.add_mutually_exclusive_group()

    group.add_argument("--full", default=False, action="store_true",
                        help="Print a full listing for the variable")

    group.add_argument("-fileonly", "--fileonly", default=False, action="store_true",
                        help="Only print the filename that the field is defined in.")

    group.add_argument("-value", "--value", default=False, action="store_true",
                        help="Only print one value without newline character. If more than one has been found print first value in list.")

    group.add_argument("--raw", default=False, action="store_true",
                        help="Print the complete raw record associated with this variable")
    group.add_argument("--description", default=False, action="store_true",
                        help="Print the description associated with this variable")

    group.add_argument("--group", default=False, action="store_true",
                        help="Print the group associated with this variable")

    group.add_argument("--type", default=False, action="store_true",
                        help="Print the data type associated with this variable")

    group.add_argument("--valid-values", default=False, action="store_true",
                        help="Print the valid values associated with this variable if defined")

    args = CIME.utils.parse_args_and_handle_standard_logging_options(args, parser)

    if (len(sys.argv) == 1) :
        parser.print_help()
        exit()

    if len(args.variables) == 1:
        variables = args.variables[0].split(',')
    else:
        variables = args.variables

    return variables, args.subgroup, args.caseroot, args.listall, args.fileonly, \
        args.value, args.no_resolve, args.raw, args.description, args.group, args.full, \
        args.type, args.valid_values, args.partial_match, args.file

def get_value_as_string(case, var, attribute=None, resolved=False, subgroup=None):
    thistype = case.get_type_info(var)
    value = case.get_value(var, attribute=attribute, resolved=resolved, subgroup=subgroup)
    if value is not None and thistype:
        value = convert_to_string(value, thistype, var)
    return value

def xmlquery(case, variables, subgroup=None, fileonly=False,
             resolved=True, raw=False, description=False, group=False,
             full=False, dtype=False, valid_values=False, xmlfile=None):
    """
    Return list of attributes and their values, print formatted

    """
    results = {}
    comp_classes = case.get_values("COMP_CLASSES")
    if xmlfile:
        case.set_file(xmlfile)
    for var in variables:
        if subgroup is not None:
            groups = [subgroup]
        else:
            groups = case.get_record_fields(var, "group")
            if groups is None and xmlfile:
                groups = ['none']

        if xmlfile:
            if not groups:
                value =  case.get_value(var, resolved=resolved)
                results['none'] = {}
                results['none'][var] = {}
                results['none'][var]['value']  = value
        else:
            expect(groups, " No results found for variable %s"%var)
        for group in groups:
            if not group in results:
                results[group] = {}
            if not var in results[group]:
                results[group][var] = {}

            expect(group, "No group found for var %s"% var)
            value = get_value_as_string(case, var, resolved=resolved, subgroup=group)
            if value is None:
                var, comp, iscompvar = case.check_if_comp_var(var)
                if iscompvar:
                    value = []
                    for comp in comp_classes:
                        try:
                            nextval = get_value_as_string(case,var, attribute={"component" : comp}, resolved=resolved, subgroup=group)
                        except:
                            nextval = get_value_as_string(case,var, attribute={"component" : comp}, resolved=False, subgroup=group)

                        if nextval is not None:
                            value.append(comp + ":" + "%s"%nextval)
                else:
                    value = get_value_as_string(case, var, resolved=resolved, subgroup=group)

            if value is None:
                if xmlfile:
                    expect(False, " No results found for variable %s in file %s"%(var, xmlfile))
                else:
                    expect(False, " No results found for variable %s"%var)

            results[group][var]['value'] = value

            if raw:
                results[group][var]['raw'] = case.get_record_fields(var, "raw")
            if description or full:
                results[group][var]['desc'] = case.get_record_fields(var, "desc")
            if fileonly or full:
                results[group][var]['file'] = case.get_record_fields(var, "file")
            if dtype or full:
                results[group][var]['type'] = case.get_type_info(var)
            if valid_values or full:
                results[group][var]['valid_values'] = case.get_record_fields(var, "valid_values")

    return results

def _main_func():
    if ("--test" in sys.argv):
        test_results = doctest.testmod(verbose=True)
        sys.exit(1 if test_results.failed > 0 else 0)

    # Initialize command line parser and get command line options
    variables, subgroup, caseroot, listall,  fileonly, \
        value, no_resolve, raw, description, group, full, dtype, \
        valid_values, partial_match, xmlfile = parse_command_line(sys.argv)

    # Initialize case ; read in all xml files from caseroot
    with Case(caseroot) as case:
        if listall or partial_match:
            all_variables = sorted(case.get_record_fields(None, "varid"))
            if partial_match:
                all_matching_vars = []
                for variable in variables:
                    regex = re.compile(variable)
                    for all_variable in all_variables:
                        if regex.search(all_variable):
                            if subgroup is not None:
                                vargroups = case.get_record_fields(all_variable, "group")
                                if subgroup not in vargroups:
                                    continue

                            all_matching_vars.append(all_variable)

                variables = all_matching_vars
            else:
                if subgroup is not None:
                    all_matching_vars = []
                    for all_variable in all_variables:
                        vargroups = case.get_record_fields(all_variable, "group")
                        if subgroup not in vargroups:
                            continue
                        else:
                            all_matching_vars.append(all_variable)

                    variables = all_matching_vars
                else:
                    variables = all_variables

        expect(variables, "No variables found")
        results = xmlquery(case, variables, subgroup, fileonly, resolved=not no_resolve,
                           raw=raw, description=description, group=group, full=full,
                           dtype=dtype, valid_values=valid_values, xmlfile=xmlfile)

    if full or description:
        wrapper=textwrap.TextWrapper()
        wrapper.subsequent_indent = "\t\t\t"
        wrapper.fix_sentence_endings = True

    for group in sorted(iter(results)):
        if len(variables) > 1 or len(results.keys()) > 1 or full:
            print "\nResults in group %s"%group
        for var in variables:
            if var in results[group]:
                if raw:
                    print results[group][var]['raw']
                elif value:
                    sys.stdout.write("%s"%results[group][var]['value'])
                elif description:
                    desc_text = ' '.join(results[group][var]['desc'][0].split())
                    print "\t%s: %s"%(var, wrapper.fill(desc_text))
                elif fileonly:
                    print "\t%s: %s"%(var, results[group][var]['file'])
                elif dtype:
                    print  "\t%s: %s"%(var, results[group][var]['type'])
                elif valid_values:
                    if 'valid_values' in results[group][var]:
                        print  "\t%s: %s"%(var, results[group][var]["valid_values"])
                elif full:
                    desc_text = ' '.join(results[group][var]['desc'][0].split())
                    print "\t%s: value=%s"%(var, results[group][var]['value'])
                    print  "\t\ttype: %s"%(results[group][var]['type'][0])
                    if 'valid_values' in results[group][var]:
                        print  "\t\tvalid_values: %s"%(results[group][var]["valid_values"])
                    print "\t\tdescription: %s"%(wrapper.fill(desc_text))
                    print "\t\tfile: %s"%(results[group][var]['file'][0])
                else:
                    print "\t%s: %s"%(var, results[group][var]['value'])


if (__name__ == "__main__"):
    _main_func()
