import string

CODE = 0
COLON = 1
TRIPLE_QUOTE1 = 2
TRIPLE_QUOTE2 = 3
QUOTE1 = 4
QUOTE2 = 5
LBRACKET = 6
RBRACKET = 7
HASH = 8
LAMBDA = 9

def GetLeadingWs(text):
    lstart = string.find(text, string.lstrip(text)[0])
    if lstart == '-1':
        return ''
    else:
        return text[:lstart]


def Preprocess(pyFileName, pyFile, outputFile):
    output_lines = []
    state_stack = [CODE]
    lines = pyFile.readlines()
    code_clock_counter = 1
    insert_block_mark = 0
    for line in lines:
        print '.',
        if insert_block_mark:
            insert_block_mark = 0
            new_line = GetLeadingWs(line) + 'Coverage.ReachedCodeBlock("%s", %i)\n'%(pyFileName, code_clock_counter)
            output_lines.append(new_line)

        events = []
        triple_quote_locations = []

        #Process triple single-quote events
        splits = string.split(line, "'''")
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 3
            new_events.append((original_str_pos, TRIPLE_QUOTE1))
        new_events = new_events[:-1]
        for a_new_event in new_events:
            triple_quote_locations = triple_quote_locations + \
                                     [a_new_event[0],
                                      a_new_event[0] - 1,
                                      a_new_event[0] - 2]
        events = events + new_events

        #Process triple double-quote events
        splits = string.split(line, '"""')
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 3
            new_events.append((original_str_pos, TRIPLE_QUOTE2))
        new_events = new_events[:-1]   
        for a_new_event in new_events:
            triple_quote_locations = triple_quote_locations + \
                                     [a_new_event[0],
                                      a_new_event[0] - 1,
                                      a_new_event[0] - 2]
        events = events + new_events

        #Process single-quote events
        splits = string.split(line, "'")
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 1
            if original_str_pos not in triple_quote_locations:
                new_events.append((original_str_pos, QUOTE1))
        new_events = new_events[:-1]   
        events = events + new_events

        #Process double-quote events
        splits = string.split(line, '"')
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 1
            if original_str_pos not in triple_quote_locations:
                new_events.append((original_str_pos, QUOTE2))
        new_events = new_events[:-1]   
        events = events + new_events

        #Process left bracket events
        splits = string.split(line, '[')
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 1
            if original_str_pos not in triple_quote_locations:
                new_events.append((original_str_pos, LBRACKET))
        new_events = new_events[:-1]   
        events = events + new_events

        #Process right bracket events
        splits = string.split(line, ']')
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 1
            if original_str_pos not in triple_quote_locations:
                new_events.append((original_str_pos, RBRACKET))
        new_events = new_events[:-1]   
        events = events + new_events

        #Process hash events
        splits = string.split(line, '#')
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 1
            if original_str_pos not in triple_quote_locations:
                new_events.append((original_str_pos, HASH))
        new_events = new_events[:-1]   
        events = events + new_events

        #Process lambda events
        splits = string.split(line, 'lambda')
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 1
            if original_str_pos not in triple_quote_locations:
                new_events.append((original_str_pos, LAMBDA))
        new_events = new_events[:-1]   
        events = events + new_events

        #Process colon events
        splits = string.split(line, ':')
        new_events = []
        original_str_pos = 0
        for a_split in splits:
            original_str_pos = original_str_pos + len(a_split) + 1
            new_events.append((original_str_pos, COLON))
        new_events = new_events[:-1]   
        events = events + new_events

        events.sort(lambda x, y: cmp(x[0],y[0]))

        output_lines.append(line)
        for an_event in events:
            if an_event[1] in [TRIPLE_QUOTE1, TRIPLE_QUOTE2, QUOTE1, QUOTE2]:
                if state_stack[-1] == CODE:
                    state_stack.append(an_event[1])
                elif state_stack[-1] == an_event[1]:
                    del state_stack[-1]
            elif an_event[1] == LBRACKET:
                if state_stack[-1] in [CODE, LBRACKET]:
                    state_stack.append(an_event[1])
            elif an_event[1] == RBRACKET:
                if state_stack[-1] == LBRACKET:
                    del state_stack[-1]
            elif an_event[1] == HASH:
                if state_stack[-1] in [CODE, LBRACKET]:
                    break
            elif an_event[1] == LAMBDA:
                if state_stack[-1] == CODE:
                    break
            elif an_event[1] == COLON:
                if state_stack[-1] == CODE:
                    after_colon = line[an_event[0]:]
                    if string.strip(after_colon):
                        line = line[:an_event[0]] + '\n'
                        output_lines[-1] = line
                        lws = (GetLeadingWs(line) + GetLeadingWs(line)) or '    '
                        output_lines.append(lws + 'Coverage.ReachedCodeBlock("%s", %i)\n'%(pyFileName, code_clock_counter))
                        output_lines.append(lws + string.lstrip(after_colon))
                    else:
                        insert_block_mark = 1
                    code_clock_counter = code_clock_counter + 1

    line_ix = 0
    while output_lines[line_ix][0] == '#' and line_ix < len(output_lines):
        line_ix = line_ix + 1
        pass
    output_lines = output_lines[:line_ix] + \
                   ['from Ft.Lib import Coverage\n'] + \
                   ['Coverage.RegisterFile("%s", %i)\n'%(pyFileName, code_clock_counter-1)] + \
                   output_lines[line_ix:]

    outputFile.writelines(output_lines)
    outputFile.close()
    pyFile.close()


if __name__ == '__main__':
    import sys, os
    fname = os.path.join(os.getcwd(), sys.argv[1])
    py_file = open(fname, 'r')
    output_file = open(fname+'.cpp', 'w')
    Preprocess(fname, py_file, output_file)

    
