## -*- 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
import traceback
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(self.changes['backup_db'])
        except:
            print("WARNING: Failed to set from config file. Setting defaults")
            traceback.print_exc(file=sys.stdout)
            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)
        
        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))
        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 self.changes['backup_schedule_day'] == "*":
            self.monthly_combobox.set_active(0)
        if self.changes['backup_schedule_weekday'] == "*":
            self.weekly_combobox.set_active(0)
        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('Save Backup Location', self.BACKUP_LOCATION)
                    self._markReconfigureRoot('BackupDatabase', str(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', str(self.weekly_combobox.get_active()))
                    elif self.schedule_monthly_button.get_active():
                        self._markReconfigureRoot('BackupMonthly', str(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, 'w', encoding='utf-8') 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)
