## -*- coding: utf-8 -*-
#
# «mythbuntu-bare» - A Plugin to assist in backing up and restoring the mythtv DB
#
# Copyright (C) 2010, Thomas Mashos, for Mythbuntu
#
#
# Mythbuntu 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 2 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 application; if not, write to the Free Software Foundation, Inc., 51
# Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
##################################################################################

from MythbuntuControlCentre.plugin import MCCPlugin
from gi.repository import Gtk
import time
import os
import tarfile
import shutil
import subprocess
from threading import Thread
import sys
import configparser
import hashlib
sys.path.append("/usr/share/mythbuntu-bare/bareclient")
from mythbackup import Backup
from mythrestore import Restore


class MythbuntuBaRePlugin(MCCPlugin):
    """A Plugin to assist in backing up and restoring the mythtv DB"""
    #
    #Load GUI & Calculate Changes
    #
    
    CONFIGFILE = "/var/lib/mythtv/bare-client/mythbuntu-bare-client.conf"

    #USER=os.getenv("HOME")  ## This doesn't work when passed to root changes
    USER=os.path.expanduser('~')

    BACKUPFILES = [
    "/etc/mythtv/config.xml",
    "/etc/mythtv/mysql.txt",
    "/etc/lirc/lircd.conf",
    "/etc/lirc/hardware.conf",
    "/etc/udev/rules.d"
    ]

    BACKUPFILES.append(USER+"/.lirc")

    BACKUP_LOCATION = USER
    BACKUP_DB=False
    restore_file = '/tmp'

    IsTarFile = False
    ValidNewConf = False

    def __init__(self):
        #Initialize parent class
        information = {}
        information["name"] = "Backup and Restore"
        information["icon"] = "revert"
        information["ui"] = "tab_mythbuntu-bare"
        MCCPlugin.__init__(self,information)
        import configparser
        self.config = configparser.ConfigParser()

    def captureState(self):
        """Determines the state of the items on managed by this plugin
           and stores it into the plugin's own internal structures"""
        import os
        self.changes = {}
        try:
            self.config.read(self.CONFIGFILE)
            self.changes['serverip'] = self.config.get("General", "serverip")
            self.changes['managed'] = self.config.get("General", "managed")
            if str.lower(self.changes['managed']) == "true":
                self.managed_label.set_text("This machine is current in managed mode")
                self.radio_convert.set_sensitive(False)
                self.backup_schedule.set_sensitive(False)
            self.changes['BackupLocation'] = self.config.get("Backup", "storagedir")
            self.changes['backup_db'] = self.config.get("Backup", "db")
            self.changes['backup_schedule'] = self.config.get("Backup", "schedule")
            self.changes['backup_schedule_day'] = self.config.get("Backup", "day")
            self.changes['backup_schedule_hour'] = self.config.get("Backup", "hour")
            self.changes['backup_schedule_minute'] = self.config.get("Backup", "minute")
            self.changes['backup_schedule_weekday'] = self.config.get("Backup", "weekday")
            self.db_backup_checkbutton.set_active(int(self.config.get("Backup", "db")))
        except:
            self.changes['serverip'] = "127.0.0.1"
            self.changes['managed'] = "False"
            self.changes['BackupLocation'] = "/var/lib/mythtv/bare/"
            self.changes['backup_db'] = False
            self.changes['backup_schedule'] = "disabled"
            self.changes['backup_schedule_day'] = "*"
            self.changes['backup_schedule_hour'] = "0"
            self.changes['backup_schedule_minute'] = "00"
            self.changes['backup_schedule_weekday'] = "*"
        if not self.query_installed('mythtv-backend-master'):
            self.backup_db_label.set_text("Database can only be backed up from master backend")
            self.db_backup_checkbutton.set_sensitive(False)
            self.db_backup_checkbutton.set_active(False)

    def applyStateToGUI(self):
        """Takes the current state information and sets the GUI
           for this plugin"""
        self.button_box.hide()
        self.hide_tabs()
        self.vbox_start.show()
#        self.vbox_backup.hide()
        self.vbox_restore.hide()
        self.vbox_choices.hide()
        self.radio_backup.set_active(False)
        ## Set radio buttons to default (off)
        self.radio_backup_default_off.set_active(True)
        self.radio_backup_default_off.hide()
        self.radio_schedule_default_off.set_active(True)
        self.radio_schedule_default_off.hide()
        self.backup_schedule_off.set_active(True)
        self.backup_schedule_off.hide()
        self.radio_restore.set_active(False)
        self.backup_now.set_active(False)
        self.backup_dir_button.set_active(False)
#        self.on_radio_backup_toggled(None)
        self.on_backup_dir_button_toggled(None)
#        self.on_backup_schedule_toggled(None)
        if self.changes['backup_schedule'] == "disabled":
            self.scheduled_label.set_text("Disabled")
            self.schedule_disabled_button.set_active(True)
        elif self.changes['backup_schedule'] == "daily":
           self.schedule_daily_button.set_active(True)
        elif self.changes['backup_schedule'] == "weekly":
            self.schedule_weekly_button.set_active(True)
            wday = int(self.changes['backup_schedule_weekday'])
            self.weekly_combobox.set_active(wday)
            self.hbox_schedule_weekly.show()
        elif self.changes['backup_schedule'] == "monthly":
            self.schedule_monthly_button.set_active(True)
            md = int(self.changes['backup_schedule_day'])
            mday = md-1
            self.monthly_combobox.set_active(mday)
            self.hbox_schedule_monthly.show()
        if not self.changes['backup_schedule'] == "disabled":
            shour = self.changes['backup_schedule_hour']
            self.schedule_hour_combobox.set_active(int(shour))
            smin = int(self.changes['backup_schedule_minute'])/5
            self.schedule_minute_combobox.set_active(int(smin))
        self.restore_label_isfile.hide()
        self.managed_label_isfile.hide()
        ## Filter only tar.gz files for restore
        filter = Gtk.FileFilter()
        filter.set_name("Backup Files")
        filter.add_pattern("*.tar.gz")
        self.restore_file.add_filter(filter)
        self.restore_file.unselect_all()
        conffilter = Gtk.FileFilter()
        conffilter.set_name("Config Files")
        conffilter.add_pattern("*.conf")
        self.managedfilechooser.add_filter(conffilter)
        self.managedfilechooser.unselect_all()
        self.backup_location.unselect_all()
        ## Disable non-implemented items
        self.backup_dropbox_button.hide()
        self.backup_u1_button.hide()
        self.radio_convert.hide()
        self.managed_label.hide()

    def compareState(self):
        """Determines what items have been modified on this plugin"""
        MCCPlugin.clearParentState(self)
        if self.radio_backup.get_active():
            if self.backup_dir_button.get_active():
                self.BACKUP_LOCATION=self.backup_location.get_filename()
            self.BACKUP_DB = self.db_backup_checkbutton.get_active()
            if self.backup_now.get_active():
                self._markReconfigureUser('BackupNow',self.BACKUP_LOCATION)
            elif self.backup_schedule.get_active():
                #TODO
                if self.schedule_disabled_button.get_active():
                    self._markReconfigureRoot('BackupSchedule', False)
                else:
                    self._markReconfigureRoot('BackupSchedule', True)
                    self._markReconfigureRoot('BackupHour', self.schedule_hour_combobox.get_active_text())
                    self._markReconfigureRoot('BackupMinute', self.schedule_minute_combobox.get_active_text())
                    self._markReconfigureRoot('BackupLocation', self.BACKUP_LOCATION)
                    self._markReconfigureRoot('BackupDatabase', self.db_backup_checkbutton.get_active())
                    if self.schedule_daily_button.get_active():
                       self._markReconfigureRoot('BackupDaily', True)
                    elif self.schedule_weekly_button.get_active():
                       self._markReconfigureRoot('BackupWeekly', self.weekly_combobox.get_active())
                    elif self.schedule_monthly_button.get_active():
                        self._markReconfigureRoot('BackupMonthly', self.monthly_combobox.get_active_text())
        if self.radio_restore.get_active():
            self._markReconfigureRoot('restore',self.restore_file.get_filename())
        if self.restore_db.get_active():
            self._markReconfigureRoot('restore_db',True)
        if self.radio_convert.get_active():
            self._markReconfigureRoot('convert',self.managedfilechooser.get_filename())

    #
    # Callbacks
    #

    def on_restore_file_selection_changed(self,widget,data=None):
        if self.restore_file.get_filename():
            tarfilename=self.restore_file.get_filename()
            if tarfile.is_tarfile(tarfilename):
                TF=tarfile.open(tarfilename,mode='r:gz')
                for line in TF.getnames():
                    if line.endswith('sql.gz'):
                        self.RESTORE_DB_EXISTS=True
                        break
                    else:
                        self.RESTORE_DB_EXISTS=False
                        self.restore_db.set_active(False)
                TF.close()
                self.IsTarFile=True
                self.restore_label_isfile.hide()
            else:
                self.restore_label_isfile.show()

    def on_managedfilechooser_selection_changed(self,widget,data=None):
        if self.managedfilechooser.get_filename():
            newconfigfilename=self.managedfilechooser.get_filename()
            newconfig = configparser.ConfigParser()
            newconfig.read(newconfigfilename)
            self.changes['checksum'] = newconfig.get("General", "checksum")
            newconfig.set("General", "checksum", "XXXXXXXXXX")
            with open("/tmp/mythbuntu-bare.tmp", 'w') as conffile:
                newconfig.write(conffile)
            h = hashlib.sha1()
            f = open("/tmp/mythbuntu-bare.tmp", "rb")
            h.update(f.read())
            hsh = h.hexdigest()
            f.close()
            if hsh == self.changes['checksum']:
                try:
                    self.changes['serverip'] = newconfig.get("General", "serverip")
                    self.changes['serverport'] = newconfig.get("General", "serverport")
                    self.changes['managed'] = newconfig.get("General", "managed")
                    self.changes['BackupLocation'] = newconfig.get("Backup", "storagedir")
                    self.changes['backup_db'] = newconfig.get("Backup", "db")
                    self.changes['backup_schedule_day'] = newconfig.get("Backup", "day")
                    self.changes['backup_schedule_hour'] = newconfig.get("Backup", "hour")
                    self.changes['backup_schedule_minute'] = newconfig.get("Backup", "minute")
                    self.changes['backup_schedule_weekday'] = newconfig.get("Backup", "weekday")
                    self.changes['backup_schedule'] = newconfig.get("Backup", "schedule")
                    self.ValidNewConf = True
                    self.managed_label_isfile.hide()
                except:
                    self.ValidNewConf = False
                    self.managed_label_isfile.show()
            else:
                print("Error: Signature failure. Configuration file possibly corrupt.")

    def on_button_started_clicked(self,widget,data=None):
        self.tab_history=[]
        self.CUR_TAB = "vbox_choices"
        self.tab_history.append("vbox_start")
        self.vbox_start.hide()
        self.vbox_choices.show()
        self.button_box.show()
        self.next_button.set_sensitive(True)

    def on_next_button_clicked(self,widget,data=None):
        self.change_tab(self.CUR_TAB,"next")

    def on_back_button_clicked(self,widget,data=None):
        self.hide_tabs()
        if self.CUR_TAB == "vbox_final":
            self.next_button.set_sensitive(True)
        self.CUR_TAB = self.tab_history.pop()
        f = getattr(self, self.CUR_TAB)
        f.show()
        if self.CUR_TAB == "vbox_start":
            self.button_box.hide()

    def change_tab(self,CURRENT,DIRECTION):
        """Monkee around to display the correct wizard page"""
        self.hide_tabs()
        if self.CUR_TAB == "vbox_choices":
            if self.radio_backup_default_off.get_active() == False:
                self.tab_history.append(self.CUR_TAB)
                if self.radio_backup.get_active():
                    self.CUR_TAB = "vbox_backup_choice"
                    self.vbox_backup_choice.show()
                elif self.radio_convert.get_active():
                    self.CUR_TAB = "vbox_managed_convert"
                    self.vbox_managed_convert.show()
                else:
                    self.CUR_TAB = "vbox_restore"
                    self.vbox_restore.show()
            else:
                self.vbox_choices.show()
        elif self.CUR_TAB == "vbox_backup_choice":
            self.tab_history.append(self.CUR_TAB)
            if self.backup_now.get_active():
                self.CUR_TAB = "vbox_backup_location"
                self.vbox_backup_location.show()
                self.backup_type = "now"
            elif self.backup_schedule.get_active():
                self.CUR_TAB = "vbox_set_schedule"
                self.vbox_set_schedule.show()
                self.backup_type = "scheduled"
            else:
                self.vbox_backup_choice.show()
        elif self.CUR_TAB == "vbox_restore":
            if self.IsTarFile == True:
                self.tab_history.append(self.CUR_TAB)
                if self.RESTORE_DB_EXISTS:
                    self.CUR_TAB = "vbox_restore_db"
                    self.vbox_restore_db.show()
                else:
                    self.CUR_TAB = "vbox_final"
                    self.vbox_final.show()
                    self.next_button.set_sensitive(False)
            else:
                self.vbox_restore.show()
        elif self.CUR_TAB == "vbox_restore_db":
            self.tab_history.append(self.CUR_TAB)
            self.CUR_TAB = "vbox_final"
            self.vbox_final.show()
            self.next_button.set_sensitive(False)
        elif self.CUR_TAB == "vbox_backup_location":
            #from u1files import U1Files
            if self.backup_dir_button.get_active():# or (self.BACKUP_LOCATION == "U1" and U1Files().login()):
                self.tab_history.append(self.CUR_TAB)
                self.CUR_TAB = "vbox_final"
                self.vbox_final.show()
                self.next_button.set_sensitive(False)
        elif self.CUR_TAB == "vbox_set_schedule":
            if self.schedule_daily_button.get_active() or (self.schedule_monthly_button.get_active() and not self.monthly_combobox.get_active_text() == None) or (self.schedule_weekly_button.get_active() and not self.weekly_combobox.get_active_text() == None):
                self.tab_history.append(self.CUR_TAB)
                self.CUR_TAB = "vbox_backup_location"
                self.vbox_backup_location.show()
            elif self.schedule_disabled_button.get_active():
                self.tab_history.append(self.CUR_TAB)
                self.CUR_TAB = "vbox_final"
                self.vbox_final.show()
                self.next_button.set_sensitive(False)
        elif self.CUR_TAB == "vbox_managed_convert":
            if self.ValidNewConf == True:
                self.tab_history.append(self.CUR_TAB)
                self.CUR_TAB = "vbox_final"
                self.vbox_final.show()
                self.next_button.set_sensitive(False)
            else:
                self.vbox_managed_convert.show()

    def backup_setter(self,widget,data=None):
        radio_convert = self.radio_convert.get_active()
        radio_restore = self.radio_restore.get_active()
        radio_backup = self.radio_backup.get_active()
        self.set_final_label()

    def schedule_setter(self,widget,data=None):
        self.hbox_schedule.hide()
        self.hbox_schedule_weekly.hide()
        self.hbox_schedule_monthly.hide()
        self.monthly_text_label.hide()
        self.monthly_the_label.hide()
        self.schedule_label_sent.hide()
        texthour = self.schedule_hour_combobox.get_active_text()
        textminute = self.schedule_minute_combobox.get_active_text()
        if self.schedule_weekly_button.get_active():
            self.schedule_label_sent.show()
            self.hbox_schedule.show()
            self.hbox_schedule_weekly.show()
            self.schedule_label_sent.set_text("Scheduled backup will be set to run weekly on "+str(self.weekly_combobox.get_active_text())+" at "+texthour+":"+textminute)
        elif self.schedule_monthly_button.get_active():
            self.schedule_label_sent.show()
            self.monthly_text_label.show()
            self.monthly_the_label.show()
            self.hbox_schedule.show()
            self.hbox_schedule_monthly.show()
            self.schedule_label_sent.set_text("Scheduled backup will be set to run monthly on the "+str(self.monthly_combobox.get_active_text())+" day at "+texthour+":"+textminute)
        elif self.schedule_daily_button.get_active():
            self.schedule_label_sent.show()
            self.schedule_label_sent.set_text("Scheduled backup will be set to run daily at "+texthour+":"+textminute)
        self.set_final_label()

    def set_final_label(self):
        if self.radio_convert.get_active():
            self.job_label.set_text("Please click apply to convert the machine to managed mode")
        if self.radio_restore.get_active():
            self.job_label.set_text("Please click apply to perform the restore procedure")
        if self.radio_backup.get_active():
            if self.backup_schedule.get_active():
                self.job_label.set_text("Please click apply to perform the specified scheduling changes")
            else:
                self.job_label.set_text("Please click apply to perform the backup procedure")

    def on_backup_dir_button_toggled(self,widget,data=None):
        """Prompt user for save location if needed"""
        self.hide_all()
        if self.backup_dir_button.get_active():
            self.backup_location.show()
        elif self.backup_u1_button.get_active():
            self.preconfigured_label.show()
            self.BACKUP_LOCATION = "U1"
        elif self.backup_dropbox_button.get_active():
            self.preconfigured_label.show()
            self.BACKUP_LOCATION = "DROPBOX"
        else:        
            print("Error: Unknown option selected")

#    def on_backup_schedule_toggled(self,widget,data=None):
#        """Show backup schedule options if needed"""
#        if self.backup_schedule.get_active():
#            self.backup_schedule_status.show()
#            if self.changes['BackupLocation'] != "NOWHERE":
#                self.backup_location.set_filename(self.changes['BackupLocation'])
#        else:
#            self.backup_schedule_status.hide()

    def hide_tabs(self):
        self.vbox_start.hide()
        self.vbox_restore.hide()
        self.vbox_restore_db.hide()
        self.vbox_backup_schedule.hide()
        self.vbox_choices.hide()
        self.vbox_backup_choice.hide()
        self.vbox_backup_location.hide()
        self.vbox_final.hide()
        self.vbox_set_schedule.hide()
        self.vbox_managed_convert.hide()

    def hide_all(self):
        """Hide everything before showing what is needed"""
        self.preconfigured_label.hide()
        self.backup_location.hide()
        self.hbox_schedule.hide()
        self.hbox_schedule_weekly.hide()
        self.hbox_schedule_monthly.hide()
        self.monthly_text_label.hide()
        self.monthly_the_label.hide()
        self.schedule_label_sent.hide()

    #
    # Process selected activities
    #

    def root_scripted_changes(self,reconfigure):
        """System-wide changes that need root access to be applied.
           This function is ran by the dbus backend"""
        self.config.add_section("Backup")
        time.sleep(2)
        RESTORE_SET=False
        RESTORE_DB=False
        STATUSFILE = "/tmp/mythbuntu-bare-status"
        for item in reconfigure:
            if item == "Save Backup Location":
                self.BACKUP_LOCATION = reconfigure[item]
        for item in reconfigure:
            if item == "BackupSchedule":
                self.emit_progress("Processing Backup Schedule", 10)
                if reconfigure[item] == False:
                    self.emit_progress("Removing scheduled job", 50)
                    if os.path.isfile("/etc/cron.d/mythbuntu-bare"):
                        os.remove("/etc/cron.d/mythbuntu-bare")
                elif reconfigure[item] == True:
                    CONFIGFILE = "/var/lib/mythtv/bare-client/mythbuntu-bare-client.conf"
                    self.emit_progress("Saving Backup Settings", 30)
                    import configparser
                    config = configparser.ConfigParser()
                    config.add_section('General')
                    config.add_section('Backup')
                    config.set('General', 'serverip', '127.0.0.1')
                    config.set('General', 'managed', 'false')
                    config.set('General', 'revision', '1')
                    for subitem in reconfigure:
                        if subitem == 'BackupHour':
                            config.set('Backup', 'hour', reconfigure[subitem])
                        elif subitem == 'BackupMinute':
                            config.set('Backup', 'minute', reconfigure[subitem])
                        elif subitem == 'BackupLocation':
                            config.set('Backup', 'storagedir', reconfigure[subitem])
                        elif subitem == 'BackupDatabase':
                            config.set('Backup', 'db', reconfigure[subitem])
                        elif subitem == 'BackupDaily':
                            config.set('Backup', 'schedule', 'daily')
                            config.set('Backup', 'day', '*')
                            config.set('Backup', 'weekday', '*')
                        elif subitem == 'BackupWeekly':
                            config.set('Backup', 'schedule', 'weekly')
                            config.set('Backup', 'day', '*')
                            config.set('Backup', 'weekday', reconfigure[subitem])
                        elif subitem == 'BackupMonthly':
                            config.set('Backup', 'schedule', 'monthly')
                            config.set('Backup', 'day', reconfigure[subitem])
                            config.set('Backup', 'weekday', '*')
                    with open(CONFIGFILE, 'wb') as conffile:
                        config.write(conffile)
                    sys.path.append("/usr/share/mythbuntu/bare")
                    from updater import CronUpdater
                    self.emit_progress("Saving Backup Schedule", 10)
                    CronUpdater().generate_cron_schedule()
            elif item == "restore":
                RESTORE_LOCATION=reconfigure[item]
                RESTORE_SET=True
            elif item == "restore_db":
                RESTORE_DB=True
            elif item == "convert":
                PATH=reconfigure[item]
                try:
                    shutil.move(PATH, "/var/lib/mythtv/bare-client/mythbuntu-bare-client.conf")
                    sys.path.append("/usr/share/mythbuntu/bare")
                    from updater import CronUpdater
                    CronUpdater().generate_cron_schedule()
                except:
                    print("Unable to import file")
        if RESTORE_SET == True:
            rj = Thread(target=Restore().restore_job, args=(RESTORE_LOCATION,RESTORE_DB))
            rj.start()
            self.emit_progress("Restoring files", 20)
            time.sleep(2)
            status=20
            while rj.isAlive():
                f = open(STATUSFILE, 'r')
                ANS1 = f.readline().rstrip('\n')
                ANS2 = f.readline().rstrip('\n')
                status = int(ANS1)
                self.emit_progress(ANS2, status)
                f.close()
                while ANS2 == 'Restoring database (this could take a few minutes)':
                    f = open(STATUSFILE, 'r')
                    ANS1 = f.readline().rstrip('\n')
                    ANS2 = f.readline().rstrip('\n')
                    f.close()
                    if status <= 90:
                        status=status+0.1
                        self.emit_progress(ANS2, status)
                        time.sleep(0.1)
                time.sleep(0.1)
            self.emit_progress("Restore Job Complete", 100)
        time.sleep(2)

    def user_scripted_changes(self,reconfigure):
        """Local changes that can be performed by the user account.
           This function will be ran by the frontend"""
        time.sleep(0.5)
        for item in reconfigure:
            if item == "BackupNow":
                STATUSFILE = "/tmp/mythbuntu-bare-status"
                bj = Thread(target=Backup().backup_job, args=(self.BACKUP_DB,self.BACKUP_LOCATION))
                bj.start()
                self.emit_progress("Starting Backup", 10)
                time.sleep(2)
                status=10
                while bj.isAlive():
                    f = open(STATUSFILE, 'r')
                    ANS1 = f.readline().rstrip('\n')
                    ANS2 = f.readline().rstrip('\n')
                    status = int(ANS1)
                    self.emit_progress(ANS2, status)
                    f.close()
                    while ANS2 == 'Backing up database (this could take a few minutes)':
                        f = open(STATUSFILE, 'r')
                        ANS1 = f.readline().rstrip('\n')
                        ANS2 = f.readline().rstrip('\n')
                        f.close()
                        if status <= 85:
                            status=status+0.1
                            self.emit_progress(ANS2, status)
                            time.sleep(0.1)
                    time.sleep(0.1)
        self.emit_progress("Backup Finished", 100)
        time.sleep(2)
