# /usr/bin/env python

"""
This script is used for launching the triage tool. It takes as input an incident
string (containing a meta ID and maybe a timestamp), downloads the data from
Chum, re-runs perception as needed, and launches the triage tool.
"""

# Moved on top to prevent:
# ImportError: dlopen: cannot load any more object with static TLS
from data.triage import launch_triage_tool

import argparse
import os
import sys
import tempfile

from data.triage.pcp_triage_tests.pcp_triage_to_sdl import translate_tests
from data.triage import util

def edit(bag_id, **kwargs):
    """
    Command to edit the set of existing tests associated with a bag_id.
    """
    if util.is_full_log(bag_id):
        raise RuntimeError('Cannot edit tests for full logs. Try running '
                           '"view" instead of "edit".')
    runner = launch_triage_tool.TriageToolRunner(
            bag_id, edit=True, command_path='data/triage/triage_tool', **kwargs)
    return runner.main()

def view(bag_id, command_path="data/triage/triage_tool", **kwargs):
    """
    Command to simply visualize a cropped or whole log. Trying to create tests
    will result in the program crashing.
    """
    runner = launch_triage_tool.TriageToolRunner(
        bag_id, edit=False, command_path=command_path, **kwargs)
    return runner.main()

def run_tests(bag_id,
              run_perception=False,
              cached_perception=False,
              run_vision=False,
              cached_vision=False,
              zcache_vision_dataset=None,
              zcache_vision_build=None,
              zcache_vision_version=None,
              custom_variant_name=None,
              **kwargs):
    """
    Command to run the set of locally cached tests associated with a bag_id.
    """
    print 'Not currently implemented.'
    return 1

def translate(bag_id, **kwargs):
    """
    Command to translate the triage test protos to SDL and add the SDL file,
    along with the pbtxts, to the repo.
    """
    return translate_tests(bag_id)

# The list of functions exposed as subcommands in the command line interface.
subcommand_functions = [edit, view, run_tests, translate]
# Create help messages the commands above.

logged_metaspins_flag_help_message = " The environment variable FLAGS_ignore_logged_metaspins can be set to true to ignore logged metaspins and instead re-compute metaspins."

subcommand_helps = {
    edit.__name__: "Start editing tests for an incident. Does not work for full logs." + logged_metaspins_flag_help_message,
    view.__name__: "Visualize an incident or full log." + logged_metaspins_flag_help_message,
    run_tests.__name__: "Locally run the cached tests for an incident. Does not work for full logs.",
    translate.__name__: "Translate the triage tests into perception log tests and store them in the repo.",
}

incident_help_text = '''
The incident is a meta id, optionally coupled with a timestamp. There are four formats:
1. '<meta_id>' will triage the entire log
2. '<meta_id>@<time>' will start 35 seconds before time and end 10 seconds after time
3. '<meta_id>@<time1>-<time2>' will start at time1 and end at time2
4. '<meta_id>@<time1>+<time2>' will start at time1 and end time2 seconds after time1.
'''

def parse_args():
    parser = argparse.ArgumentParser(prog="triage",
                                     usage="A tool for generating regression "
                                           "tests from triage tickets.")

    # Add a list of subparsers to the parser. The 'dest' keyword makes it so the
    # particular subparser name used is stored in a variable named 'command'.
    # 'choices' restricts the possible values  of the positional parameter which
    # picks the subparser.
    subparsers = parser.add_subparsers(help="commands", dest="command")

    # Now add a subparser for each possible value of the command.
    subparsers = {
        func.__name__: subparsers.add_parser(func.__name__, usage=subcommand_helps.get(func.__name__))
        for func in subcommand_functions
        }

    # Add a required incident parameter for every action.
    for command in subparsers.keys():
        data_parser = subparsers[command].add_mutually_exclusive_group()
        data_parser.add_argument(
                "incident",
                type=str,
                nargs='?',
                help=incident_help_text)
        data_parser.add_argument(
                "--chum_uri",
                type=str,
                help="Chum URI (possibly including custom variants) to run "
                "triage on")

    util.add_tool_args(subparsers["view"])
    util.add_tool_args(subparsers["edit"])

    subparsers["view"].add_argument("--command_path", type=str,
            default="data/triage/triage_tool",
            help="If specified, the path to a triage tool binary to run "
            "instead of the default triage tool. Of the form "
            "vehicle/perception/my_tool for bazel target "
            "//vehicle/perception:my_tool. Any tool referenced here must "
            "be added as a data dependency to the target "
            "//data/triage:launch_triage_tool, and MUST take the same "
            "arguments as //data/triage:triage_tool.")

    util.add_perception_runner_args(subparsers['run_tests'])

    return parser.parse_args()

if __name__ == "__main__":
    args = parse_args()
    args_dict = vars(args)

    # The bag_id is always parsed from the incident string if available. A default
    # bag_id of (None, None) is used of there was no incident string provided at
    # the command line.
    if args.incident:
        bag_id = util.parse_input_string(args.incident)
        if not bag_id:
            print "Invalid incident identifier '%s'" % args.incident
            sys.exit(1)
    elif args.chum_uri:
        bag_id, args_dict['variants'], args_dict['remote_roots'] = (
                util.parse_cli_chum_uri(args.chum_uri))

    # The list of available subcommands. This is a dict which maps command names
    # to function.
    actions = {func.__name__: func for func in subcommand_functions}

    # Retrieve the function to be executed based on the command provided.
    function = actions.get(args.command, None)
    if not function:
        print "Error: Unknown command '%s'." % args.command
        sys.exit(1)
    else:
        # The bag_id above, plus the entire set of arguments is passed to the
        # action/function.
        sys.exit(function(bag_id, **args_dict))
