#!/usr/bin/python3
# vim:et:sta:sts=4:sw=4:ts=8:tw=79:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import os
import subprocess
import re
import shutil


# Internationalization
import locale
import gettext
locale.setlocale(locale.LC_ALL, '')
locale.setlocale(locale.LC_ALL, '')
locale.bindtextdomain("dotnew", "/usr/share/locale")
gettext.bindtextdomain("dotnew", "/usr/share/locale")
gettext.textdomain("dotnew")
_ = gettext.gettext

def configfiles():
    filelist = []
    for dirpath, dirname, files in os.walk('/etc'):
        for file in files:
            if file.endswith('.new'):
                filelist.append(os.path.join(dirpath, file))
    return filelist


def getdiff(conffile):
    res = subprocess.getoutput(
        'diff -Naur {0} {0}.new'.format(conffile))
    return res

def exec_exists(path):
    return os.access(path, os.X_OK)

def get_terminal():
    try:
        # First see if we're inside a standard desktop session and pick the
        # appropriate terminal if it's there. If using sudo to start
        # dotnew-gtk, you need to use the -E option for the variable to exist.
        xdg_session = os.environ['XDG_CURRENT_DESKTOP']
        if xdg_session == 'XFCE' and exec_exists('/usr/bin/xfce4-terminal'):
            return '/usr/bin/xfce4-terminal -e '
        if xdg_session == 'MATE' and exec_exists('/usr/bin/mate-terminal'):
            return '/usr/bin/mate-terminal -e '
        if xdg_session == 'KDE' and exec_exists('/usr/bin/konsole'):
            return '/usr/bin/konsole -e '
        if xdg_session == 'LXDE' and exec_exists('/usr/bin/lxterminal'):
            return '/usr/bin/lxterminal -e '
        if xdg_session == 'LXQt' and exec_exists('/usr/bin/qterminal'):
            return '/usr/bin/qterminal -e '
        if xdg_session == 'GNOME' and exec_exists('/usr/bin/gnome-terminal'):
            return '/usr/bin/gnome-terminal -e '
    except KeyError:
        pass
    # or fall back to anything that is installed, in this order
    if exec_exists('/usr/bin/xfce4-terminal'):
        return '/usr/bin/xfce4-terminal -e '
    if exec_exists('/usr/bin/mate-terminal'):
        return '/usr/bin/mate-terminal -e '
    if exec_exists('/usr/bin/konsole'):
        return '/usr/bin/konsole -e '
    if exec_exists('/usr/bin/lxterminal'):
        return '/usr/bin/lxterminal -e '
    if exec_exists('/usr/bin/qterminal'):
        return '/usr/bin/qterminal -e '
    if exec_exists('/usr/bin/gnome-terminal'):
        return '/usr/bin/gnome-terminal -e '
    if exec_exists('/usr/bin/xterm'):
        return '/usr/bin/xterm -e '
    if exec_exists('/usr/bin/urxvt'):
        return '/usr/bin/urxvt -e '
    return None


class DotnewGTK:

    def create_file_list(self):
        self.liststore_files.clear()
        for i in configfiles():
            self.liststore_files.append(
                [False, False, re.sub('\.new$', '', i)])

    def on_button_about_clicked(self, widget, data=None):
        self.aboutdialog.show()

    def on_aboutdialog_response(self, widget, data=None):
        self.aboutdialog.hide()

    def on_aboutdialog_delete_event(self, widget, event):
        self.aboutdialog.hide()
        return True

    def on_button_ok_clicked(self, widget, data=None):
        for i in self.liststore_files:
            if i[0] == 1:
                if os.path.exists(i[2]):
                    try:
                        shutil.copy2(i[2], i[2] + '.orig')
                    except IOError:
                        pass
                try:
                    shutil.move(i[2] + '.new', i[2])
                except IOError:
                    pass
            else:
                if i[1] == 1:
                    if os.path.exists(i[2] + '.new'):
                        try:
                            os.remove(i[2] + '.new')
                        except OSError:
                            pass
        Gtk.main_quit()

    def on_button_cancel_clicked(self, widget, data=None):
        Gtk.main_quit()

    def gtk_main_quit(self, widget, data=None):
        Gtk.main_quit()

    global replace_clicked
    replace_clicked = False
    global delete_clicked
    delete_clicked = False

    def on_checkbox_replace_toggled(self, widget, data=None):
        global replace_clicked
        replace_clicked = True

    def on_checkbox_delete_toggled(self, widget, data=None):
        global delete_clicked
        delete_clicked = True

    def on_treeview_files_cursor_changed(self, widget, data=None):
        global replace_clicked
        global delete_clicked
        selectedfile = self.treeview_files.get_selection()
        self.liststore_files, iter = selectedfile.get_selected()
        if iter is not None:
            selectedfilename = self.liststore_files.get_value(iter, 2)
            if replace_clicked == True:
                for i in self.liststore_files:
                    if i[2] == selectedfilename:
                        if i[0] == 0:
                            i[0] = 1
                            i[1] = 0
                        else:
                            i[0] = 0
                replace_clicked = False
            if delete_clicked == True:
                for i in self.liststore_files:
                    if i[2] == selectedfilename:
                        if i[1] == 0:
                            i[1] = 1
                            i[0] = 0
                        else:
                            i[1] = 0
                delete_clicked = False

    def on_button_compare_clicked(self, widget, data=None):
        try:
            selectedfile = self.treeview_files.get_selection()
            self.liststore_files, iter = selectedfile.get_selected()
            selectedfilename = self.liststore_files.get_value(iter, 2)
            diff = getdiff(selectedfilename)
            self.textbuffer_diff.set_text(diff)
            self.dialog_diff.set_title(
                selectedfilename + ' ' + _('vs') + ' ' + selectedfilename + '.new')
            self.dialog_diff.show()
        except TypeError:
            pass

    def on_button_vim_clicked(self, widget, data=None):
        if self.has_gvim:
            self.launch_merge_tool('/usr/bin/gvimdiff')
        elif self.has_vim:
            self.launch_merge_tool('/usr/bin/vimdiff', use_terminal = True)

    def on_button_neovim_clicked(self, widget, data=None):
        self.launch_merge_tool('/usr/bin/nvim -d', use_terminal = True)

    def on_button_meld_clicked(self, widget, data=None):
        self.launch_merge_tool('/usr/bin/meld')

    def on_button_diffuse_clicked(self, widget, data=None):
        self.launch_merge_tool('/usr/bin/diffuse')

    def on_button_kompare_clicked(self, widget, data=None):
        self.launch_merge_tool('/usr/bin/kompare')

    def on_button_kdiff3_clicked(self, widget, data=None):
        self.launch_merge_tool('/usr/bin/kdiff3')

    def on_button_xxdiff_clicked(self, widget, data=None):
        self.launch_merge_tool('/usr/bin/xxdiff')

    def on_button_tkdiff_clicked(self, widget, data=None):
        self.launch_merge_tool('/usr/bin/tkdiff')

    def launch_merge_tool(self, merge_tool, use_terminal = False):
        self.popover_compare.hide()
        try:
            selectedfile = self.treeview_files.get_selection()
            self.liststore_files, iter = selectedfile.get_selected()
            selectedfilename = self.liststore_files.get_value(iter, 2)
            if use_terminal:
                if self.terminal:
                    # careful with the spaces here
                    os.system(self.terminal + ' "' + merge_tool + ' {0} {0}.new"'.format(selectedfilename))
                else:
                    print('No terminal emulator found.')
            elif self.has_dbus:
                # careful with the spaces here
                os.system('/usr/bin/dbus-launch ' + merge_tool + ' {0} {0}.new'.format(selectedfilename))
            else:
                # careful with the spaces here
                os.system(merge_tool + ' {0} {0}.new'.format(selectedfilename))
        except TypeError:
            pass

    def on_button_replace_all_clicked(self, widget, data=None):
        for i in self.liststore_files:
            i[0] = 1
            i[1] = 0

    def on_button_delete_all_clicked(self, widget, data=None):
        for i in self.liststore_files:
            i[0] = 0
            i[1] = 1

    def on_button_none_clicked(self, widget, data=None):
        for i in self.liststore_files:
            i[0] = 0
            i[1] = 0

    def on_button_refresh_clicked(self, widget, data=None):
        self.create_file_list()

    def on_button_merge_clicked(self, widget, data=None):
        # only show the popover if there are 2 or more options
        if self.merge_options == 1:
            self.launch_merge_tool(self.merge_tool,
                    use_terminal = self.merge_tool_use_terminal)
        else:
            self.popover_compare.show()

    def on_dialog_diff_delete_event(self, widget, event):
        self.dialog_diff.hide()
        return True

    def on_button_diff_ok_clicked(self, widget):
        self.dialog_diff.hide()

    def on_dialog_nonew_delete_event(self, widget, event):
        self.dialog_nonew.hide()
        return True

    def on_button_nonew_ok_clicked(self, widget):
        self.dialog_nonew.hide()

    def __init__(self):
        builder = Gtk.Builder()
        builder.set_translation_domain("dotnew")
        if os.path.exists('dotnew-gtk.ui'):
            builder.add_from_file('dotnew-gtk.ui')
        elif os.path.exists('/usr/share/dotnew/dotnew-gtk.ui'):
            builder.add_from_file('/usr/share/dotnew/dotnew-gtk.ui')
        self.window = builder.get_object('dotnew')

        #
        # Main window
        #
        self.button_compare = builder.get_object('button_compare')
        self.button_merge = builder.get_object('button_merge')
        self.popover_compare = builder.get_object('popover_compare')
        self.flowboxchild_vim = builder.get_object('flowboxchild_vim')
        self.flowboxchild_neovim = builder.get_object('flowboxchild_neovim')
        self.flowboxchild_meld = builder.get_object('flowboxchild_meld')
        self.flowboxchild_diffuse = builder.get_object('flowboxchild_diffuse')
        self.flowboxchild_kompare = builder.get_object('flowboxchild_kompare')
        self.flowboxchild_kdiff3 = builder.get_object('flowboxchild_kdiff3')
        self.flowboxchild_xxdiff = builder.get_object('flowboxchild_xxdiff')
        self.flowboxchild_tkdiff = builder.get_object('flowboxchild_tkdiff')

        # determine capabilities
        self.has_gvim = False
        self.has_vim = False
        self.merge_options = 0
        self.merge_tool = None   # only used when only one tool is installed
        self.merge_tool_use_terminal = False
        if exec_exists('/usr/bin/gvimdiff'):
            self.has_gvim = True
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/gvimdiff'
            self.flowboxchild_vim.show()
        elif exec_exists('/usr/bin/vimdiff'):
            self.has_vim = True
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/vimdiff'
            self.merge_tool_use_terminal = True
            self.flowboxchild_vim.show()
        if exec_exists('/usr/bin/nvim'):
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/nvim'
            self.merge_tool_use_terminal = True
            self.flowboxchild_neovim.show()
        if exec_exists('/usr/bin/meld'):
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/meld'
            self.flowboxchild_meld.show()
        if exec_exists('/usr/bin/diffuse'):
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/diffuse'
            self.flowboxchild_diffuse.show()
        if exec_exists('/usr/bin/kompare'):
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/kompare'
            self.flowboxchild_kompare.show()
        if exec_exists('/usr/bin/kdiff3'):
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/kdiff3'
            self.flowboxchild_kdiff3.show()
        if exec_exists('/usr/bin/xxdiff'):
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/xxdiff'
            self.flowboxchild_xxdiff.show()
        if exec_exists('/usr/bin/tkdiff'):
            self.merge_options = self.merge_options + 1
            self.merge_tool = '/usr/bin/tkdiff'
            self.flowboxchild_tkdiff.show()
        self.has_dbus = exec_exists('/usr/bin/dbus-launch')
        # hide the merge button if there is no merge tool installed
        if self.merge_options == 0:
            self.button_merge.hide()

        self.treeview_files = builder.get_object('treeview_files')
        self.treeviewcolumn_replace = builder.get_object(
            'treeviewcolumn_replace')
        self.treeviewcolumn_replace.set_title(_('Replace'))
        self.treeviewcolumn_delete = builder.get_object(
            'treeviewcolumn_delete')
        self.treeviewcolumn_delete.set_title(_('Delete'))
        self.treeviewcolumn_file = builder.get_object('treeviewcolumn_file')
        self.treeviewcolumn_file.set_title(_('Configuration file'))

        self.liststore_files = builder.get_object('liststore_files')
        self.liststore_files.clear()
        self.create_file_list()

        #
        # About dialog
        #
        self.aboutdialog = builder.get_object('aboutdialog')

        #
        # Diff window
        #
        self.dialog_diff = builder.get_object('dialog_diff')
        self.button_diff_ok = builder.get_object('button_diff_ok')
        self.textview_diff = builder.get_object('textview_diff')
        self.textview_diff.set_monospace(True)
        self.textbuffer_diff = builder.get_object('textbuffer_diff')

        #
        # No .new files window
        #
        self.dialog_nonew = builder.get_object('dialog_nonew')
        self.button_nonew_ok = builder.get_object('button_nonew_ok')

        # Connect signals
        builder.connect_signals(self)
        self.window.show()

        # Get favorable terminal
        self.terminal = get_terminal()

        # If no .new files are present, show a message
        count = 0
        for i in self.liststore_files:
            count += 1
        if count == 0:
            self.dialog_nonew.show()

if __name__ == "__main__":
    app = DotnewGTK()
    app.window.show()
    Gtk.main()
