#!/usr/bin/python
'''
    Valhalla 0.0.1
    Copyright (C) 2009 Luka Napotnik <luka.napotnik@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''

import sys
import pygtk
import gtk
import gobject
from xml.dom import minidom
import vte

def selected_frame_cb(treeview, path, view_column, vte_widget):
    store = treeview.get_model()
    iter = store.get_iter(path)
    file = store.get_value(iter, 2)
    dir = store.get_value(iter, 3)
    line = store.get_value(iter, 4)

    if file and line > -1:
        vte_widget.fork_command("vim", ["vim", "+"+str(line), dir+"/"+file], None, "", False, False, False)

def get_stack(store, iter, node):
    frame_n = 0
    for el in node.childNodes:
        if el.nodeName == "frame":
            fn = el.getElementsByTagName("fn")
            ob = el.getElementsByTagName("obj")
            ip = el.getElementsByTagName("ip")
            file = el.getElementsByTagName("file")
            dir = el.getElementsByTagName("dir")
            line = el.getElementsByTagName("line")
            if file:
                if line:
                    frame_str = "#" + str(frame_n) + " - [" + \
                            ip[0].childNodes[0].data+ "]\t<span foreground=\"blue\">" + \
                            fn[0].childNodes[0].data + " [<i>" + file[0].childNodes[0].data + \
                            ":" + line[0].childNodes[0].data + \
                            "</i>]</span> @ <span foreground=\"green\">" + \
                            ob[0].childNodes[0].data + "</span>"

                    store.insert(iter, 0, [frame_str,fn[0].childNodes[0].data, \
                            file[0].childNodes[0].data, dir[0].childNodes[0].data, \
                            int(line[0].childNodes[0].data)])
                else:
                    frame_str = "#" + str(frame_n) + " - [" + ip[0].childNodes[0].data + \
                            "]\t<span foreground=\"blue\">" + fn[0].childNodes[0].data + \
                            " [<i>" + file[0].childNodes[0].data + \
                            "</i>]</span> @ <span foreground=\"green\">" + \
                            ob[0].childNodes[0].data + "</span>"

                    store.insert(iter, 0 [frame_str,fn[0].childNodes[0].data, \
                            file[0].childNodes[0].data, dir[0].childNodes[0].data, -1])
            else:
                if fn and ob:
                    frame_str = "#" + str(frame_n) + " - [" + ip[0].childNodes[0].data + \
                            "]\t<span foreground=\"blue\">" + fn[0].childNodes[0].data + \
                            "</span> @ <span foreground=\"green\">" + ob[0].childNodes[0].data + \
                            "</span>"

                    store.insert(iter, 0, [frame_str, None, None, None, -1])

            frame_n += 1

def parse_error(store, node, p_iter = None, tid_node = None):
    found_kind = False
    msg = ""
    for el in node.childNodes:
        if el.nodeName == "what":
           if found_kind:
               if kind_value == "Leak_DefinitelyLost":
                    msg = '<span foreground=\"#ff5151\">' + el.childNodes[0].data + '</span>'
               elif kind_value == "InvalidFree":
                    msg = '<span foreground=\"red\">' + el.childNodes[0].data + '</span>'
               else:
                    msg = el.childNodes[0].data	
           else:
               msg = el.childNodes[0].data	
           found_kind = False
           iter = store.append(p_iter, [msg, None, None, None, -1])
        if el.nodeName == "kind":
            found_kind = True
            kind_value = el.childNodes[0].data
        if el.nodeName == "origin":
            parse_error(store, el, iter)
        if el.nodeName == "auxwhat":
            msg = "<i>" + el.childNodes[0].data + "</i>"
            iter = store.append(iter, [msg, None, None, None, -1])
            get_stack(store, iter, el)
        if el.nodeName == "stack":
            get_stack(store, iter, el)

def parse_output(filename, store):
    xmldoc = minidom.parse(filename)
    threads = xmldoc.getElementsByTagName("tid")
    tid_table = dict()
    iter = None
    for e in xmldoc.childNodes:
        for el in e.childNodes:     
            if el.nodeName == "error":
                
                el_tid = el.getElementsByTagName("tid")
                tid = el_tid[0].childNodes[0].data	
                tid_node = tid_table.get(int(tid))
                if not tid_node:
                   msg = "Thread " + el_tid[0].childNodes[0].data
                   iter = store.append(None, [msg, None, None, None, -1])
                   tid_table[int(tid)] = iter
                parse_error(store, el, iter, el_tid)

def button_open_cb(button, data):
    file_chooser = gtk.FileChooserDialog(title="Select valgrind XML output", action=gtk.FILE_CHOOSER_ACTION_OPEN,     buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))


    response = file_chooser.run()
    if response == gtk.RESPONSE_OK:
        data.clear()
        filename = file_chooser.get_filename()
        parse_output(filename, data)
    file_chooser.destroy()

def button_about_cb(button, data):
    about = gtk.AboutDialog()
    about.set_program_name("Valhalla")
    about.set_copyright("Copyright (C) 2009 Luka Napotnik <luka.napotnik@gmail.com>")
    about.set_version("0.0.2")
    response = about.run()
    about.destroy()

def button_expand_cb(button, tree):
    tree.expand_all()

def button_leaks_cb(button, store):
    print "AAA"
def main():
    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    scrolled = gtk.ScrolledWindow()
    vte_widget = vte.Terminal()
    scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    cell = gtk.CellRendererText()
    tvcolumn = gtk.TreeViewColumn("Message", cell, markup = 0)
    cell_leaks = gtk.CellRendererText()
    tvcolumn_leaks = gtk.TreeViewColumn("Leaked bytes", cell_leaks, markup = 0)
    treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_INT)
    treeview = gtk.TreeView(model = treestore)
    treeview.append_column(tvcolumn)
    #treeview.append_column(tvcolumn_leaks)
    treeview.connect("row-activated", selected_frame_cb, vte_widget)

    scrolled.add(treeview)

    box = gtk.VPaned()
    box.pack1(scrolled);

    box.pack2(vte_widget)
    box.set_position(300)

    button_open = gtk.Button(stock=gtk.STOCK_OPEN)
    button_open.connect("clicked", button_open_cb, treestore)
    button_expand = gtk.Button("Expand All")
    button_expand.connect("clicked", button_expand_cb, treeview)
    button_leaks = gtk.Button("Leak Information");
    button_leaks.connect("clicked", button_leaks_cb, treestore)
    button_about = gtk.Button(stock=gtk.STOCK_ABOUT)
    button_about.connect("clicked", button_about_cb, None)

    button_box = gtk.HBox(False, 2)
    button_box.pack_start(button_open, False, False)
    button_box.pack_start(button_expand, False, False)
    button_box.pack_start(button_leaks, False, False)
    button_box.pack_end(button_about, False, False)
    main_box = gtk.VBox(False, 2)
    main_box.pack_start(button_box, False, False)
    main_box.pack_start(box)
    window.set_default_size(700, 500)
    window.add(main_box)
    window.set_title("Valhalla")
    window.connect("destroy", gtk.main_quit, None)
    window.show_all()

    if len(sys.argv) > 1:
        parse_output(sys.argv[1], treestore)
    gtk.main()

main()
