#!/usr/bin/python

import copy
import datetime
import logging
import os
import pprint
import sys
import time

THIS_YEAR = datetime.datetime.now().year


def AnalyzeJavaLog(line):
    """Analyzes a log line printed by VTS Java harness.

    Args:
        line: string for a log line.

    Returns:
        boolean (True if it's a Java log line, False otherwise)
        string (module type)
        string (phase name)
        float (timestamp)
    """
    tokens = line.split()
    timestamp = 0
    if len(tokens) < 4:
        return False, None, None, 0
    # Example
    # 05-12 07:51:45 I/VtsMultiDeviceTest:
    date_time_str = "%s-%s %s" % (THIS_YEAR, tokens[0], tokens[1])
    try:
        timestamp = time.mktime(datetime.datetime.strptime(
            date_time_str, "%Y-%m-%d %H:%M:%S").timetuple())
    except ValueError as e:
        timestamp = -1

    if (len(tokens[2]) > 2 and tokens[2] == "D/ModuleDef:" and
            tokens[3][-1] == ":"):
        return True, tokens[3][:-1], tokens[4], timestamp
    return False, None, None, timestamp


def AnalyzePythonLog(line):
    """Analyzes a log line printed by VTS Python runner.

    Args:
        line: string for a log line.

    Returns:
        boolean (True if it's a Python log line, False otherwise)
        string (test module name)
        string (test case name)
        string ('begin' or 'end')
        float (timestamp)
    """
    tokens = line.split()
    timestamp = 0
    test_module_name = None
    if len(tokens) < 7:
        return False, test_module_name, None, None, timestamp
    # Example
    # [VtsKernelSelinuxFileApi] 05-12 07:51:32.916 INFO ...
    if len(tokens[0]) > 2 and tokens[0][0] == "[" and tokens[0][-1] == "]":
        test_module_name = tokens[0][1:-1]

        date_time_str = "%s-%s %s" % (THIS_YEAR, tokens[1], tokens[2])
        try:
            timestamp = time.mktime(datetime.datetime.strptime(
                date_time_str, "%Y-%m-%d %H:%M:%S.%f").timetuple())
        except ValueError as e:
            timestamp = -1
        if tokens[4] == "[Test" and tokens[5] == "Case]":
            test_case_name = tokens[7]
            if len(tokens) == 9 and tokens[8] in ["PASS", "FAIL", "SKIP", "ERROR"]:
                return True, test_module_name, test_case_name, "end", timestamp
            elif len(tokens) == 8:
                return True, test_module_name, test_case_name, "begin", timestamp
            else:
                assert False, "Error at '%s'" % line
        return True, test_module_name, None, None, timestamp
    return False, test_module_name, None, None, timestamp

# Java harness's execution stats.
java_exec_stats = {}
# Python runner's execution stats.
python_exec_stats = {}
flag_show_samples = False


def ProcessEvent(module_type, module_name, timestamp):
    """Processes a given Java log event."""
    if module_type in java_exec_stats:
        java_exec_stats[module_type]["sum"] += timestamp
        java_exec_stats[module_type]["count"] += 1
        if module_name in java_exec_stats[module_type]:
            java_exec_stats[module_type][module_name]["sum"] += timestamp
            java_exec_stats[module_type][module_name]["count"] += 1
            if flag_show_samples:
                java_exec_stats[module_type][module_name]["samples"].append(
                    timestamp)
        else:
            java_exec_stats[module_type][module_name] = {}
            java_exec_stats[module_type][module_name]["sum"] = timestamp
            java_exec_stats[module_type][module_name]["count"] = 1
            if flag_show_samples:
                java_exec_stats[module_type][module_name]["samples"] = [
                    timestamp
                ]
    else:
        java_exec_stats[module_type] = {}
        java_exec_stats[module_type]["sum"] = timestamp
        java_exec_stats[module_type]["count"] = 1


def FilterDict(input_dict, threashold):
    """Filters items in input_dict whose values are greater than threshold."""
    result_dict = {}
    org_dict = copy.copy(input_dict)
    for key, value in input_dict.iteritems():
        if value["value"] > threashold and value["state"] == "end":
            result_dict[key] = value["value"]
            del org_dict[key]
    return org_dict, result_dict


def main(log_file_path):
    """Analyzes the phases of an execution caught in the log.

    Args:
        log_file_path: string, log file path.
    """
    print "Log File:", log_file_path

    prev_java_module_type = None
    prev_java_module_name = None
    prev_timestamp = 0
    last_timestamp = 0
    python_exec_stats = {}

    with open(log_file_path, "r") as log_file:
        for line in log_file:
            (is_java_log, java_module_type, java_module_name,
             timestamp) = AnalyzeJavaLog(line)
            if is_java_log:
                last_timestamp = timestamp
                if prev_java_module_type:
                    ProcessEvent(prev_java_module_type, prev_java_module_name,
                                 timestamp - prev_timestamp)
                prev_java_module_type = java_module_type
                prev_java_module_name = java_module_name
                prev_timestamp = timestamp
            else:
                (is_python_log, test_module_name, test_case_name, event_type,
                 timestamp) = AnalyzePythonLog(line)
                if is_python_log:
                    last_timestamp = timestamp
                    if test_case_name:
                        if event_type == "begin":
                            if test_case_name not in python_exec_stats:
                                python_exec_stats[test_case_name] = {}
                                python_exec_stats[test_case_name][
                                    "value"] = timestamp
                                python_exec_stats[test_case_name][
                                    "state"] = "begin"
                            else:
                                for count in range(1, 1000):
                                    new_test_case_name = "%s_%s" % (
                                        test_case_name, str(count))
                                    if new_test_case_name not in python_exec_stats:
                                        python_exec_stats[
                                            new_test_case_name] = {}
                                        python_exec_stats[new_test_case_name][
                                            "value"] = timestamp
                                        python_exec_stats[new_test_case_name][
                                            "state"] = "begin"
                                        break
                                python_exec_stats[test_case_name] = {}
                                python_exec_stats[test_case_name][
                                    "value"] = timestamp
                                python_exec_stats[test_case_name][
                                    "state"] = "begin"
                        elif event_type == "end":
                            assert python_exec_stats[test_case_name][
                                "state"] == "begin"
                            python_exec_stats[test_case_name]["state"] = "end"
                            python_exec_stats[test_case_name]["value"] = (
                                timestamp -
                                python_exec_stats[test_case_name]["value"])
                            assert python_exec_stats[test_case_name] >= 0, (
                                "%s >= 0 ?" %
                                python_exec_stats[test_case_name])

    if prev_java_module_type:
        ProcessEvent(prev_java_module_type, prev_java_module_name,
                     last_timestamp - prev_timestamp)

    for threshold in [600, 300, 180, 120, 60, 30]:
        python_exec_stats, filtered_dict = FilterDict(python_exec_stats,
                                                      threshold)
        print "Python test cases took >%s seconds:" % threshold
        print filtered_dict.keys()
    print "Total Execution Time Breakdown:"
    pprint.pprint(java_exec_stats, width=1)


def usage():
    """Prints usage and exits."""
    print "Script to analyze the total execution of a VTS run."
    print "Usage: <this script> <VTS host log file path>"
    exit(-1)


if __name__ == "__main__":
    if len(sys.argv) != 2:
        usage()
    main(sys.argv[1])