## mainwindow.py
## 
## Copyright (C) 2008 Stefan Betz <stefan_betz@gmx.net>
##
## 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; version 3 only.
##
## 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.

import pygtk
pygtk.require('2.0')
import gtk
import gtk.glade
import about
import gobject
import string
import gettext
import options
import os
import subprocess
import thread
import threading
import smbtree
import smbclient
import mountwindow
import addwindow
from pyneighborhood import config
from pyneighborhood.misc import sharepath, iconpath, print_debug

_ = gettext.gettext

class MainWindowHosts(threading.Thread):
    """
    Browsing Thread for the MainWindow Host TreeView
    """
    
    def __init__(self, mainwindow, host, iter):
        """
        Constructor
        """
        threading.Thread.__init__(self)
        self.smbclient = smbclient.smbclient()
        self.mainwindow = mainwindow
        self.host = host
        self.iter = iter

    def run(self):
        """
        Run the Browsing for Shares...
        """
        self.smbclient.shares(self.host)
        sharelist = []
        childlist = []
        for share in self.host.shares.keys():
            sharelist.append(share)
        if self.mainwindow.treestore.iter_has_child(self.iter):
            childiter = self.mainwindow.treestore.iter_children(self.iter)
            while childiter != None:
                child = self.mainwindow.treestore.get_value(childiter, self.mainwindow.NAME)
                childlist.append(child)
                if child in sharelist:
                    childiter = self.mainwindow.treestore.iter_next(childiter)
                else:
                    gtk.gdk.threads_enter()
                    self.mainwindow.treestore.remove(childiter)
                    gtk.gdk.threads_leave()
        for share in self.host.shares.keys():
            if not share in childlist:
                print_debug("Appending Share: " + str(share))
                gtk.gdk.threads_enter()
                i = self.mainwindow.treestore.append(self.iter)
                self.mainwindow.treestore.set_value(i, self.mainwindow.ICON, self.mainwindow.SHARE_ICON)
                self.mainwindow.treestore.set_value(i, self.mainwindow.NAME, share)
                self.mainwindow.treestore.set_value(i, self.mainwindow.COMMENT, self.host.shares[share])
                self.mainwindow.treestore.set_value(i, self.mainwindow.IS_SHARE, True)
                gtk.gdk.threads_leave()

class MainWindowBrowser(threading.Thread):
    """
    Browsing Thread for the MainWindow Browser TreeView
    """
    
    def __init__(self, mainwindow):
        """
        Constructor
        """
        threading.Thread.__init__(self)
        self.smbtree = smbtree.smbtree()
        self.mainwindow = mainwindow

    def run(self):
        """
        Run the Browsing for Hosts...
        """
        gtk.gdk.threads_enter()
        self.mainwindow.reload_browser_button.set_sensitive(False)
        gtk.gdk.threads_leave()
        for host in self.smbtree.hosts():
            if host.workgroup == "":
                print_debug("Throwing away %s, because no Workgroup detected!" % ( host ))
                continue
            parent = None
            for row in self.mainwindow.treestore:
                if row[self.mainwindow.NAME] == host.workgroup:
                    parent = row
                    break
            if parent == None:
                # Add the Workgroup:
                gtk.gdk.threads_enter()
                i = self.mainwindow.treestore.append(None)
                print_debug("Append Workgroup: " + host.workgroup)
                self.mainwindow.treestore.set_value(i, self.mainwindow.NAME, host.workgroup)
                self.mainwindow.treestore.set_value(i, self.mainwindow.ICON, self.mainwindow.GROUP_ICON)
                self.mainwindow.treestore.set_value(i, self.mainwindow.IS_WORKGROUP, True)
                gtk.gdk.threads_leave()
                # Add the frist host per Workgroup:                                       
                for subrow in self.mainwindow.treestore:
                    if subrow[self.mainwindow.NAME] == host.workgroup:
                        print_debug("Append Host: " + host.name)
                        gtk.gdk.threads_enter()
                        i = self.mainwindow.treestore.append(subrow.iter)
                        self.mainwindow.treestore.set_value(i, self.mainwindow.NAME, host.name)
                        self.mainwindow.treestore.set_value(i, self.mainwindow.ICON, self.mainwindow.HOST_ICON)
                        self.mainwindow.treestore.set_value(i, self.mainwindow.COMMENT, host.comment)
                        self.mainwindow.treestore.set_value(i, self.mainwindow.IS_HOST, True)
                        gtk.gdk.threads_leave()
                        MainWindowHosts(self.mainwindow, host, i).start()
                        if self.mainwindow.host_objects.has_key(host.workgroup):
                            self.mainwindow.host_objects[host.workgroup][host.name] = host
                        else:
                            self.mainwindow.host_objects[host.workgroup] = {}
                            self.mainwindow.host_objects[host.workgroup][host.name] = host
            else:
                # Add the other Host to the Workgroup:
                for each in parent.iterchildren():
                    if each[self.mainwindow.NAME] == host.name:
                        break
                else:
                    print_debug("Append Host: " + host.name)
                    gtk.gdk.threads_enter()
                    i = self.mainwindow.treestore.append(parent.iter)
                    self.mainwindow.treestore.set_value(i, self.mainwindow.NAME, host.name)
                    self.mainwindow.treestore.set_value(i, self.mainwindow.ICON, self.mainwindow.HOST_ICON)
                    self.mainwindow.treestore.set_value(i, self.mainwindow.COMMENT, host.comment)
                    self.mainwindow.treestore.set_value(i, self.mainwindow.IS_HOST, True)
                    gtk.gdk.threads_leave()
                    MainWindowHosts(self.mainwindow, host, i).start()
                    if self.mainwindow.host_objects.has_key(host.workgroup):
                        self.mainwindow.host_objects[host.workgroup][host.name] = host
                    else:
                        self.mainwindow.host_objects[host.workgroup] = {}
                        self.mainwindow.host_objects[host.workgroup][host.name] = host
                    parent = None
                    continue
        gtk.gdk.threads_enter()
        self.mainwindow.reload_browser_button.set_sensitive(True)
        gtk.gdk.threads_leave()

class MainWindow(object):
    """
    Base Class of the MainWindow Widget
    """

    def delete_event(self, event=None, widget=None):
        """
        Delete Window Event
        """
        gtk.main_quit()
        return True

    def about(self, action):
        """
        Show the About Dialog
        """
        print_debug("Creating About Dialog")
        about.AboutDialog()

    def preferences(self, action):
        """
        Show the Preferences Dialog
        """
        print_debug("Creating Options Dialog")
        prefs = options.OptionsDialog(self.window)

    def reload_browser(self, event=None, widget=None):
        """
        Reloades the Browser TreeView
        """
        print_debug("Reloading Browsing View...")
        try:
            if self.browser_thread.isAlive() == False:
                print_debug("...OK")
                self.treestore.clear()
                self.browser_thread = MainWindowBrowser(self)
                self.browser_thread.start()
            else:
                print_debug("...already running!")
        except AttributeError:
            print_debug("...OK")
            self.browser_thread = MainWindowBrowser(self)
            self.browser_thread.start()

    def reload_mounts(self, event=None, widget=None):
        """
        Reloades the Mounts TreeView
        """
        self.reload_mounts_button.set_sensitive(False)
        for each in self.mountsstore:
            del self.mountsstore[each.iter]
        f = open("/proc/mounts", "r")
        mtab = f.readlines()
        f.close()
        for mount in mtab:
            tmount = mount.split()
            if tmount[2] == "cifs":
                servicetype = "CIFS"
            elif tmount[2] == "smbfs":
                servicetype = "SMB"
            else:
                servicetype = "?"
            if servicetype == "?":
                continue
            self.mountsstore.append(None, [servicetype,tmount[0].replace("\\040", " "),tmount[1].replace("\\040", " ")])
        self.reload_mounts_button.set_sensitive(True)

    def mounts_recv_data(self, treeview=None, context=None, x=None, y=None, selection=None, info=None, timestamp=None):
        """
        Mounts TreeView Handler for Drag and Drop Operations, Receive
        """
        iter = self.treestore.get_iter_from_string(selection.data)
        self.connect_share(iter=iter)
        treeview.emit_stop_by_name("drag_data_received")
        return

    def browser_send_data(self, treeview=None, context=None, selection=None, info=None, timestamp=None):
        """
        Browser TreeView Handler for Drag and Drop Operations, Transmit
        """
        treeselection = treeview.get_selection()
        model, iter = treeselection.get_selected()
        data = model.get_string_from_iter(iter)
        selection.set_text(data, -1)
        return

    def disconnect_share(self, widget):
        """
        Share Disconnect Callback
        """
        selection = self.mounts.get_selection()
        model, iter = selection.get_selected()
        mountpoint = self.mountsstore.get_value(iter, 2).rstrip("/")
        if self.mountsstore.get_value(iter, 0) == "CIFS":
            type = config.get("CIFS", "unmount")
        else:
            type = config.get("SMB", "unmount")
        self.mountsstore.remove(iter)
        self.disconnectbutton.set_sensitive(False)
        disconnectcommand = []
        if config.get("Main", "enable_sudo") == "True":
            disconnectcommand.append(config.get("Main", "sudo"))
        disconnectcommand.append(type)
        disconnectcommand.append(mountpoint)
        print_debug("Executing Unmount Command: " + str(disconnectcommand))
        process = subprocess.Popen(disconnectcommand, executable = disconnectcommand[0])

    def connect_share(self, widget = None, iter = None):
        """
        Share Connect Callback
        """
        if widget:
            selection = self.browser.get_selection()
            if selection.get_mode() == gtk.SELECTION_SINGLE:
                model, iter = selection.get_selected()
        if iter:
            if self.treestore.get_value(iter, self.IS_SHARE):
                hostiter = self.treestore.iter_parent(iter)
                workgroupiter = self.treestore.iter_parent(hostiter)
                mounter = mountwindow.MountWindow(self,
                    self.host_objects[self.treestore.get_value(workgroupiter, self.NAME)][self.treestore.get_value(hostiter, self.NAME)],
                    self.treestore.get_value(iter, self.NAME),
                    iter)

    def add(self, action=None):
        """
        Handler for the Add-Button
        """
        addwindow.AddWindow(self)

    def mounts_cursor_changed(self, treeview):
        """
        Callback for cursor-changed Events
        """
        selection = treeview.get_selection()
        if selection.get_mode() == gtk.SELECTION_SINGLE:
            self.disconnectbutton.set_sensitive(True)
        else:
            self.disconnectbutton.set_sensitive(False)

    def browser_cursor_changed(self, treeview):
        """
        Callback for cursor-changed Events
        """
        self.connectbutton.set_sensitive(False)
        selection = treeview.get_selection()
        if selection.get_mode() == gtk.SELECTION_SINGLE:
            model, iter = selection.get_selected()
            if iter:
                if self.treestore.get_value(iter, self.IS_SHARE):
                    self.connectbutton.set_sensitive(True)

    def __init__(self):
        """
        Constructor
        """
        # Create the Main Window
        self.xml = gtk.glade.XML(sharepath() + "/pyNeighborhood.glade", "mainwindow")
        self.xml.signal_autoconnect(self)
        self.window = self.xml.get_widget("mainwindow")
        self.window.set_icon_from_file(os.path.join(iconpath(), 'pyneighborhood.png'))
        # Main TreeStore
        self.treestore = gtk.TreeStore(str, gtk.gdk.Pixbuf, str, str, bool, bool, bool)
        self.NAME = 0
        self.ICON = 1
        self.COMMENT = 2
        self.MOUNTPOINT = 3
        self.IS_HOST = 4
        self.IS_SHARE = 5
        self.IS_WORKGROUP = 6
        self.HOST_ICON = gtk.gdk.pixbuf_new_from_file(os.path.join(iconpath(), "host.png"))
        self.GROUP_ICON = gtk.gdk.pixbuf_new_from_file(os.path.join(iconpath(), "workgroup.png"))
        self.SHARE_ICON = gtk.gdk.pixbuf_new_from_file(os.path.join(iconpath(), "share.png"))
        # Browser TreeView
        self.browser = self.xml.get_widget("browser")
        self.browser.set_model(self.treestore)
        self.browserimagecell = gtk.CellRendererPixbuf()
        self.browsertextcell = gtk.CellRendererText()
        self.browsercolumn = gtk.TreeViewColumn()
        self.browsercolumn.pack_start(self.browserimagecell, False)
        self.browsercolumn.add_attribute(self.browserimagecell, "pixbuf", self.ICON)
        self.browsercolumn.pack_start(self.browsertextcell, True)
        self.browsercolumn.add_attribute(self.browsertextcell, "text", self.NAME)
        self.browser.append_column(self.browsercolumn)
        if gtk.check_version(2, 12, 0):
            print_debug("Sorry, Tooltips require at least PyGTK Version 2.12!")
        else:
            self.browser.set_tooltip_column(2)
        self.browser.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, [("text/plain", gtk.TARGET_SAME_APP, 0)], gtk.gdk.ACTION_COPY)
        # Connections Buttons
        self.disconnectbutton = self.xml.get_widget("disconnect-button")
        self.disconnectbutton.set_sensitive(False)
        self.connectbutton = self.xml.get_widget("connect-button")
        self.connectbutton.set_sensitive(False)
        # Mounts TreeView
        self.mounts = self.xml.get_widget("mounts")
        self.mountsstore = gtk.TreeStore(str, str, str)
        self.mounts.set_model(self.mountsstore)
        self.mountstypecell = gtk.CellRendererText()
        self.mountstypecolumn = gtk.TreeViewColumn(_("Type"), self.mountstypecell, text=0)
        self.mountshostcell = gtk.CellRendererText()
        self.mountshostcolumn = gtk.TreeViewColumn(_("Service"), self.mountshostcell, text=1)
        self.mountsmountpointcell = gtk.CellRendererText()
        self.mountsmountpointcolumn = gtk.TreeViewColumn(_("Mountpoint"), self.mountsmountpointcell, text=2)
        self.mounts.append_column(self.mountstypecolumn)
        self.mounts.append_column(self.mountshostcolumn)
        self.mounts.append_column(self.mountsmountpointcolumn)
        # Reload Buttons
        self.reload_browser_button = self.xml.get_widget("reload-browser-button")
        self.reload_mounts_button = self.xml.get_widget("reload-mounts-button")
        # Drag & Drop for Mounts:
        self.mounts.enable_model_drag_dest([("text/plain", gtk.TARGET_SAME_APP, 0)], gtk.gdk.ACTION_COPY)
        # Host Objects Dict
        self.host_objects = {}
        # Autostart Scanning and Mounts View
        self.reload_browser()
        self.reload_mounts()
