# Copyright 2013 The Emscripten Authors.  All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License.  Both these licenses can be
# found in the LICENSE file.

"""Tool to find or compare big functions in a js or ll file
"""

import sys


def humanbytes(nbytes):
    if nbytes > 9 * 1024 * 1024:
        return '{}MB'.format(nbytes / 1024 / 1024)
    elif nbytes > 9 * 1024:
        return '{}KB'.format(nbytes / 1024)
    else:
        return '{}B'.format(nbytes)


def processfile(filename):
    start = None
    curr = None
    nbytes = None
    data = {}
    for i, line in enumerate(open(filename)):
        if line.startswith(('function ', 'define ', ' (func ')) and '}' not in line:
            start = i
            curr = line
            nbytes = len(line)
        elif line.startswith(('}', ' )')) and curr:
            nlines = i - start
            data[curr] = (nlines, nbytes + 1)
            curr = None
            start = None
        elif curr:
            nbytes += len(line)
    return data


def common_compare(data1, data2):
    fns1 = set(data1.keys())
    fns2 = set(data2.keys())
    commonfns = fns1.intersection(fns2)
    commonlinediff = 0
    commonbytediff = 0
    for fn in commonfns:
        d1 = data1[fn]
        d2 = data2[fn]
        commonlinediff += d2[0] - d1[0]
        commonbytediff += d2[1] - d1[1]
    linesword = 'more' if commonlinediff >= 0 else 'less'
    bytesword = 'more' if commonbytediff >= 0 else 'less'
    print('file 2 has {} lines {} than file 1 in {} common functions'.format(abs(commonlinediff), linesword, len(commonfns)))
    print('file 2 has {} {} than file 1 in {} common functions'.format(humanbytes(abs(commonbytediff)), bytesword, len(commonfns)))


def uniq_compare(data1, data2):
    fns1 = set(data1.keys())
    fns2 = set(data2.keys())
    uniqfns1 = fns1 - fns2
    uniqfns2 = fns2 - fns1
    uniqlines1 = 0
    uniqbytes1 = 0
    uniqlines2 = 0
    uniqbytes2 = 0
    for fn in uniqfns1:
        d = data1[fn]
        uniqlines1 += d[0]
        uniqbytes1 += d[1]
    for fn in uniqfns2:
        d = data2[fn]
        uniqlines2 += d[0]
        uniqbytes2 += d[1]
    uniqcountdiff = len(uniqfns2) - len(uniqfns1)
    assert len(fns2) - len(fns1) == uniqcountdiff
    uniqlinediff = uniqlines2 - uniqlines1
    uniqbytediff = uniqbytes2 - uniqbytes1
    countword = 'more' if uniqcountdiff >= 0 else 'less'
    linesword = 'more' if uniqlinediff >= 0 else 'less'
    bytesword = 'more' if uniqbytediff >= 0 else 'less'
    print('file 2 has {} functions {} than file 1 overall (unique: {} vs {})'.format(abs(uniqcountdiff), countword, len(uniqfns2), len(uniqfns1)))
    print('file 2 has {} lines {} than file 1 overall in unique functions'.format(abs(uniqlinediff), linesword))
    print('file 2 has {} {} than file 1 overall in unique functions'.format(humanbytes(abs(uniqbytediff)), bytesword))


def list_bigfuncs(data):
    data = list(data.items())
    data.sort(key=lambda f_d: f_d[1][0])
    print(''.join(['%6d lines (%6s) : %s' % (d[0], humanbytes(d[1]), f) for f, d in data]))


def main():
    if len(sys.argv) < 2 or len(sys.argv) > 3 or sys.argv[1] == '--help':
        print('Usage:')
        print('    {} file1 - list functions in a file in ascending order of size'.format(sys.argv[0]))
        print('    {} file1 file2 - compare functions across two files'.format(sys.argv[0]))
        return 1

    if len(sys.argv) == 2:
        filename = sys.argv[1]
        data = processfile(filename)
        list_bigfuncs(data)
        return 0

    if len(sys.argv) == 3:
        filename1 = sys.argv[1]
        data1 = processfile(filename1)
        filename2 = sys.argv[2]
        data2 = processfile(filename2)
        uniq_compare(data1, data2)
        common_compare(data1, data2)
        return 0

    assert False


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