#!/usr/bin/env python3
#
# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

'''
Print a trtexec timing trace from a JSON file

Given a JSON file containing a trtexec timing trace,
this program prints the trace in CSV table format.
Each row represents an entry point in the trace.

The columns, as indicated by the header, respresent
one of the metric recorded. The output format can
be optionally converted to a format suitable for
GNUPlot.
'''

import sys
import json
import argparse
import prn_utils as pu


timestamps = ['startInMs', 'endInMs', 'startComputeMs', 'endComputeMs', 'startOutMs', 'endOutMs']

intervals = ['inMs', 'computeMs', 'outMs', 'latencyMs', 'endToEndMs']

allMetrics = timestamps + intervals

defaultMetrics = ",".join(allMetrics)

descriptions = ['start input', 'end input', 'start compute', 'end compute', 'start output',
                'end output', 'input', 'compute', 'output', 'latency', 'end to end latency']

metricsDescription = pu.combine_descriptions('Possible metrics (all in ms) are:',
                                             allMetrics, descriptions)



def skipTrace(trace, start):
    ''' Skip trace entries until start time '''

    for t in range(len(trace)):
        if trace[t]['startComputeMs'] >= start:
            return trace[t:]

    return []



def hasTimestamp(metrics):
    ''' Check if features have at least one timestamp '''

    for timestamp in timestamps:
        if timestamp in metrics:
            return True
    return False;



def avgData(data, avg, times):
    ''' Average trace entries (every avg entries) '''

    averaged = []
    accumulator = []
    r = 0

    for row in data:
        if r == 0:
            for m in row:
                accumulator.append(m)
        else:
            for m in row[times:]:
                accumulator[t] += m

        r += 1
        if r == avg:
            for t in range(times, len(row)):
                accumulator[t] /= avg
            averaged.append(accumulator)
            accumulator = []
            r = 0

    return averaged



def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--metrics', metavar='M[,M]*', default=defaultMetrics,
                        help='Comma separated list of metrics to print. ' + metricsDescription)
    parser.add_argument('--avg', metavar='N', type=int, default=1,
                        help='Print average every N records.')
    parser.add_argument('--start', metavar='T', type=float, default=0,
                        help='Start trace at time T (drop records with compute start before T ms).')
    parser.add_argument('--gp', action='store_true', help='Print GNUPlot format.')
    parser.add_argument('--no-header', action='store_true', help='Omit the header row.')
    parser.add_argument('name', metavar='filename', help='Trace file.')
    args = parser.parse_args()

    metrics = args.metrics.split(',')
    count = args.gp and (not hasTimestamp(metrics) or len(metrics) == 1)

    if not args.no_header:
        pu.printHeader(allMetrics, metrics, args.gp, count)

    with open(args.name) as f:
        trace = json.load(f)

    if args.start > 0:
        trace = skipTrace(trace, args.start)

    trace = pu.filterData(trace, allMetrics, metrics)

    if args.avg > 1:
        trace = avgData(trace, args.avg, hasTimestamp(metrics))

    pu.printCsv(trace, count)


if __name__ == '__main__':
    sys.exit(main())
