# tests the gui. we are going to slowly add functions and handlers for this
# service daemon and a way to populate the tree view.
import gtk
import gtk.glade
import os, os.path
import subprocess



daemon_path = "/etc/rc.d/"
running_daemon_path = "/var/run/"
startup_daemon_path = "/etc/rc.conf"
d_def_file = "arch_services"
d_def_output_file = "arch_services.output"
log_file_path = "log"
VERBOSE = True
class ServiceGUI:
    def __init__(self):
        # import the glade file
        self.xml = gtk.glade.XML('services.glade')
        # definition dictionary, key: description, value: binary name
        self.desc_dict = self.daemon_array(d_def_file) 
        # retreive the treeview from our glade file.
        self.stop_button = self.xml.get_widget("stop_button")
        self.start_button = self.xml.get_widget("start_button")
        self.treeview1 = self.xml.get_widget("treeview1")
        self.treeselection1 = self.treeview1.get_selection()
        # set callback function for our treeselection.
        self.treeselection1.set_select_function(self.active_cb, None)
        # create a new liststore to put in the treeview
        self.liststore1 = gtk.ListStore(str, str, 'gboolean')

	self.gen_autostarted()
        self.fillRows(self.liststore1, daemon_path)
        
        self.treeview1.set_model(self.liststore1)
        self.tvcolumn_service = gtk.TreeViewColumn('Service')
        self.tvcolumn_bools = gtk.TreeViewColumn('Start on Boot')
        # add them to the tree view
        self.treeview1.append_column(self.tvcolumn_service)
        self.treeview1.append_column(self.tvcolumn_bools)
        # set column type
        cell_str = gtk.CellRendererText()
        cell_bool = gtk.CellRendererToggle()
        cell_pixbuf = gtk.CellRendererPixbuf()
        # make the cell toggleable
        cell_bool.set_property('activatable', 'True')
        # add a signal handler, it passes the treemodel in so the handler can update it.
        cell_bool.connect('toggled', self.toggled_cb, self.treeview1.get_model())
        # pack them in
        self.tvcolumn_service.pack_start(cell_pixbuf, False)
        self.tvcolumn_service.pack_start(cell_str, True)
        self.tvcolumn_bools.pack_start(cell_bool, True)
        # set the column to render the cells
        self.tvcolumn_service.set_attributes(cell_pixbuf, stock_id=0)
        self.tvcolumn_service.set_cell_data_func(cell_pixbuf, self.running_cb)
        self.tvcolumn_service.set_attributes(cell_str, text=1)
        self.tvcolumn_bools.add_attribute(cell_bool, 'active', 2)
        self.tvcolumn_bools.set_cell_data_func(cell_bool, self.autostart_cb)
        # check if this daemon is in init.d/rc2, if so, set it true.
        self.connect_handlers()
        return
    
    # serves to connect the handlers defined in the .glade file
    
#Startup
    def fillRows(self, liststore, path):
        daemons = os.listdir(path)
        for daemon in daemons:
            cur_bool = False;
            if str(daemon) in self.desc_dict:
                liststore.append([gtk.STOCK_NO, self.desc_dict[str(daemon)], cur_bool])
        return

    def autostart_cb(self, column, cell, model, iter):
        if self.is_autostart(iter):
            cell.set_property('active', 1)
        else:
            cell.set_property('active', 0)
        return

    def running_cb(self, column, cell, model, iter):
        if self.is_running(iter):
            cell.set_property('stock-id', gtk.STOCK_YES)
        else:
            cell.set_property('stock-id', gtk.STOCK_NO)
	return
    # figure out which ones are set to autostart
    def gen_autostarted(self):
        daemon_file = open(startup_daemon_path, 'r')
        lines = daemon_file.readlines()
        daemon_file.close()
        nextline = False
        daemons = ""
        for line in lines:
            if line.find("DAEMONS=(") == 0:
                if line.find(")")!=-1:
                    nextline = False
                else:
                    nextline = True
                daemons += line[line.find("("):]
            if nextline:
                if line.find(")")!=-1:
                    daemons += line[:line.find(")")]
                else:
                    continue
       	daemons = daemons.strip("\n)(").split(" ")
        self.autostarted = {}
        for daemon in daemons:
            if daemon[0] != "!":
                self.autostarted[daemon] = 1
        return 
#read a file and create a dictionary file for daemon names and their short descriptions
# file should be in the format: <binary>:<description>:S##:K##
    def daemon_array(self, path_to_db_file):
        if os.path.exists(path_to_db_file):
            desc_file = open(path_to_db_file, 'r')
            lines = desc_file.readlines()
            binaries = []
            descriptions = []
            d_array = {}
            i = -1
            while i < len(lines)-1:
                i+=1
                lines[i].strip
                # if the line is commented out, let's ignore it.
                if lines[i][0] == '#':
                    continue
                cur_line = lines[i].split(":")
                # if there is more than one semi-colon involved, this line is no good.
                if len(cur_line) != 2:
                    continue
                # create a redundant entry, so that every key,value pair also has a value, key pair.
                binary = cur_line[0].strip()
                desc = cur_line[1].strip()
                d_array[binary] = desc
                # the value to a desc key is a list. it may be a triple, it may not be.
                d_array[desc] = binary
        else:
            raise RuntimeError, "%s does not exist" % (path_to_array_file)
        return d_array
# Shutdown
#TODO: need to have it actually output to file.
    def write_definitions(self, path_to_db_file):
        daemon_file = open(startup_daemon_path, 'r')
        output_file = open(path_to_db_file, 'w')
        lines = daemon_file.readlines()
        output_lines = []
        for line in lines:
            if line.find("DAEMONS=") == 0:
                daemon_line = ""
                for daemon in self.autostarted:
                    daemon_line += daemon + " "
                output_lines.append("DAEMONS=(" + daemon_line + ")")
                continue
            output_lines.append(line)
        for line in output_lines:
            output_file.write(line)
        output_file.close()
        daemon_file.close()
        return
#Helpers
    def is_autostart(self, iter):
        return self.autostarted.has_key(self.desc_dict[self.liststore1.get_value(iter, 1)])

    def is_running(self, iter):
        binary = self.desc_dict[self.liststore1.get_value(iter, 1)]
        path = running_daemon_path + binary
        return os.path.exists(path) or os.path.exists(path+".pid")

    def handle_service(self, service, action):
        service_path = daemon_path + '/' + service
        ret = subprocess.call(service_path +" "+ action, shell=True)
        return ret
    def log(self, log_message, verbose):
        if verbose:
            print log_message
        log_file = open(log_file_path, 'w')
        log_file.write(log_message+'\n')
        log_file.close()

#Callbacks
    # callback for toggled auto-start
    # when we click it, we should either remove the symlink or create one
    # in init.d/rc2
    def toggled_cb(self, cell, path, user_data):
        model = user_data
        model[path][2] = not model[path][2]
        service = self.desc_dict[model[path][1]]

	# from true to false
        if cell.get_property('active'):
            cell.set_property('active', 0)
            del self.autostarted[service]
        # from false to true
        else:
	    cell.set_property('active', 1)
            self.autostarted[service] = 1
        # LOG
        self.log(str("toggled "+ service + " " + cell.get_property('active')), VERBOSE)
	return
    # callback to treeview's treeselect, to activate/deactivate buttons on selection change.
    def active_cb(self, path, unused):
        iter = self.liststore1.get_iter(path)
        print path
        if self.is_running(iter):
            self.start_button.set_property('sensitive', False)
            self.stop_button.set_property('sensitive', True)
        else:
            self.start_button.set_property('sensitive', True)
            self.stop_button.set_property('sensitive', False)
        return True
    #Signal Handlers
    def delete_event(self, obj):
        self.write_definitions(d_def_output_file)
        gtk.main_quit()
    # start a service using init invoke.
    def start_service(self, obj):
        model, iter = self.treeselection1.get_selected()
        if iter != None:
            service = self.desc_dict[self.liststore1.get_value(iter, 1)]
            # LOG
            self.log(str("Starting "+ service), VERBOSE)
            ret = self.handle_service(service, "start")
            if (ret == 0):
                self.liststore1.set_value(iter, 0, gtk.STOCK_YES)
                self.start_button.set_property('sensitive', False)
                self.stop_button.set_property('sensitive', True)
                self.log(str(service) + " successfully started (" + str(ret) + ")", VERBOSE)
            else:
                self.log("EE:" + service + " unsuccessfully started (" + str(ret) + ")", VERBOSE)
            # LOG
            print ret
    # stop a service using init invoke.
    def stop_service(self, obj):
        model, iter = self.treeselection1.get_selected()
        if iter != None:
            service = self.desc_dict[self.liststore1.get_value(iter, 1)]
            # LOG
            self.log(str("Stopping " + service), VERBOSE)
            ret = self.handle_service(service, "stop")
            if (ret == 0):
                self.start_button.set_property('sensitive', True)
                self.stop_button.set_property('sensitive', False)
                self.liststore1.set_value(iter, 0, gtk.STOCK_NO)
                self.log(str(service) + " successfully stopped (" + str(ret) + ")", VERBOSE)
            else:
                self.log("EE:" + service + " unsuccessfully stopped (" + str(ret) + ")", VERBOSE)
            # LOG
            print ret
            
        
    def connect_handlers(self):
        # connect signal handlers. kinda.
        dic = { "on_window1_destroy" : self.delete_event }
        self.xml.signal_autoconnect(dic)
        dic = { "on_close_button_clicked" : self.delete_event }
        self.xml.signal_autoconnect(dic)
        dic = { "on_start_button_clicked" : self.start_service }
        self.xml.signal_autoconnect(dic)
        dic = { "on_stop_button_clicked" : self.stop_service }
        self.xml.signal_autoconnect(dic)
        return

def main():
    gtk.main()
if __name__ == "__main__":
    driver = ServiceGUI()
    main()


