#!/usr/bin/python

import gobject
import gtk
import pango

import gc
import os
from stat import *
import datetime
import getopt
import sys
import traceback

# for address
import socket
import fcntl
import struct

import time
import signal
import Queue

import textwrap

# localization
import gettext
try:
    gettext.install("msec")
except IOError:
    _ = str

from net_monitor import Monitor

ifaces = {}
HISTOGRAM_SIZE=50

# borrowed from gnome-network-monitor 0.9.1 (gnetworkmonitor.sourceforge.net)
class LoadGraph:
    """
    This class is able do display a nicely formatted graph if interface
    bandwidth load.
    """
    # we don't want to allow the window to shrink below this height
    padding = { "left" : 50, "right" : 10, "top" : 10, "bottom" : 10 }
    colors  = ( "bg", "bg_outer", "in", "out" )

    def __init__(self, widget, hist, size,
            min_height=70, axes_text=_("Bytes"), draw_both=True, max=None,
            draw_legend = True, legend1=_("Upload"), legend2=_("Download")):
        """
        widget   => GtkDrawingArea we paint into
        hist     => a list of integers containing the hist of incoming traffic
        width, height => initial size of the GtkDrawingArea widget
        """
        # set the minimum height of the widget
        widget.set_size_request(-1, min_height)

        # the object holding the history and its size
        self.__hist = hist
        self.__size = size

        # strings holding 0, middle and max values for displayed graph
        self.__str_min = "0"
        self.__str_mid = ""     # gets computed later
        self.__str_max = ""     # gets computer later

        # axes descriptions
        self.axes_text = axes_text
        self.draw_legend = draw_legend
        self.legend1 = legend1
        self.legend2 = legend2

        # are we drawing both values?
        self.draw_both = draw_both

        # minimum and maximum values
        self.min = min
        self.max = max

        # size of the GtkDrawingArea
        self.__rect = self.__inner = gtk.gdk.Rectangle()
        self.maxval = 0                     # maximum value in the history
        self.__mesh_x = self.__mesh_y = 0   # distance in pixels between items
        self.__get_max()
        self.__on_size(widget.get_allocation())

        # save reference to the widget we paint into
        self.__widget = widget

        # lists holding bandwidth history mapped to actual coordinates
        self.__in  = list()
        self.__out = list()

        self.__colors = dict()
        self.set_color(bg=(0, 0, 0), fg_in=(255, 0, 0), fg_out=(0, 255, 0))
        self.__context = None

    def __set_context_color(self, con, col_tuple):
        """ Cleaner might be to extend the context class, but who cares?  """
        con.set_source_rgb(col_tuple[0], col_tuple[1], col_tuple[2])

    def __draw(self):
        """ Strokes the rectangles and draws the curves """
        if (self.__context == None):
            return
        # stroke the outer rectangle
        self.__context.rectangle(0, 0, self.__rect.width, self.__rect.height)
        self.__set_context_color(self.__context, self.__colors["bg"])
        self.__context.fill_preserve()
        self.__context.stroke()

        # stroke the inner rectangle
        self.__context.rectangle(self.__inner.x,
                                 self.__inner.y,
                                 self.__inner.width,
                                 self.__inner.height)
        self.__set_context_color(self.__context, self.__colors["bg"])
        self.__context.fill_preserve()
        self.__context.stroke()

        # stroke the quad around
        self.__context.move_to(self.__inner.x, self.__inner.y + self.__inner.height)
        self.__context.line_to(self.__inner.x, self.__inner.y)
        self.__context.line_to(self.__inner.x + self.__inner.width - self.__mesh_x, self.__inner.y)
        self.__context.line_to(self.__inner.x + self.__inner.width - self.__mesh_x, self.__inner.y + self.__inner.height)
        self.__set_context_color(self.__context, (255, 255, 255))
        self.__context.stroke()

        # draw the actual bandwidth curves
        self.__draw_bw(self.__in, self.__colors["fg_in"])
        if self.draw_both:
                self.__draw_bw(self.__out, self.__colors["fg_out"])


        # draw legend
        if self.draw_legend:
            self.__draw_legend(self.__inner.y + 15, self.legend1, self.__colors["fg_in"])
            if self.draw_both:
                self.__draw_legend(self.__inner.y + 25, self.legend2, self.__colors["fg_out"])

        # draw minimum, middle and max numbers
        self.__draw_num(self.__inner.height + self.__inner.y, self.__str_min, (255, 255, 255))
        self.__draw_num(self.__rect.height/2, self.__str_mid, (255, 255, 255))
        self.__draw_num(self.__inner.y, self.__str_max, (255, 255, 255))

    def __draw_num(self, ypos, num, color):
        """
        The leftmost column is used to draw info about maximum, minimum
        and average bw
        """
        self.__set_context_color(self.__context, color)
        self.__context.move_to(5, ypos)
        self.__context.show_text(num)
        self.__context.stroke()

    def __draw_legend(self, ypos, text, color):
        """
        The rightmost column is used to draw graph legend
        """
        text_length = self.__context.text_extents(text)[2]
        self.__context.move_to(self.__inner.x + self.__inner.width - text_length - 20, ypos)
        self.__context.show_text(text)
        self.__set_context_color(self.__context, color)
        self.__context.stroke()

    def __draw_bw(self, bw_list, color):
        """ Draws a curve from points stored in bw_list in color """
        self.__context.move_to(self.__inner.x, self.__inner.y + self.__inner.height)
        self.__set_context_color(self.__context, color)

        x = self.__inner.x + self.__mesh_x
        for i in bw_list[1:]:
            self.__context.line_to(x, i)
            x += self.__mesh_x
        self.__context.stroke()

    def __convert_one_hist(self, hist):
        """
        Maps values from one history object to real coordinates of the
        drawing area
        """
        converted = list()

        if self.__mesh_y == 0:
            return [self.__inner.height + self.__inner.y] * len(hist)

        for item in hist:
            converted.append((self.__inner.height - int(item / self.__mesh_y)) + self.__inner.y)
        return converted

    def __convert_points(self):
        """
        The bandwidth history object has the bandwidth stored as bytes. This method
        converts the bytes into actual coordiantes of the rectangle displayed
        """
        # compute the aspect ratio
        self.__mesh_x = float(self.__inner.width) / float(self.__size)
        self.__mesh_y = float(self.maxval) / float(self.__inner.height)

        self.__in  = self.__convert_one_hist(self.__hist["in"])
        self.__out = self.__convert_one_hist(self.__hist["out"])

    def __get_max(self):
        """ Finds the maximum value in both incoming and outgoing queue  """
        if self.max != None:
                self.maxval = self.max
        else:
                if self.__hist["in"]:
                    maxin = max(self.__hist["in"])
                else:
                    maxin = 0
                if self.__hist["out"]:
                    maxout = max(self.__hist["out"])
                else:
                    maxout = 0
                self.maxval = max(maxin, maxout)

    def __text_size(self):
        """ Computes the size of the text and thus the left border """
        val = self.maxval
        if val == 0 and self.maxval != 0:
            val = self.maxval

        self.__str_max = "%d %s" % (val, self.axes_text)
        if val != val/2:
            self.__str_mid = "%d %s" % (val/2, self.axes_text)
        else:
            self.__str_mid = ""
        LoadGraph.padding["left"] = self.__context.text_extents(self.__str_max)[2] + 10

    def __on_size(self, rect):
        """ rect => a rectangle holding the size of the widget """
        self.__rect  = rect

        self.__inner.x = LoadGraph.padding["left"]
        self.__inner.y = LoadGraph.padding["top"]
        self.__inner.width = rect.width - LoadGraph.padding["right"] - self.__inner.x
        self.__inner.height = rect.height - LoadGraph.padding["bottom"] - self.__inner.y

        self.__convert_points()

    def on_expose(self, widget, event):
        """ A signal handler that is called every time we need to redraw
        the widget """
        self.__context = widget.window.cairo_create()

        self.__get_max()
        self.__text_size()
        self.__on_size(widget.get_allocation())

        self.__draw()

        return False

    def set_history(self, hist):
        """ Called typically on change of interface displayed """
        self.__hist = hist
        self.__convert_points()

    def update(self):
        """ Redraws the area """
        alloc = self.__widget.get_allocation()
        self.__widget.queue_draw_area(0, 0, alloc.width, alloc.height)

    def change_colors(self, col_in = None, col_out = None):
        """ Sets the colors to draw the curves with """
        if ( col_in )  : self.__colors["fg_in"] = col_in
        if ( col_out ) : self.__colors["fg_out"] = col_out

    def set_color(self, *args, **kwargs):
        """ Sets the colors of the graph """
        for key, value in kwargs.items():
            self.__colors[key] = value

class MonitorGui:
    # icon pattern - icons are pulled from drakx-net
    ICON_PATTERN="/usr/share/libDrakX/pixmaps/%s-16.png"
    ICON_CONNECTED="/usr/share/libDrakX/pixmaps/connected.png"
    (COLUMN_PROTO, COLUMN_LOC_ADDR, COLUMN_LOC_PORT, COLUMN_REM_ADDR, COLUMN_REM_PORT, COLUMN_STATUS) = range(6)
    def __init__(self, default_iface=None):
        self.window = gtk.Window()
        self.window.set_title(_("Network monitor"))
        self.window.set_default_size(640, 440)
        self.window.connect('delete-event', lambda *w: gtk.main_quit())

        self.main_vbox = gtk.VBox()
        self.window.add(self.main_vbox)

        # notebook
        self.notebook = gtk.Notebook()
        self.main_vbox.pack_start(self.notebook)
        #self.notebook.connect('switch-page', self.show_net_status)

        # monitor
        self.monitor = Monitor()

        self.ifaces = self.monitor.readnet()
        self.enabled_ifaces = []
        self.wireless_ifaces = filter(self.monitor.has_wireless, self.ifaces.keys())

        # load uptime log
        self.monitor.load_uptime_log()

        sorted_ifaces = self.ifaces.keys()
        sorted_ifaces.sort()

        net=self.monitor.readnet()
        select_page=0
        for iface in sorted_ifaces:
            device_exists, data_in, data_out = self.monitor.get_traffic(iface,net)
            self.ifaces[iface] = {'data_in': 0,
                              'data_out': 0,
                              'total_in': 0,
                              'total_out': 0,
                              'widget_in': None,
                              'widget_out': None,
                              'widget_speed_in': None,
                              'widget_speed_out': None,
                              'graph': None,
                              'histogram': [],
                              'address': "",
                              }
            iface_stat, iface_label = self.build_iface_stat(iface)
            cur_page = self.notebook.append_page(iface_stat, iface_label)
            if default_iface and iface == default_iface:
                select_page = cur_page
            if self.check_network_accounting(iface):
                self.enabled_ifaces.append(iface)

        # finally, we have tabs for network connections
        network_stat_label, network_stat, self.connections = self.build_network_stat()
        # refresh current connections
        self.refresh_connections(None)

        cur_page = self.notebook.append_page(network_stat, network_stat_label)

        # global statusbar
        self.statusbar = gtk.Statusbar()
        self.context_id = self.statusbar.get_context_id("Statusbar")
        self.main_vbox.pack_start(self.statusbar, False, False, padding=1)

        # add initial message
        self.statusbar.push(self.context_id, _("Please wait.."))

        # configure timer
        self.signals = Queue.Queue()
        gobject.timeout_add_seconds(1, self.update)

        self.window.show_all()

        # do we have to select a default interface?
        if select_page:
            self.notebook.set_current_page(select_page)

    def update_tray_info(self):
        """Updates tray information"""
        dns_servers = self.monitor.get_dns()
        if dns_servers:
            dns_message = ", ".join(dns_servers)
        else:
            dns_message = _("Not found")
        # routes
        routes, default_routes = self.monitor.get_routes()
        if default_routes:
            gw_message = ", ".join(["%s (%s)" % (gw, iface) for gw, iface in default_routes])
        else:
            gw_message = _("Not found")
        tray_message = _("Default routes: %s; DNS: %s") % (gw_message, dns_message)
        self.statusbar.pop(self.context_id)
        self.statusbar.push(self.context_id, tray_message)

    def update(self, interval=1):
        """Updates traffic counters (interval is in seconds)"""
        # check for pending signals
        self.check_signals()
        # TODO: move it to Monitor()?
        net=self.monitor.readnet()
        wifi_stats = self.monitor.wireless_stats()
        self.update_tray_info()
        for iface in self.ifaces:
            status = self.monitor.get_status(iface)
            old_data_in = self.ifaces[iface]['data_in']
            old_data_out = self.ifaces[iface]['data_out']
            total_in = self.ifaces[iface]['total_in']
            total_out = self.ifaces[iface]['total_out']
            device_exists, data_in, data_out = self.monitor.get_traffic(iface, net)
            # is it a wireless interface?
            if iface in self.wireless_ifaces:
                essid = self.monitor.wifi_get_essid(iface)
                mode = self.monitor.wifi_get_mode(iface)
                bitrate = self.monitor.wifi_get_bitrate(iface)
                ap = self.monitor.wifi_get_ap(iface)
                link = wifi_stats.get(iface, 0)
                # calculate link quality
                if "max_quality" in self.ifaces[iface]:
                    max_quality = self.ifaces[iface]["max_quality"]
                    if max_quality != 0:
                        quality = link * 100.0 / max_quality
                    else:
                        quality = 0
                else:
                    quality = 0
            else:
                essid = None
                mode = None
                bitrate = None
                ap = None
                quality = 0
            # is it the first measure?
            if old_data_in == 0 and old_data_out == 0:
                old_data_in = data_in
                old_data_out = data_out
            # check if device exists
            if not device_exists:
                old_data_in = data_in
                old_data_out = data_out
            # check total download
            diff_in = data_in - old_data_in
            diff_out = data_out - old_data_out
            # checking for 32bits overflow
            if diff_in < 0:
                diff_in += 2**32
            if diff_out < 0:
                diff_out += 2**32
            total_in += diff_in
            total_out += diff_out
            # speed
            speed_in = diff_in / interval
            speed_out = diff_out / interval
            # update saved values
            self.ifaces[iface]['data_in'] = data_in
            self.ifaces[iface]['data_out'] = data_out
            self.ifaces[iface]['total_in'] = total_in
            self.ifaces[iface]['total_out'] = total_out
            # updating graphs
            for histogram, graph, value_in, value_out in [
                                ('histogram', 'graph', speed_in, speed_out),
                                ('link_histogram', 'link_graph', quality, quality)
                                ]:
                if histogram not in self.ifaces[iface] or graph not in self.ifaces[iface]:
                    # skip invalid graphs
                    continue
                # calculating histogram
                hist_in = self.ifaces[iface][histogram]['in']
                hist_out = self.ifaces[iface][histogram]['out']
                if hist_in:
                    histo_in = reduce(lambda x, y: x+y, hist_in) / len(hist_in)
                else:
                    histo_in = 0
                if hist_out:
                    histo_out = reduce(lambda x, y: x+y, hist_out) / len(hist_in)
                else:
                    histo_out = 0
                hist_in.append(value_in)
                if len(hist_in) > HISTOGRAM_SIZE:
                    del hist_in[0]
                hist_out.append(value_out)
                if len(hist_out) > HISTOGRAM_SIZE:
                    del hist_out[0]
                graph = self.ifaces[iface][graph]
                graph.update()
            # calculate average network traffic
            hist_in = self.ifaces[iface]['histogram']['in']
            hist_out = self.ifaces[iface]['histogram']['out']
            if hist_out:
                histo_out = reduce(lambda x, y: x+y, hist_out) / len(hist_in)
            else:
                histo_out = 0
            # get the uptime
            uptime = self.monitor.get_uptime(iface)
            # update widgets
            ip, mac = self.monitor.get_address(iface)
            for widget, value in [('widget_in', self.monitor.format_size(total_in)),
                                  ('widget_out', self.monitor.format_size(total_out)),
                                  ('widget_speed_in', self.monitor.format_size(speed_in, "/s")),
                                  ('widget_speed_out', self.monitor.format_size(speed_out, "/s")),
                                  ('widget_ip_address', ip),
                                  ('widget_status', status),
                                  ('widget_hw_address', mac),
                                  ('widget_essid', essid),
                                  ('widget_mode', mode),
                                  ('widget_bitrate', bitrate),
                                  ('widget_ap', ap),
                                  ('quality', "%d%%" % quality),
                                  ('widget_uptime', uptime),
                                  ]:
                if widget in self.ifaces[iface]:
                    # is it absolute value or pretty-formatted number?
                    if value.__class__ == tuple:
                        pretty_size, pretty_bytes = value
                        if pretty_size == pretty_bytes:
                            value = pretty_size
                        else:
                            value = "%s (%s)" % (pretty_size, pretty_bytes)
                    self.ifaces[iface][widget].set_text(str(value))
        gobject.timeout_add_seconds(interval, self.update)

    def check_network_accounting(self, iface):
        """Checks if network accounting was enabled on interface"""
        try:
            os.stat("/var/lib/vnstat/%s" % iface)
            return True
        except:
            return False

    def show_statistics_dialog(self, widget, iface):
        """Shows statistics dialog"""
        dialog = gtk.Dialog(_("Network statistics for %s") % iface,
                self.window, 0,
                (gtk.STOCK_OK, gtk.RESPONSE_OK)
                )
        # statistics vbox
        stats_vbox = dialog.vbox
        if self.check_network_accounting(iface):
            # graph
            graph_vnstat = gtk.Image()
            pixbuf = self.load_graph_from_vnstat(iface, type="summary")
            graph_vnstat.set_from_pixbuf(pixbuf)
            stats_vbox.pack_start(graph_vnstat)
            # buttons
            frame = gtk.Frame(_("Network traffic statistics for %s") % iface)
            stats_vbox.add(frame)
            vbox = gtk.VBox()
            frame.add(vbox)
            # summary
            button = gtk.RadioButton(None, _("Summary"))
            button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "summary"))
            vbox.pack_start(button, False, False)
            # summary
            button = gtk.RadioButton(button, _("Hourly traffic"))
            button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "hourly"))
            vbox.pack_start(button, False, False)
            # summary
            button = gtk.RadioButton(button, _("Daily traffic"))
            button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "daily"))
            vbox.pack_start(button, False, False)
            # summary
            button = gtk.RadioButton(button, _("Monthly traffic"))
            button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "monthly"))
            vbox.pack_start(button, False, False)
            # summary
            button = gtk.RadioButton(button, _("Top 10 traffic days"))
            button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "top"))
            vbox.pack_start(button, False, False)
        else:
            label = gtk.Label(_("Network accounting was not enabled on interface %s.\nPlease enable network accounting on the interface in order to view traffic statistics and restart your network connection to start collecting traffic statistics."))
            stats_vbox.add(label)

        stats_vbox.show_all()
        ret = dialog.run()
        dialog.destroy()

    def refresh_connections(self, widget):
        """Updates connections"""
        lstore = self.connections
        lstore.clear()
        for proto in ["tcp", "udp"]:
            connections = self.monitor.get_connections(proto=proto)
            for loc_addr, loc_port, rem_addr, rem_port, status in connections:
                iter = lstore.append()
                lstore.set(iter,
                        self.COLUMN_PROTO, proto,
                        self.COLUMN_LOC_ADDR, loc_addr,
                        self.COLUMN_LOC_PORT, loc_port,
                        self.COLUMN_REM_ADDR, rem_addr,
                        self.COLUMN_REM_PORT, rem_port,
                        self.COLUMN_STATUS, status)


    def build_network_stat(self):
        """Builds graphical view for connections"""
        vbox = gtk.VBox()
        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        lstore = gtk.ListStore(
                gobject.TYPE_STRING,
                gobject.TYPE_STRING,
                gobject.TYPE_INT,
                gobject.TYPE_STRING,
                gobject.TYPE_INT,
                gobject.TYPE_STRING
                )
        treeview = gtk.TreeView(lstore)
        treeview.set_rules_hint(True)
        treeview.set_search_column(self.COLUMN_LOC_ADDR)

        # treeview.connect('row-activated', self.expand_domain, lstore)

        # now building columns
        for c, descr in [
                (self.COLUMN_PROTO, _("Protocol")),
                (self.COLUMN_LOC_ADDR, _("Local address")),
                (self.COLUMN_LOC_PORT, _("Local port")),
                (self.COLUMN_REM_ADDR, _("Remote address")),
                (self.COLUMN_REM_PORT, _("Remote port")),
                (self.COLUMN_STATUS, _("Connection status")) ]:
            renderer = gtk.CellRendererText()
            column = gtk.TreeViewColumn(descr, renderer, text=c)
            column.set_sort_column_id(c)
            column.set_resizable(True)
            column.set_expand(True)
            treeview.append_column(column)

        sw.add(treeview)

        # build tab label
        widget = gtk.HBox()
        try:
            icon = gtk.Image()
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.ICON_CONNECTED)
            icon.set_from_pixbuf(pixbuf)
            widget.pack_start(icon)
        except:
            traceback.print_exc()
        widget.pack_start(gtk.Label(_("connections")))
        widget.show_all()

        vbox.pack_start(sw)

        button = gtk.Button(_("Refresh"))
        button.connect('clicked', self.refresh_connections)
        vbox.pack_start(button, False, False)

        return widget, vbox, lstore

    def build_iface_stat(self, iface):
        """Builds graphical view for interface"""
        traf_vbox = gtk.VBox(spacing=5)
        # graph
        draw = gtk.DrawingArea()
        traf_vbox.pack_start(draw)
        histogram = {"in": [], "out": []}
        graph = LoadGraph(draw, histogram, HISTOGRAM_SIZE)
        draw.connect('expose_event', graph.on_expose)
        self.ifaces[iface]['graph'] = graph
        self.ifaces[iface]['histogram'] = histogram

        # configuring callbacks
        sizegroup1 = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
        sizegroup2 = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
        sizegroup3 = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
        sizegroup4 = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        # traffic
        frame = gtk.Frame(_("Traffic statistics"))
        traf_vbox.pack_start(frame, False, False)

        table = gtk.Table(2, 2, False)
        frame.add(table)
        cur_row = 0

        total_in_h, total_in = self.build_value_pair(sizegroup1, _("Downloaded:"), value_sizegroup=sizegroup2)
        self.ifaces[iface]["widget_in"] = total_in
        total_out_h, total_out = self.build_value_pair(sizegroup3, _("Uploaded:"), value_sizegroup=sizegroup4)
        self.ifaces[iface]["widget_out"] = total_out

        # speed
        speed_in_h, speed_in = self.build_value_pair(sizegroup1, _("Download speed:"), value_sizegroup=sizegroup2)
        self.ifaces[iface]["widget_speed_in"] = speed_in
        speed_out_h, speed_out = self.build_value_pair(sizegroup3, _("Upload speed:"), value_sizegroup=sizegroup4)
        self.ifaces[iface]["widget_speed_out"] = speed_out

        # pack items into table
        for items in [
                        [total_in_h, total_out_h],
                        [speed_in_h, speed_out_h],
                        ]:
            self.__add_row(table, cur_row, items)
            cur_row += 1

        frame_global = gtk.Frame(_("Interface settings"))
        traf_vbox.pack_start(frame_global, False, False)

        table = gtk.Table(2, 2, False)
        frame_global.add(table)
        cur_row = 0

        # interface
        iface_h, iface_p = self.build_value_pair(sizegroup1, _("Network interface:"), iface, value_sizegroup=sizegroup2)
        iface_s, iface_status = self.build_value_pair(sizegroup1, _("Device status:"), value_sizegroup=sizegroup2)
        self.ifaces[iface]["widget_status"] = iface_status
        iface_addr_s, iface_addr = self.build_value_pair(sizegroup3, _("IP Address:"), value_sizegroup=sizegroup4)
        self.ifaces[iface]["widget_ip_address"] = iface_addr
        iface_mac_s, iface_mac = self.build_value_pair(sizegroup3, _("Hardware address:"), value_sizegroup=sizegroup4)
        self.ifaces[iface]["widget_hw_address"] = iface_mac

        # pack items into table
        for items in [
                        [iface_h, iface_addr_s],
                        [iface_s, iface_mac_s],
                        ]:
            self.__add_row(table, cur_row, items)
            cur_row += 1

        # wireless statistics
        if iface in self.wireless_ifaces:
            # essid
            essid_h, essid = self.build_value_pair(sizegroup1, _("Wireless ESSID:"), value_sizegroup=sizegroup2)
            self.ifaces[iface]["widget_essid"] = essid
            # mode
            mode_h, mode = self.build_value_pair(sizegroup3, _("Wireless mode:"), value_sizegroup=sizegroup4)
            self.ifaces[iface]["widget_mode"] = mode

            # bitrate
            bitrate_h, bitrate = self.build_value_pair(sizegroup1, _("Connection speed:"), value_sizegroup=sizegroup2)
            self.ifaces[iface]["widget_bitrate"] = bitrate
            # AP
            ap_h, ap = self.build_value_pair(sizegroup3, _("Access point or cell:"), value_sizegroup=sizegroup4)
            self.ifaces[iface]["widget_ap"] = ap

            # caching quality values
            self.ifaces[iface]["max_quality"] = self.monitor.wifi_get_max_quality(iface)

            # link quality info
            quality_h, quality = self.build_value_pair(sizegroup1, _("Link quality:"), value_sizegroup=sizegroup2)
            self.ifaces[iface]["quality"] = quality

            # link quality graph
            draw = gtk.DrawingArea()
            histogram = {"in": [], "out": []}
            graph = LoadGraph(draw, histogram, HISTOGRAM_SIZE, min_height=40, axes_text="%", draw_both=False, max=100, draw_legend=False)
            draw.connect('expose_event', graph.on_expose)
            self.ifaces[iface]['link_graph'] = graph
            self.ifaces[iface]['link_histogram'] = histogram

            # pack everything into table
            for items in [
                            [essid_h, mode_h],
                            [bitrate_h, ap_h],
                            [quality_h, draw],
                            ]:
                self.__add_row(table, cur_row, items)
                cur_row += 1

        # statistics button
        frame_accounting = gtk.Frame(_("Traffic accounting"))
        vbox = gtk.VBox()
        frame_accounting.add(vbox)
        if self.check_network_accounting(iface):
            iface_u, iface_uptime = self.build_value_pair(sizegroup1, _("Connection time:"))
            self.ifaces[iface]["widget_uptime"] = iface_uptime
            vbox.pack_start(iface_u, False, False)
            button = gtk.Button(_("Show detailed network statistics"))
            button.connect('clicked', self.show_statistics_dialog, iface)
            vbox.pack_start(button, False, False)
        else:
            label = gtk.Label("\n".join(textwrap.wrap(_("Network accounting is not enabled for this interface. Please enable it in Mandriva network center in order to view detailed traffic statistics"))))
            vbox.pack_start(label, False, False)
        traf_vbox.pack_start(frame_accounting, False, False)

        # building notebook label icons
        traf_label = gtk.HBox(False, 2)
        if iface in self.wireless_ifaces:
            # wifi
            self.__load_interface_icon(traf_label, 'wireless')
        elif iface[:3] == 'ppp':
            # modem or peer-to-peer connection
            self.__load_interface_icon(traf_label, 'potsmodem')
        elif iface[:3] == 'eth':
            # ethernet
            self.__load_interface_icon(traf_label, 'ethernet')
        elif iface[:3] == 'pan':
            # ethernet
            self.__load_interface_icon(traf_label, 'bluetooth')

        traf_label.pack_start(gtk.Label(iface))
        traf_label.show_all()

        return traf_vbox, traf_label

    def __load_interface_icon(self, widget, icon_title):
        """Loads interface icon"""
        try:
            icon = gtk.Image()
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.ICON_PATTERN % icon_title)
            icon.set_from_pixbuf(pixbuf)
            widget.pack_start(icon)
        except:
            traceback.print_exc()

    def __add_row(self, table, row, items, markup=False, wrap=False):
        cur_pos = 1
        for item in items:
            table.attach(item, cur_pos - 1, cur_pos, row, row + 1, gtk.EXPAND | gtk.FILL, 0, 0, 0)
            cur_pos += 1

    def build_widget_pair(self, container, w1, w2):
        """Puts two widgets side-by-side"""
        # finding left and right vboxes from container
        vbox_left, vbox_right = container
        vbox_left.pack_start(w1, False, False)
        vbox_right.pack_start(w2, False, False)

    def build_value_pair(self, sizegroup, text, value_text=None, value_sizegroup=None):
        """Builds a value pair"""
        hbox = gtk.HBox(spacing=10)
        name = gtk.Label(text)
        name.set_property("xalign", 0.0)
        hbox.pack_start(name, False, False)
        value = gtk.Label(value_text)
        value.set_property("xalign", 0.0)
        hbox.pack_start(value, False, False)
        if sizegroup:
            sizegroup.add_widget(name)
        if value_sizegroup:
            value_sizegroup.add_widget(value)
        return hbox, value

    def update_stat_iface(self, widget, data):
        """Updates graphic statistics"""
        iface, graph, type = data
        pixbuf = self.load_graph_from_vnstat(iface, type)
        graph.set_from_pixbuf(pixbuf)

    def load_graph_from_vnstat(self, iface, type="hourly"):
        """Loads graph from vnstat. Right now uses vnstati to do all the dirty job"""
        # load image from data
        if type == "hourly":
            param="-h"
        elif type == "monthly":
            param="-m"
        elif type == "daily":
            param="-d"
        elif type == "top":
            param="-t"
        elif type == "summary":
            param="-s"
        else:
            # show summary if parameter is unknown
            print "Unknown parameter %s, showing summary.." % type
            param="-s"
        data = os.popen("vnstati %s -o - -i %s" % (param, iface)).read()
        loader = gtk.gdk.PixbufLoader()
        loader.write(data)
        loader.close()
        pixbuf = loader.get_pixbuf()
        return pixbuf

    def check_signals(self):
        """Checks for received signals"""
        if not self.signals.empty():
            s = self.signals.get()
            if s == signal.SIGHUP:
                # reload network configuration
                self.monitor.load_uptime_log()

    def queue_update(self, s):
        """Queue update for network devices"""
        self.signals.put(s)

# {{{ usage
def usage():
    """Prints help message"""
    print """net_monitor: Mandriva network monitoring tool.

Arguments to net_monitor:
    -h, --help                displays this helpful message.
    -i, --defaultintf <iface> start monitoring the specified interface
"""
# }}}

if __name__ == "__main__":
    # default monitoring interface
    iface = None
    # parse command line
    try:
        opt, args = getopt.getopt(sys.argv[1:], 'hi:', ['help', 'defaultintf='])
    except getopt.error:
        usage()
        sys.exit(1)
    for o in opt:
        if o[0] == '-h' or o[0] == '--help':
            usage()
            sys.exit(0)
        elif o[0] == '-i' or o[0] == '--defaultintf':
            iface = o[1]
    monitor = MonitorGui(default_iface=iface)
    signal.signal(signal.SIGHUP, lambda s, f: monitor.queue_update(s))
    gtk.main()
