#!/usr/bin/python3
import gi, os, re, jwmkit_utils
from subprocess import run, PIPE
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


# JWM Kit - A set of Graphical Apps to simplify use of JWM (Joe's Window Manager) <https://codeberg.org/JWMKit/JWM_Kit>
# Copyright ©  2020-2022 Calvin Kent McNabb <apps.jwmkit@gmail.com>
#
# This file is part of JWM Kit.
#
# JWM Kit is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# JWM Kit 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 JWM Kit.  If not, see <https://www.gnu.org/licenses/>.

def get_current_zone():
    cmd = "find /usr/share/zoneinfo/ -type f | xargs md5sum | grep $(md5sum /etc/localtime | cut -d' ' -f1)"
    f = os.popen(cmd).read()
    f = re.findall('zoneinfo/(.*)', f)
    if len(f) != 0:
        return f[-1]


def pad_spinner(spinner):
    adjustment = spinner.get_adjustment()
    spinner.set_text('{:02d}'.format(int(adjustment.get_value())))
    return True


class KitWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="JWM Kit Time Config")
        main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20)
        self.set_border_width(30)
        try:
            self.set_icon_from_file('/usr/share/pixmaps/jwmkit/clockgray.svg')
        except gi.repository.GLib.Error:
            self.set_icon_name('edit-paste')

        # header above widgets
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
        label = Gtk.Label()
        label.set_markup('<big><b>Time Config</b></big>\nConfigure System Time &amp; Zone')
        box.add(jwmkit_utils.create_image('/usr/share/pixmaps/jwmkit/calgray.svg', 64, 64, True))
        box.add(label)
        main_box.add(box)

        # Time zone label
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        self.add(main_box)
        main_box.pack_start(box, False, False, 0)
        label = Gtk.Label()
        label.set_markup('<b>Time Zone</b>')

        zone_reset_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_UNDO))
        zone_reset_button.set_tooltip_text('Revert to current system value')
        zone_reset_button.set_always_show_image(True)
        zone_reset_button.connect('clicked', self.reset_zone)

        self.zone_switch = Gtk.Switch()
        self.zone_switch.set_tooltip_text('Configure Time Zone : OFF/ON')
        self.zone_switch.set_active(False)

        box.pack_start(label, False, False, 0)
        box.pack_start(zone_reset_button, False, False, 0)
        box.pack_start(self.zone_switch, False, False, 0)

        # time zone combo
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        self.zone_combo = Gtk.ComboBoxText()
        self.zone_combo.set_property("width-request", 255)
        self.zone_data = os.popen('cd /usr/share/zoneinfo/posix && find * -type f -or -type l | sort').read().split('\n')
        for item in self.zone_data:
            self.zone_combo.append_text(item)
        self.reset_zone('')
        self.zone_combo.set_sensitive(False)

        box.pack_start(self.zone_combo, False, False, 0)
        main_box.pack_start(box, True, False, 0)

        # Date Label
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        main_box.pack_start(box, False, False, 0)
        label = Gtk.Label()
        label.set_markup('<b>Date\t\t</b>')

        date_reset_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_UNDO))
        date_reset_button.set_tooltip_text('Revert to current system value')
        date_reset_button.set_always_show_image(True)
        date_reset_button.connect('clicked', self.date_reset)

        self.date_switch = Gtk.Switch()
        self.date_switch.set_tooltip_text('Configure Date: OFF/ON')

        box.pack_start(label, False, False, 0)
        box.pack_start(date_reset_button, False, False, 0)
        box.pack_start(self.date_switch, False, False, 0)

        # Date Widgets
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        main_box.pack_start(box, True, False, 0)
        months = ['January', 'February', 'March', 'April', 'May', 'June', 'July',
                  'August', 'September', 'October', 'November', 'December']

        self.month_combo = Gtk.ComboBoxText()
        self.month_combo.set_property("width-request", 255)
        for month in months:
            self.month_combo.append_text(month)

        self.month_combo.set_active(int(os.popen('date +%m').read()) - 1)
        self.day_spin = Gtk.SpinButton()
        self.day_spin.set_tooltip_text("Day")
        self.day_spin.connect('output', pad_spinner)
        self.day_spin.set_adjustment(Gtk.Adjustment(value=int(os.popen('date +%d').read()),
                                                    lower=1, upper=31, step_increment=1))

        self.year_spin = Gtk.SpinButton()
        self.year_spin.set_tooltip_text("Year")
        self.year_spin.set_adjustment(Gtk.Adjustment(value=int(os.popen('date +%Y').read()),
                                                     lower=2021, upper=3000, step_increment=1))

        box.pack_start(self.month_combo, False, False, 0)
        box.pack_start(self.day_spin, False, False, 0)
        box.pack_start(self.year_spin, False, False, 0)

        # Clock Label
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        main_box.pack_start(box, False, False, 0)
        label = Gtk.Label()
        label.set_markup('<b>Clock    \t</b>')
        clock_reset_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_UNDO))
        clock_reset_button.set_tooltip_text('Revert to current system value')
        clock_reset_button.set_always_show_image(True)
        clock_reset_button.connect('clicked', self.clock_reset)
        self.clock_switch = Gtk.Switch()
        self.clock_switch.set_tooltip_text('Configure Clock : OFF/ON')
        self.clock_switch.set_active(True)
        box.pack_start(label, False, False, 0)
        box.pack_start(clock_reset_button, False, False, 0)
        box.pack_start(self.clock_switch, False, False, 0)

        # clock widgets
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)

        self.hr_spin = Gtk.SpinButton()
        self.hr_spin.set_tooltip_text("Hour (24 Hour)")
        self.hr_spin.set_adjustment(Gtk.Adjustment(value=int(os.popen('date +%H').read()),
                                                   lower=0, upper=24, step_increment=1))

        self.min_spin = Gtk.SpinButton()
        self.min_spin.set_tooltip_text("Minutes")
        self.min_spin.connect('output', pad_spinner)
        self.min_spin.set_adjustment(Gtk.Adjustment(value=int(os.popen('date +%M').read()),
                                                    lower=0, upper=59, step_increment=1))

        self.second_entry = Gtk.Entry()
        self.second_entry.set_max_length(2)
        self.second_entry.set_width_chars(4)
        self.second_entry.set_sensitive(False)
        self.second_entry.set_tooltip_text('Seconds')
        self.second_entry.set_text(os.popen('date +%S').read())

        self.second_check = Gtk.CheckButton()
        self.second_check.set_tooltip_text('Check to enter seconds.\nUncheck to synchronize with the current clock')
        self.second_check.connect("clicked", self.second_lock)
        self.second_check.set_active(False)

        box.pack_start(self.hr_spin, False, False, 0)
        box.pack_start(Gtk.Label(label=':'), False, False, 0)
        box.pack_start(self.min_spin, False, False, 0)
        box.pack_start(self.second_entry, False, False, 0)
        box.pack_start(self.second_check, False, False, 0)
        main_box.pack_start(box, True, False, 0)

        # Buttons at the bottom
        about_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_ABOUT))
        about_button.set_property("width-request", 35)
        about_button.set_always_show_image(True)
        about_button.connect('clicked', jwmkit_utils.get_about, self)

        close_button = Gtk.Button(label="Close", image=Gtk.Image(stock=Gtk.STOCK_CANCEL))
        close_button.connect("clicked", Gtk.main_quit)
        close_button.set_property("width-request", 90)
        close_button.set_always_show_image(True)

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        self.apply_button = Gtk.Button(label='Apply', image=Gtk.Image(stock=Gtk.STOCK_APPLY))
        self.apply_button.set_property("width-request", 90)
        self.apply_button.set_always_show_image(True)
        self.apply_button.connect("clicked", self.apply_time)
        self.zone_switch.connect("notify::active", self.toggle_zone)
        self.date_switch.connect("notify::active", self.toggle_date)
        self.clock_switch.connect("notify::active", self.toggle_clock)
        self.date_switch.set_active(False)
        self.toggle_date(self.date_switch, '')

        box.pack_start(about_button, False, False, 0)
        box.pack_end(close_button, False, False, 0)
        box.pack_end(self.apply_button, False, False, 0)
        main_box.pack_start(box, True, False, 0)

    def reset_zone(self, button):
        try:
            self.zone_combo.set_active(self.zone_data.index(get_current_zone()))
        except ValueError:
            self.zone_combo.set_active(-1)
            #  The Value is not in the list. zone_combo is cleared
            pass

    def date_reset(self, button):
        self.day_spin.set_value(int(os.popen('date +%d').read()))
        self.year_spin.set_value(int(os.popen('date +%Y').read()))
        self.month_combo.set_active(int(os.popen('date +%m').read()) - 1)

    def clock_reset(self, button):
        self.hr_spin.set_value(int(os.popen('date +%H').read()))
        self.min_spin.set_value(int(os.popen('date +%M').read()))
        self.second_entry.set_text(os.popen('date +%S').read())

    def toggle_apply(self):
        if self.clock_switch.get_active() or self.zone_switch.get_active() or self.date_switch.get_active():
            self.apply_button.set_sensitive(True)
        else:
            self.apply_button.set_sensitive(False)

    def toggle_clock(self, button, state):
        if button.get_active():
            if self.second_check.get_active():
                self.second_entry.set_sensitive(True)
            self.hr_spin.set_sensitive(True)
            self.min_spin.set_sensitive(True)
        else:
            self.second_entry.set_sensitive(False)
            self.hr_spin.set_sensitive(False)
            self.min_spin.set_sensitive(False)
        self.toggle_apply()

    def toggle_date(self, button, state):
        if button.get_active():
            self.day_spin.set_sensitive(True)
            self.year_spin.set_sensitive(True)
            self.month_combo.set_sensitive(True)
        else:
            self.day_spin.set_sensitive(False)
            self.year_spin.set_sensitive(False)
            self.month_combo.set_sensitive(False)
        self.toggle_apply()

    def toggle_zone(self, button, state):
        if button.get_active():
            self.zone_combo.set_sensitive(True)
        else:
            self.zone_combo.set_sensitive(False)
        self.toggle_apply()

    def second_lock(self, button):
        if button.get_active():
            if self.clock_switch.get_active():
                self.second_entry.set_sensitive(True)
        else:
            self.second_entry.set_sensitive(False)

    def apply_time(self, button):
        def prompt(command):
            if su == 'gksudo_r':
                suu = su[:-2]
                pw_data = os.popen('{} -p -m "Authorization Required" 2>/dev/null'.format(suu)).read().strip()
                pw_data = (True, pw_data)
            else:
                pw_data = jwmkit_utils.private_entry(self)
            cmd = 'echo "{}" | sudo -S -s eval \'{} ; echo success\''.format(pw_data[1], command)
            if not pw_data[0]:
                return
            else:
                print(cmd)
                auth_check(cmd)

        def auth_check(cmd):
            check = run(cmd, shell=True, stdout=PIPE)
            check = check.stdout.decode()
            if "success" in check:
                print("OK")
            else:
                print("Fail")

        def common_cmd():
            cmd_time, cmd_date, cmd = '', '', ''
            if self.zone_switch.get_active():
                zone = self.zone_data[self.zone_combo.get_active()]
                tmp1 = 'cp --remove-destination /usr/share/zoneinfo/{} /etc/timezone'.format(zone)
                tmp2 = 'cp --remove-destination /usr/share/zoneinfo/{} /etc/localtime'.format(zone)
                cmd = '{} ; {}'.format(tmp1, tmp2)
            if self.date_switch.get_active():
                year = str(int(self.year_spin.get_value())).zfill(2)
                day = str(int(self.day_spin.get_value())).zfill(2)
                month = str(self.month_combo.get_active() + 1).zfill(2)
                cmd_date = '{}-{}-{}'.format(year, month, day)
            else:
                cmd_date = os.popen('date +"%Y-%m-%d"').read().strip()
            if self.clock_switch.get_active():
                hour = str(int(self.hr_spin.get_value())).zfill(2)
                minute = str(int(self.min_spin.get_value())).zfill(2)
                if self.second_check.get_active():
                    second = self.second_entry.get_text()
                else:
                    second = os.popen('date +"%S"').read().strip()
                cmd_time = '{}:{}:{}'.format(hour, minute, second)
            else:
                cmd_time = os.popen('date +"%H:%M:%S"').read().strip()
            if self.date_switch.get_active() or self.clock_switch.get_active():
                cmd = '{}date -s "{} {}"'.format(cmd, cmd_date, cmd_time).replace('timedate -s', 'time ; date -s')
            else:
                cmd = '{}'.format(cmd)

            if su == 'sudoa':
                suu = "sudo -A -s eval '"
            elif su == 'sudo':
                suu = "sudo -s eval '"
            elif su == 'gksu_r':
                cmd = cmd.replace('"', '\\"')
                suu = "gksu '"
            elif su == 'su-to-root -X':
                cmd = cmd.replace('"', '\\"')
                suu = "{} -c '".format(su)
            elif su.startswith('term:'):
                cmd = cmd.replace('"', '\\"')
                suu = su.split(':')
                if suu[2] in ('su -c', 'su-to-root -c'):
                    suu = "{} -e \"{} '".format(suu[1], suu[2])
                elif suu[2] == 'sudo':
                    suu = "{} -e \"{} -s eval '".format(suu[1], suu[2])
                else:
                    suu = "{} -e \"{} eval '".format(suu[1], suu[2])
            elif su in ('gksudo', 'gksu'):
                suu = "{} -s eval '".format(su)
            else:
                suu = ''
            cmd = '{}{}'.format(suu, cmd)
            if su not in ('', 'sup', 'sudop', 'gksudo_r'):
                cmd = "{}'".format(cmd)
            if su.startswith('term:'):
                cmd = '{}"'.format(cmd)
            if su in ('sudop', 'gksudo_r'):
                prompt(cmd)
            else:
                os.system(cmd)

        def sysd_cmd():
            # remove print lines and enable os.system(cmd)
            zone = self.zone_data[self.zone_combo.get_active()]
            cmd = 'timedatectl set-timezone {}'.format(zone)
            os.system(cmd)
            year = str(int(self.year_spin.get_value())).zfill(2)
            day = str(int(self.day_spin.get_value())).zfill(2)
            month = str(self.month_combo.get_active() + 1).zfill(2)
            cmd = 'timedatectl set-time “{}-{}-{}”'.format(year, month, day)
            os.system(cmd)
            hour = str(int(self.hr_spin.get_value())).zfill(2)
            minute = str(int(self.min_spin.get_value())).zfill(2)
            second = self.second_entry.get_text()
            if self.clock_switch.get_active():
                cmd = 'timedatectl set-time {}:{}:{}'.format(hour, minute, second)
            else:
                cmd = 'timedatectl set-time {}:{}:$(date +%S)'.format(hour, minute)
            os.system(cmd)

        su = jwmkit_utils.get_su('time')
        if su == 'nosu':
            su = ''
        elif su.startswith('nox'):
            su = 'term{}:su-to-root -c'.format(su[3:])
        elif su.startswith('su:'):
            su = 'term{}:su -c'.format(su[2:])
        elif su.startswith('sudo:'):
            su = 'term{}:sudo'.format(su[4:])
        elif su == 'x':
            su = 'su-to-root -X'
        if su in ('sysd', 'systemd', 'timedatectl'):
            sysd_cmd()
        else:
            common_cmd()


window = KitWindow()
window.connect("delete-event", Gtk.main_quit)
window.set_position(Gtk.WindowPosition.CENTER)
window.set_resizable(False)
window.show_all()
Gtk.main()


# TODO
#  - prompt - Do not call again if pw_data[1] is true if variable is set.
