#!/usr/bin/env python
"""
Find bottleneck from NetLogger transfer summary logs
"""
import logging
import operator
import optparse
import sys
from netlogger.parsers.base import NLFastParser
from netlogger.analysis import bottleneck
from netlogger import nllog

# Logging
log = None
def activateLogging(name):
    global log
    log = nllog.getScriptLogger(name)

class GIGO(Exception):
    pass

def parseError(line, linenum=0, error=""):
    if log.isEnabledFor(logging.DEBUG):
        log.debug("parse.error", msg=str(error).replace('\n', ';;'))

def btlvalstr(v):
    return  "%lf MB/s (%lf Mbits/s)" % (v/1000000., v/1000000.*8)

def run(infile, outfile, algorithm=None, verbose=False):
    parser = NLFastParser(err_cb=parseError)
    parsed = parser.parseStream(infile)
    values = [ ]
    for event in parsed:
        ecode = bottleneck.Event.get(event['event'])
        if ecode:
            try:
                bytes = float(event['r.s'])
                sec = float(event['nv'])
            except KeyError:
                raise GIGO("event '%s' missing fields r.s and/or nv" % ecode)            
            value = bytes / sec
            values.append(bottleneck.Value(ecode, value))
    if verbose:
        sorted_values = algorithm.sortedValues(values)
        for v in sorted_values:
            outfile.write("Event %s = %s\n" % (v.event, btlvalstr(v.value)))            
    if len(values) != 4:
        raise GIGO("Not enough or too many values (%d)" % len(values))
    values_set = dict.fromkeys(map(operator.attrgetter('event'), values))
    if len(values_set) != 4:
        raise GIGO("Duplicate values, only %d are distinct" % len(values_set)) 
    btl, reason = algorithm.calculate(values)
    if btl is None:
        outfile.write("Unknown bottleneck")
        if reason:
            outfile.write(": %s" % reason)
        outfile.write("\n")
    else:
        outfile.write("Bottleneck is: %s at %s" % (btl.event, btlvalstr(btl.value)))
        if reason:
            outfile.write(", %s" % reason)
        outfile.write("\n")
    
def main(cmdline):
    activateLogging("nl_findbottleneck")
    usage = "%prog [options] [log-file]"
    parser = optparse.OptionParser(usage=usage,version="1.0")
    parser.add_option('-d','--debug', action="store_true", dest="debug",
                      default=False,
                      help="log debugging information, including parsing errors")
    parser.add_option('-a', '--algorithm', action="store", dest="alg", 
                      type="choice", choices=("simple",), default="simple",
                      help="choose bottleneck algorithm by name (default=%default)")
    parser.add_option('-v','--verbose',action="store_true",dest="verbose",
                      default=False,
                      help="verbose output mode (report all values)")
    options, args = parser.parse_args(cmdline[1:])    
    if len(args) == 0:
        infile = sys.stdin
    else:
        infile = file(args[0])
    outfile = sys.stdout
    if options.debug:
        log.setLevel(logging.DEBUG)
    else:
        log.setLevel(logging.WARN)
    if options.alg == "simple":
        alg = bottleneck.Method1()
    else:
        parser.error("unknown bottleneck algorithm %s" % options.alg)
    #
    try:
        run(infile, outfile, algorithm=alg, verbose=options.verbose)
    except GIGO, E:
        sys.stderr.write("error: bad input: %s\n" % E)
        sys.exit(1)

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