#!/usr/bin/python

import gtk
import gtk.glade
import gobject
import os
import sys
import string
import socket
import fcntl
import struct
from popen2 import Popen3
import ltsp
import apt
import apt_pkg

# starts network tool from gnome-system-tools and uses icons from there, needs to depend on it

class LTSPManager:
    def __init__(self):
        self.wTree=gtk.glade.XML ("./ltsp-manager.glade")
        self.win = self.wTree.get_widget("window1")
        self.buildwin = self.wTree.get_widget("build_window")
        self.selectwin = self.wTree.get_widget("select_window")
        self.win.connect("destroy", lambda w: gtk.main_quit())
        close_button = self.wTree.get_widget("close_button")
        close_button.connect("clicked", lambda w: gtk.main_quit())
        help_button = self.wTree.get_widget("help_button")
        help_button.connect("clicked", lambda w: os.system('firefox http://people.ubuntu.com/~ogra/LTSPManager/'))

        self.lines=0

        self.fulfill_prereq()
            
    def initialize(self):
        self.get_devices()
        self.set_toggles()
        self.get_keymaps()
        self.get_kbmodels()
        self.get_serial_protocols()
        self.get_videodrivers()

    def show_mainwin(self, path):
        self.root=path
        self.buildwin.hide()
        self.selectwin.hide()

        self.initialize()
        self.win.show_all()

    def fulfill_prereq(self):
        self.win.hide()
        self.selectwin.hide()
        self.buildwin.hide()
        
        pathlist = self.get_root_path()
        arch = apt_pkg.Config.Find("APT::Architecture")
        
        i=0

        if len(pathlist) > 1:
            self.selectwin.show_all()

            combo = self.wTree.get_widget("chroot_combo")

            select_cancel = self.wTree.get_widget("chroot_cancel_button")
            select_cancel.connect("clicked", lambda w: gtk.main_quit())
            
            select_ok = self.wTree.get_widget("chroot_ok_button")
            select_ok.connect("clicked", lambda w: self.show_mainwin('/opt/ltsp/'+combo.get_active_text()))

            for path in pathlist:
                if path == 'i386' or path == 'powerpc' or path == 'amd64':
                    combo.append_text(path)
                    if path == arch:
                        combo.set_active(i)
                    i=i+1
                
        elif len(pathlist) == 1 and (pathlist[0] == 'i386' or
            pathlist[0] == 'amd64' or pathlist[0] == 'powerpc'):
            self.show_mainwin('/opt/ltsp/'+pathlist[0])
        else:
            combo = self.wTree.get_widget("build_arch_selector")

            build_button = self.wTree.get_widget("build_start_button")
            build_button.connect("clicked", lambda w: self.build_client(combo.get_active_text()))
            build_cancel = self.wTree.get_widget("build_cancel_button")
            build_cancel.connect("clicked", lambda w: self.cleanup(combo.get_active_text()))

            if arch == 'amd64':
                archlist = ['amd64', 'i386']
            else:
                archlist = [arch]

            for item in archlist:
                combo.append_text(item)
            combo.set_active(0)
            self.buildwin.show_all()
            self.wTree.get_widget("build_help_button").hide()

    def get_root_path(self):
        pathlist = os.listdir('/opt/ltsp/')
        if pathlist:
            return pathlist
        else:
            return 0
    
    def cleanup(self, root):
        dirs = ['proc','sys','var/run','var/lock']
        os.system('pkill ltsp-build-client')
        os.system('pkill debootstrap')
        for directory in dirs:
            os.system('umount /opt/ltsp/'+root+'/'+directory+' 2>/dev/null')
        os.system('rm -rf /opt/ltsp/'+root)
        sys.exit(0)
    
    def build_client(self, arch):
        progbar = self.wTree.get_widget("build_progressbar")
        self.wTree.get_widget("build_start_button").set_sensitive(0)
        self.wTree.get_widget("build_arch_selector").set_sensitive(0)
        fraction=0
        
        pp=Popen3('/usr/sbin/ltsp-build-client --arch '+arch+' 2>/dev/null')
        for nn in xrange(2000):
            while gtk.events_pending():
                gtk.main_iteration(True)
            line = pp.fromchild.readline().strip()
            if line.startswith('I: '):
                line = line.lstrip('I: ')
            out = self.line_filter(line)
            if out:
                progbar.set_text(out)
                step = fraction+0.000666667 #0.000731529 for http, 0.000877193 for cdroms
                if step > 1.0:
                    step = 1.0
                progbar.set_fraction(step)
                fraction = step

        progbar.set_fraction(1.0)
        progbar.set_text('LTSP Setup Done...')
        
        gobject.timeout_add(3000, self.show_mainwin('/opt/ltsp/'+arch))
    
    def line_filter(self, line):
        if line.startswith('Retrieving') or line.startswith('Validating') or line.startswith('Extracting') or line.startswith('Installing') or line.startswith('Unpacking') or line.startswith('Configuring') or line.startswith('Need to get') or line.startswith('Setting up') or line.startswith('Cleaning'):
            line = line.split('(')[0]
            if not line.endswith('...'):
                line=line+'...'
            self.lines=self.lines+1
            return line
        elif line.startswith('Get:'):
            line = 'Retrieving '+line.split()[3]+'...'
            self.lines=self.lines+1
            return line
        else:
            return

    def set_toggles(self):
        # Server
        nbd_checkbutton = self.wTree.get_widget("nbd_checkbutton")
        nbd_checkbutton.connect("clicked", lambda w: \
            self.toggle([self.wTree.get_widget("swap_hbox")]))
        
        # Client input
        kbd_checkbutton = self.wTree.get_widget("kbd_checkbutton")
        kbd_checkbutton.connect("clicked", lambda w: \
            self.toggle([self.wTree.get_widget("kbd_layout_hbox"), \
                    self.wTree.get_widget("kbd_model_hbox")]))
        
        mouse_checkbutton = self.wTree.get_widget("mouse_checkbutton")
        mouse_checkbutton.connect("clicked", lambda w: \
            self.toggle([self.wTree.get_widget("mousedev_hbox"), \
                    self.wTree.get_widget("mouseproto_hbox"), \
                    self.wTree.get_widget("emu_hbox")]))
        
        # Client screen
        noauto_checkbutton = self.wTree.get_widget("noauto_checkbutton")
        noauto_checkbutton.connect("clicked", lambda w: \
            self.toggle([self.wTree.get_widget("hsync_hbox"), \
                    self.wTree.get_widget("vref_hbox"), \
                    self.wTree.get_widget("driver_hbox")]))
        
        xfs_checkbutton = self.wTree.get_widget("xfs_checkbutton")
        xfs_checkbutton.connect("clicked", lambda w: \
            self.toggle([self.wTree.get_widget("xfs_hbox")]))
        
        return True

    def toggle(self, widgetlist):
        status = widgetlist[0].get_property('sensitive')
        if status == True:
            status = False
        else:
            status = True
        for widget in widgetlist:
            widget.set_sensitive(status)

    def get_devices(self):
        list = []
        self.devlist = []
        devlist = os.popen("cat /proc/net/dev|grep :|\
            awk '{split($0, dev, \":\"); printf \"%s\\n\", \
            substr(dev[1], 3)}'|grep -v lo|grep -v sit")
        list.append(devlist.read().split('\n'))
        for device in list[0][:-1]:
            if not self.get_ip(device) == False:
                self.devlist.append(device)
            else:
                # make that a info dialog
                self.win.set_sensitive(False)
                dialog = gtk.MessageDialog(self.win,
                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                        gtk.MESSAGE_INFO, gtk.BUTTONS_OK, None)
                dialog.set_markup('<big><b>Not all available interfaces activated!</b></big>\n\nThe interface <b>'+device+'</b> is present but offline. Please bring it up before running LTSP Manager if you want to use it. For LTSP.')
                dialog.connect("destroy", lambda w: self.win.set_sensitive(True))
                resp = dialog.run()
                if resp:
                    dialog.destroy()
        return True

    def get_keymaps(self):
        for item in ltsp.dictionary.keymaps(self.root):
            self.wTree.get_widget("kbd_layout_combobox").append_text(str(item).strip('\"'))

    def get_kbmodels(self):
        list = []
        for model in ltsp.dictionary.kbmodels(self.root):
            self.wTree.get_widget("kbd_model_combobox").append_text(model)

    def get_serial_protocols(self):
        for line in  ltsp.dictionary.serproto(self.root):
            self.wTree.get_widget("mouseproto_combobox").append_text(line)

    def get_videodrivers(self):
        for item in ltsp.dictionary.videodrivers(self.root):
            self.wTree.get_widget("driver_combobox").append_text(item)

    def get_ip(self, ifname):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            retval = socket.inet_ntoa(fcntl.ioctl(
                s.fileno(),
                0x8915,  # SIOCGIFADDR
                struct.pack('256s', ifname[:15])
                )[20:24])
        except:
            return False
        return retval

    def main(self):
        gtk.main()

if __name__ == "__main__":
    base = LTSPManager()
    base.main()
