#!/usr/bin/env python
#coding: utf-8

__author__ = 'Toshihiro Kamiya <kamiya@mbj.nifty.com>'
__status__ = 'experimental'

from bisect import bisect_left
import re
import os
import sys

import asm_manip as am
import clonefile_manip as cm

def scan_linumber_table(text):
    lineseq, indexseq = [], []
    pat = re.compile(r'^\s+line\s+(\d+):\s+(\d+)$')
    for L in text:
        m = pat.match(L)
        assert m
        lineseq.append(int(m.group(1)))
        indexseq.append(int(m.group(2)))
    return lineseq, indexseq

def sig_to_source_file(sig):
    p = sig.index('.')
    q = sig.find('$')
    if q >= 0 and q < p:
        p = q
    source_file = sig[:p] + '.java'
    reference = sig[p+1:]
    return source_file, reference

def convert_location_from_index_to_linenum(L, sig2linenumbertable):
    p = L.rindex(' >')
    depth = int(L[p+2:])
    L = L[:p]
    p = L.rindex(',')
    sig, index = L[:p], int(L[p+1:])
    ls_is = sig2linenumbertable.get(sig)
    assert ls_is != None
    lineseq, indexseq = ls_is
    i = bisect_left(indexseq, index) - 1
    linenum = lineseq[i]
    source_file, reference = sig_to_source_file(sig)
    return (source_file, linenum, depth, reference)

def format_clone_iter(clone_index_file, sig2linenumbertable):
    def conv(L):
        return convert_location_from_index_to_linenum(L, sig2linenumbertable)

    def opeseq_i(opeseq):
        yield 'ope:'
        for ope in opeseq:
            yield '  %s' % ope

    for tag, data in cm.read_clone_file_iter(clone_index_file):
        if tag == cm.OPTIONS:
            args = data
            for name, value in args.option_iter():
                if name == 'ngram-size' and int(value) < 0:
                    args.delete('ngram-size')
            buf = args.format()
            for L in buf:
                yield L
            yield ''
        elif tag == cm.OPESEQ_LOCS:
            opeseq, locs = data
            for L in opeseq_i(opeseq): yield L
            locs = map(conv, locs)
            yield 'loc:'
            for source_file, linenum, depth, reference in sorted(locs):
                yield '  %s: %d >%d // %s' % (source_file, linenum, depth, reference)
            yield ''
        elif tag == cm.OPESEQ_TRACES:
            opeseq, traces = data
            for L in opeseq_i(opeseq): yield L
            traces = [map(conv, lst) for lst in traces]
            for trace in traces:
                yield "trace:"
                for source_file, linenum, depth, reference in trace:
                    yield '  %s: %d >%d // %s' % (source_file, linenum, depth, reference)
            yield ''
        elif tag == cm.METRIC_VALUES:
            for name, value in data:
                yield "%s= %s" % (name, value)
        elif tag == cm.OPESEQ_SINGLE:
            opeseq, _ = data
            for L in opeseq_i(opeseq): yield L
            yield ''
        else:
            assert False

def gen_argpsr():
    from argparse import ArgumentParser
    from _version_data import VERSION
    psr = ArgumentParser(description='(Re)format clone positions in line numbers')
    psr.add_argument('-a', '--asm-directory', action='store')
    psr.add_argument('clone_index', action='store',
            help="clone file generated by det-clone OR trace file generated by exp-clone. specify '-' to read from stdin")
    psr.add_argument('--version', action='version', version='%(prog)s ' + VERSION)
    return psr

def main(argv):
    psr = gen_argpsr()
    args = psr.parse_args(argv[1:])

    asmdir = args.asm_directory
    cloneindex = args.clone_index

    if not os.path.isdir(asmdir):
        sys.exit("error: fail to access asmdir: %s" % asmdir)

    sig2linenumbertable = {}
    for asm_file, sig, code, etbl, ltbl in am.get_method_code_and_tables_iter(asmdir):
        sig2linenumbertable[sig] = scan_linumber_table(ltbl)

    for L in format_clone_iter(cloneindex, sig2linenumbertable):
        sys.stdout.write(('%s\n' % L).encode('utf-8'))

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


