#!/usr/bin/python
# Mythbuntu Live CD Session Startup Script
# Copyright (C) 2007, Mario Limonciello <superm1@mythbuntu.org>
#
#
# Exit error codes:
# 0: normal exit
# 1: permissions error
# 2: --help called
# 3: didn't find file
#
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


#Text backend requirements
import os
import subprocess
import sys
import string
import stat
import shutil
import debconf
import ConfigParser

#Gui requirements
import gtk
import MySQLdb
import re

from mythbuntu_common.mysql import MySQLHandler

# Define global path
PATH = '/usr/share/mythbuntu/ui'
#PATH = '/home/test/mythbuntu-live-autostart/gui'


class MythbuntuStartup():
    def __init__(self):
        #initialize text backend
        self.configfile = None
        self.config = ConfigParser.ConfigParser()
        self.config.add_section("mythbuntu")
        #initialize gui
        self.builder = gtk.Builder()
        self.builder.add_from_file('%s/mythbuntu_live_autostart.ui' % PATH)

        #set icon
        if os.path.exists('/usr/share/pixmaps/mythbuntu.png'):
            gtk.window_set_default_icon_from_file('/usr/share/pixmaps/mythbuntu.png')
        elif os.path.exists('/usr/share/icons/Human/48x48/places/start-here.png'):
            gtk.window_set_default_icon_from_file('/usr/share/icons/Human/48x48/places/start-here.png')

        #make widgets referencable from top level
        for widget in self.builder.get_objects():
            if not isinstance(widget, gtk.Widget):
                continue
            setattr(self, widget.get_name(), widget)

        #connect signals
        self.builder.connect_signals(self)

        self.mysql=MySQLHandler()

        #for some reason, that above initialization doesn't catch the about
        #dialog, so we need to manually do it too
        setattr(self, "about_dialog", self.builder.get_object("about_dialog"))
        debconf.runFrontEnd()
        self.db = debconf.Debconf()

    def find_configuration(self):
        """Finds a mythbuntu configuration file"""
        directories = [ x for x in os.listdir('/media') if not x.startswith('.')]
        for directory in directories:
            files = os.listdir(os.path.join('/media', directory))
            for file in files:
                if file == 'mythbuntu_configuration.ini':
                    self.configfile=os.path.join('/media/', directory, file)
                    return True
        return False

    def load_configuration(self):
        """Parses a mythbuntu configuration file"""
        file_name = open(self.configfile, "r")
        self.config.readfp(file_name)
        file_name.close()

    def check_autostart(self):
        """Determines whether we want to automatically start"""
        if self.config.has_option("mythbuntu", "autostart"):
            if self.config.get("mythbuntu", "autostart") == "True":
                return True
        return False

    def print_configuration(self):
        """Outputs everything parsed from a configuration"""
        print "Found a configuration @: " + self.configfile
        print "    Automatically Start: " + self.config.get("mythbuntu", "autostart")
        print "    Use External Home Directory: " + self.config.get("mythbuntu", "externalhome")
        print "    Remote Control Enabled: " + self.config.get("mythbuntu", "remote_en")
        print "    Remote Control: " + self.config.get("mythbuntu", "remotecontrol")
        print "    Remote Driver: " + self.config.get("mythbuntu", "remotedriver")
        print "    Remote Module(s): " + self.config.get("mythbuntu", "remotemodules")
        print "    Remote Config: " + self.config.get("mythbuntu", "remoteconfig")
        print "    Hostname: "+ self.config.get("mythbuntu", "hostname")
        print "    MySQL User Name: "+ self.config.get("mythbuntu", "mysql_user")
        print "    MySQL Password: "+ self.config.get("mythbuntu", "mysql_pass")
        print "    MySQL Database: "+ self.config.get("mythbuntu", "mysql_db")
        print "    MySQL Hostname: "+ self.config.get("mythbuntu", "mysql_host")

    def process_master_backend_info(self):
        """Sets up the info to contact the master backend for the box"""
        if self.config.has_option("mythbuntu", "mysql_user") and \
            self.config.has_option("mythbuntu", "mysql_pass") and \
            self.config.has_option("mythbuntu", "mysql_db") and \
            self.config.has_option("mythbuntu", "mysql_host"):

            #Read new settings
            user = self.config.get("mythbuntu", "mysql_user")
            passwd = self.config.get("mythbuntu", "mysql_pass")
            database = self.config.get("mythbuntu", "mysql_db")
            host = self.config.get("mythbuntu", "mysql_host")

            #Update /etc/mythtv/mysql.txt
            out_f = open("/etc/mythtv/mysql.txt", "w")
            out_f.write("DBHostName=" + host + "\n")
            out_f.write("DBUserName=" + user + "\n")
            out_f.write("DBName=" + database + "\n")
            out_f.write("DBPassword=" + passwd + "\n")

    def start_frontend(self):
        """Starts the frontend"""
        newhome=os.getenv('HOME')
        if self.config.has_option("mythbuntu","externalhome") and \
            self.config.get("mythbuntu", "externalhome") == "True" and \
            self.configfile != None:
                newhome=os.path.split(self.configfile)[0]
                os.putenv('HOME',newhome)
                #Disable permission changing for now.  It doesn't get along well with autostart
                #os.chown(newhome,int(os.getenv('SUDO_UID')),int(os.getenv('SUDO_GID')))
                #existing_permissions = stat.S_IMODE(os.stat(newhome).st_mode)
                #new_permissions = existing_permissions | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
                #os.chmod(newhome,new_permissions)
        login=os.getenv('USERNAME')
        groups=string.split(subprocess.Popen(["id","-G", login], stdout=subprocess.PIPE).communicate()[0])
        mapped_groups = map(lambda x: int(x), groups)
        os.setgroups(mapped_groups)
        os.setgid(int(os.getenv('SUDO_GID')))
        os.setuid(int(os.getenv('SUDO_UID')))
        start_command = "mythfrontend.real --logfile " + newhome + "/mythfrontend.log"
        if os.system(start_command) != 0:
            print "Unable to start frontend via " + start_command

    def process_hostname(self):
        """Sets the new hostname for the box"""
        if self.config.has_option("mythbuntu", "hostname"):
            #bad hack for now.
            permissions_command = "xhost + > /dev/null"
            host_command = "hostname " + self.config.get("mythbuntu", "hostname")
            os.system(permissions_command)
            os.system(host_command)

    def process_remote(self):
        """Processes the remote control that we have chosen"""
        if self.config.has_option("mythbuntu", "remote_en") and \
            self.config.has_option("mythbuntu", "remotecontrol") and \
            self.config.has_option("mythbuntu", "remotedriver") and \
            self.config.has_option("mythbuntu", "remotemodules") and \
            self.config.has_option("mythbuntu", "remoteconfig"):
            if self.config.get("mythbuntu", "remote_en") == "True":

                #Get settings from config
                remote=self.config.get("mythbuntu", "remotecontrol")
                if remote == "(No Remote Selected)":
                    driver=""
                    modules=""
                    config=""
                else:
                    modules=self.config.get("mythbuntu", "remotemodules")
                    if modules == "none":
                        modules=""
                    driver=self.config.get("mythbuntu", "remotedriver")
                    p1 = subprocess.Popen(["lircd","-H","help"], stderr=subprocess.PIPE)
                    p2 = subprocess.Popen(["awk", "$0!~/^S|^D/{print $1}"], stdin=p1.stderr, stdout=subprocess.PIPE)
                    p3 = subprocess.Popen(["tr", "'\\n'", "'\ '"],stdin=p2.stdout, stdout=subprocess.PIPE)
                    supporteddrivers=p3.communicate()[0]
                    pattern=re.compile(driver)
                    if pattern.search(supporteddrivers) is None:
                        driver=""
                    config=self.config.get("mythbuntu", "remoteconfig")

                #Put this info into debconf
                self.db.set("lirc/remote", remote)
                self.db.set("lirc/modules", modules)
                self.db.set("lirc/lircd_conf", config)
                self.db.set("lirc/driver", driver)
                self.db.set("lirc/modules", modules)

                #copy over configuration
                if config != "":
                    shutil.copy('/usr/share/lirc/remotes/' + config, '/etc/lirc/lircd.conf')

                #Set up hardware.conf
                in_f = open("/etc/lirc/hardware.conf")
                out_f = open("/etc/lirc/hardware.conf.new", 'w')
                patternline="^MODULES"
                patternline+="|^DRIVER"
                patternline+="|^LIRCD_CONF"
                pattern=re.compile(patternline)
                found_modules=False
                found_driver=False
                found_lircd_conf=False
                for line in in_f:
                    if pattern.search(line) is None:
                        out_f.write(line)
                    else:
                        if not found_modules and re.compile("MODULES").search(line):
                            out_f.write("MODULES=\"" + modules + "\"\n")
                            found_modules = True
                        elif not found_driver and re.compile("DRIVER").search(line):
                            out_f.write("DRIVER=\"" + driver + "\"\n")
                            found_driver = True
                        elif not found_lircd_conf and re.compile("LIRCD_CONF").search(line):
                            out_f.write("LIRCD_CONF=\"" + config + "\"\n")
                            found_lircd_conf = True
                if not found_modules:
                    out_f.write("MODULES=\"" + modules + "\"\n")
                if not found_driver:
                    out_f.write("DRIVER=\"" + driver + "\"\n")
                if not found_lircd_conf:
                    out_f.write("LIRCD_CONF=\"" + config + "\"\n")
                in_f.close()
                out_f.close()
                shutil.move("/etc/lirc/hardware.conf.new", "/etc/lirc/hardware.conf")

                #Apply settings and restart lirc
                #os.system('invoke-rc.d lirc restart')
                start_lirc = subprocess.Popen(["invoke-rc.d", "lirc", "restart"],stdout=subprocess.PIPE).communicate()[0]
                print start_lirc

                ##Create a Lircrc in the right place##

                #Check and see if we have one in $HOME.  If not, make one there.
                #We might not use it, but it doesn't hurt to have
                if not os.path.exists(os.getenv('HOME') + '/.lircrc'):
                    generate_command="mythbuntu-lircrc-generator --lircd /etc/lirc/lircd.conf"
                    if os.path.exists('/usr/bin/mythbuntu-lircrc-generator'):
                        os.system(generate_command)

                #create the .mythtv directory.
                #Again, not a big deal if we don't use it
                if not os.path.exists(os.getenv('HOME') + '/.mythtv'):
                    os.mkdir(os.getenv('HOME') + '/.mythtv')
                os.chmod(os.getenv('HOME') + '/.mythtv', int('777',8))

                #Find the external path (of course only present if
                #we have saved so far)
                if self.configfile != None:
                    newhome=os.path.split(self.configfile)[0]
                else:
                    newhome=os.getenv('HOME')

                #copy over possible existing config if we are not using an external home
                #or we haven't defined an external home yet
                if (self.config.has_option("mythbuntu","externalhome") and \
                    self.config.get("mythbuntu", "externalhome") == "False"):

                    #check if we are working in the same home (eg hasn't been saved)
                    #otherwise copy over a possibly existing .lircrc
                    if newhome != os.getenv('HOME'):
                        if os.path.exists(newhome + '/.lircrc'):
                            shutil.copy(newhome + '/.lircrc', os.getenv('HOME'))
                        if os.path.exists(newhome + '/.mythtv/lircrc'):
                            shutil.copy(newhome + '/.mythtv/lircrc', os.getenv('HOME') + '/.mythtv')

                elif self.configfile != None:

                    #create the .mythtv directory
                    if not os.path.exists(newhome + '/.mythtv'):
                        os.mkdir(newhome + '/.mythtv')
                    os.chmod(newhome + '/.mythtv', int('777',8))

                    #copy config if we dont already have one
                    if not os.path.exists(newhome + '/.lircrc'):
                        shutil.copy(os.getenv('HOME') + '/.lircrc', newhome + '/.lircrc')
                    if not os.path.exists(newhome + '/.mythtv/lircrc'):
                        shutil.copy(os.getenv('HOME') + '/.mythtv/lircrc', newhome + '/.mythtv')

#Callback handlers
    def run(self):
        """Runs the PyGTK gui for the user"""
        #We only want to take the time to load into the gui and load the lirc db
        #If we are showing the gui
        self.populate_lirc()
        self.clear_settings()
        self.load_configuration_gui()
        self.main_window.show()
        gtk.main()

    def connection_test(self,widget):
        """Tests to make sure that the backend is accessible"""
        def update_config(old_config=None):
            if old_config is None:
                config={}
                config["user"]=self.mysql_user.get_text()
                config["password"]=self.mysql_pass.get_text()
                config["server"]=self.mysql_server.get_text()
                config["database"]=self.mysql_database.get_text()
            else:
                config=old_config
            self.mysql.update_config(config)

        if widget is not None:
            old_config=self.mysql.get_config()
            update_config()
            result=self.mysql.do_connection_test()
            self.connection_results.set_text(result)
            update_config(old_config)
            self.connection_results_label.show()
            self.connection_results.set_text(result)


    def menu_clicked(self,widget):
        """Catches signals sent from all menu items"""
        if (widget is not None and widget.get_name() == 'new_menuitem'):
            self.clear_settings()
        elif (widget is not None and widget.get_name() == 'open_menuitem'):
            self.open_configuration()
        elif (widget is not None and widget.get_name() == 'save_menuitem'):
            self.save_configuration()
        elif (widget is not None and widget.get_name() == 'quit_menuitem'):
            self.destroy(None)
        elif (widget is not None and widget.get_name() == 'about_menuitem'):
            self.about_dialog.run()
            self.about_dialog.hide()

    def open_configuration(self):
        """Presents the user with a dialog to open a configuration"""
        self.open_dialog.set_filename("/media/disk")
        filter=gtk.FileFilter()
        filter.set_name("Mythbuntu Configuration Files")
        filter.add_pattern("mythbuntu_configuration.ini")
        self.open_dialog.add_filter(filter)
        response = self.open_dialog.run()
        if response == gtk.RESPONSE_OK:
            self.configfile = self.open_dialog.get_filename()
            self.load_configuration()
            self.load_configuration_gui()
        self.open_dialog.hide()

    def clear_settings(self):
        """Clears all modified settings"""
        self.mysql_user.set_text("mythtv")
        self.mysql_pass.set_text("")
        self.mysql_database.set_text("mythconverg")
        self.mysql_server.set_text("")
        self.connection_results_label.hide()
        self.connection_results.set_text("")
        self.remotecontrol.set_active(False)
        self.remote_list.set_active(0)
        self.autostart.set_active(True)
        self.externalhome.set_active(True)
        self.hostname.set_text("ubuntu")

    def save_start_session(self,widget):
        """Saves all settings and then proceeds to carry on"""
        self.save_configuration()
        self.start_session()

    def save_configuration(self,widget=None):
        """Saves all settings"""

        self.save_dialog.set_filename("/media/disk")
        response = self.save_dialog.run()
        if response == gtk.RESPONSE_OK:
            self.configfile = self.save_dialog.get_filename() + '/mythbuntu_configuration.ini'
            self.process_gui()
            file_name = open(self.configfile, "w")
            self.config.write(file_name)
            file_name.close()
        self.save_dialog.hide()

    def process_gui(self):
        """Processes all inputs within the gui"""
        #Autostart Frontend
        if self.autostart.get_active():
            self.config.set("mythbuntu", "autostart", "True")
        else:
            self.config.set("mythbuntu", "autostart", "False")
        #External Home Directory
        if self.externalhome.get_active():
            self.config.set("mythbuntu", "externalhome", "True")
        else:
            self.config.set("mythbuntu", "externalhome", "False")
        #Remote Control
        if self.remotecontrol.get_active():
            self.config.set("mythbuntu", "remote_en", "True")
            self.config.set("mythbuntu", "remotecontrol", self.remote_list.get_active_text())
            self.config.set("mythbuntu", "remotedriver", self.remote_driver.get_active_text())
            self.config.set("mythbuntu", "remotemodules", self.remote_modules.get_active_text())
            self.config.set("mythbuntu", "remoteconfig", self.remote_rc.get_active_text())
        else:
            self.config.set("mythbuntu", "remote_en", "False")
            self.config.set("mythbuntu", "remotecontrol", "none")
            self.config.set("mythbuntu", "remotedriver", "none")
            self.config.set("mythbuntu", "remotemodules", "none")
            self.config.set("mythbuntu", "remoteconfig", "none")
        #Master Backend Info
        self.config.set("mythbuntu", "hostname", self.hostname.get_text())
        self.config.set("mythbuntu", "mysql_user", self.mysql_user.get_text())
        self.config.set("mythbuntu", "mysql_pass", self.mysql_pass.get_text())
        self.config.set("mythbuntu", "mysql_db", self.mysql_database.get_text())
        self.config.set("mythbuntu", "mysql_host", self.mysql_server.get_text())

    def start_session(self,widget=None):
        """Starts the session"""
        #We will only need to reprocess the gui if we enter this method
        #From within the gui.  Otherwise, assume an automatic load
        if (widget is not None and widget.get_name() == 'start'):
            self.process_gui()
        self.process_hostname()
        self.process_master_backend_info()
        self.process_remote()
        self.db.stop()
        self.start_frontend()
        sys.exit(0)

    def remote_changed(self,widget):
        """Called when the remote checkbox is toggled"""
        if (widget is not None and widget.get_name() == 'remotecontrol'):
            if widget.get_active() == True:
                self.remote_hbox.set_sensitive(True)
            else:
                self.remote_hbox.set_sensitive(False)
                self.remote_list.set_active(0)
        elif (widget is not None and widget.get_name() == 'remote_list'):
            active_num = widget.get_active()
            self.remote_driver.set_active(active_num)
            self.remote_modules.set_active(active_num)
            self.remote_rc.set_active(active_num)

    def populate_lirc(self):
        """Fills the lirc pages with the appropriate data"""
        #Note, this requires lirc >= 0.8.2
        self.remote_count=1
        hwdb = open('/usr/share/lirc/lirc.hwdb').readlines()
        hwdb.sort()
        #Filter out uncessary lines
        filter = "^\#|^\["
        #Filter out the /dev/input/eventX remote
        filter += "|http"
        pattern = re.compile(filter)
        for line in hwdb:
            if pattern.search(line) is None:
                list = string.split(line, ";")
                if len(list) > 1:
                    #Make sure we have a config file before including
                    if list[4] != "":
                        self.remote_list.append_text(list[0].translate(string.maketrans('',''),','))
                        self.remote_driver.append_text(list[1])
                        self.remote_modules.append_text(list[2])
                        self.remote_rc.append_text(list[4])
                        self.remote_count = self.remote_count + 1
        self.remote_list.set_active(0)

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def display_error(self,message):
        """Displays an error message"""
        dialog = gtk.MessageDialog(self.main_window, gtk.DIALOG_MODAL,
                                   gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
                                   message)
        dialog.run()
        sys.exit(1)

    def load_configuration_gui(self):
        """Loads all options into the gui"""
        #Master backend info
        if self.config.has_option("mythbuntu", "mysql_user") and \
            self.config.has_option("mythbuntu", "mysql_pass") and \
            self.config.has_option("mythbuntu", "mysql_db") and \
            self.config.has_option("mythbuntu", "mysql_host"):

            self.mysql_user.set_text(self.config.get("mythbuntu", "mysql_user"))
            self.mysql_pass.set_text(self.config.get("mythbuntu", "mysql_pass"))
            self.mysql_database.set_text(self.config.get("mythbuntu", "mysql_db"))
            self.mysql_server.set_text(self.config.get("mythbuntu", "mysql_host"))

        #Autostart information
        if self.config.has_option("mythbuntu", "autostart"):
            if self.config.get("mythbuntu", "autostart") == "True":
                self.autostart.set_active(True)
            elif self.config.get("mythbuntu", "autostart") == "False":
                self.autostart.set_active(False)

        #External Home Directory Information
        if self.config.has_option("mythbuntu", "externalhome"):
            if self.config.get("mythbuntu", "externalhome") == "True":
                self.externalhome.set_active(True)
            elif self.config.get("mythbuntu", "externalhome") == "False":
                self.externalhome.set_active(False)

        #Hostname
        if self.config.has_option("mythbuntu", "hostname"):
            self.hostname.set_text(self.config.get("mythbuntu", "hostname"))

        #Remote Control
        if self.config.has_option("mythbuntu", "remote_en") and \
            self.config.has_option("mythbuntu", "remotecontrol"):
            if self.config.get("mythbuntu", "remote_en") == "True":
                self.remotecontrol.set_active(True)
                for item in range(self.remote_count):
                    self.remote_list.set_active(item)
                    if self.remote_list.get_active_text() == self.config.get("mythbuntu", "remotecontrol"):
                        break
            else:
                self.remotecontrol.set_active(False)
                self.remote_list.set_active(0)

def display_help():
    """Shows the help for this application"""
    print ""
    print "mythbuntu-startup is used to create and run a custom Mythbuntu Live Session"
    print "If you have not yet created a configuration to use with it,"
    print ""
    print "USAGE:"
    print ""
    print "mythbuntu-startup [--help]"
    print "    Display this help"
    print ""
    print "mythbuntu-startup [--load]"
    print "    The application can be launched with the optional switch --load"
    print "    Providing no additional arguments after --load will imply a search"
    print "    Within the first level of directories located in /media"
    print "    If nothing is found, the application will simply exit"
    print ""
    print "mythbuntu-startup [--load] [file]"
    print "    Launching with a file after --load will attempt to start the application"
    print "    loading that file"
    print ""
    print "mythbuntu-startup [--edit]"
    print "    Lastly mythbuntu-startup can load directly into edit mode, disregarding"
    print "    Any automatic loading listed in the file. This is useful when a typographical"
    print "    Error within the file is preventing things from working correctly."
    print ""
    print "mythbuntu-startup"
    print "    Launching the application with no arguments will search within the first level"
    print "    of directories within /media.  If nothing is found, the configuration editor"
    print "    will be launched."
    sys.exit(2)

if __name__ == '__main__':

    for arg in sys.argv:
        if arg == "--help" or arg == "-help" or arg == "--h" or arg == "-h":
            display_help()

    mythbuntu = MythbuntuStartup()

    #check to make sure we are running root via sudo
    if (os.getuid() != 0) or ('SUDO_UID' not in os.environ) or ('SUDO_GID' not in os.environ):
        mythbuntu.display_error('This application must be run with administrative privileges from gksu, gksudo, sudo, or kdesu.  It can not be ran directly as root or a normal user, and cannot continue.')


    #We are presented with the --load switch without a file
    #This happens during the initial boot and should be the
    #most common case
    if len(sys.argv) == 2 and sys.argv[1] == "--load":
        if mythbuntu.find_configuration():
            mythbuntu.load_configuration()
        else:
            sys.exit(3)
    #We are presented with a file to directly load
    elif len(sys.argv) > 2 and sys.argv[1] == "--load":
        mythbuntu.configfile= sys.argv[2]
        mythbuntu.load_configuration()
    #We have a normal startup without arguments
    #Try to start from a configuration file, otherwise
    #Show the gui
    elif mythbuntu.find_configuration():
        mythbuntu.load_configuration()

    #Debug mode
    if 'DEBUG' in os.environ:
            print "******   WARNING: Debug mode enabled   *****"
            mythbuntu.print_configuration()

    #Start the session if indicated by the file
    #And if the override isn't present
    if mythbuntu.check_autostart() and not ( len(sys.argv) == 2 and sys.argv[1] == "--edit"):
        mythbuntu.start_session()
    #Otherwise, show the gui
    else:
        mythbuntu.run()
    sys.exit(0)
