#!/usr/bin/python3

import sys
import gi
import subprocess
import threading
import time
import os
import pwd
import re

import logging

gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, Gio, GLib, Graphene
from PIL import Image
import random
import string

SUPPORT_URL = "https://uncom.tech/support_channels"
IMAGES_DIR = "/usr/share/uncom-kernel68-update/pixmaps/"
IMAGE_LIGHT = "kernel68-light.png"
IMAGE_DARK = "kernel68-dark.png"

class MainWindow(Gtk.ApplicationWindow):

    workDone = False

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # window
        self.set_default_size(800, 800)
        self.set_title("Обновление системы")
        self.set_resizable(False)

        self.main_box = Gtk.Box(spacing=0, orientation=Gtk.Orientation.VERTICAL)
        self.set_child(self.main_box)

        # box1
        self.box1 = Gtk.Box(spacing=5, orientation=Gtk.Orientation.VERTICAL)
        self.box1.set_margin_start(20)
        self.box1.set_margin_end(20)
        self.box1.set_margin_top(20)
        self.box1.set_margin_bottom(20)
        self.main_box.append(self.box1)  

        # box1 label1
        self.label1 = Gtk.Label()
        self.label1.set_markup("<big>Вышло обновление Uncom OS!</big>")
        self.label1.set_wrap(True)
        self.label1.set_justify(Gtk.Justification.CENTER)
        self.label1.set_xalign(0.5)
        self.box1.append(self.label1)

        # box_picture
        self.box_picture = Gtk.Box(spacing=0, orientation=Gtk.Orientation.VERTICAL)
        self.box_picture.set_margin_start(0)
        self.box_picture.set_margin_end(0)
        self.box_picture.set_margin_top(0)
        self.box_picture.set_margin_bottom(0)
        self.main_box.append(self.box_picture)  

        # box_picture picture1
        self.picture1 = Gtk.Picture.new_for_filename(IMAGES_DIR + IMAGE_LIGHT)
        self.box_picture.append(self.picture1)

        # box2
        self.box2 = Gtk.Box(spacing=10, orientation=Gtk.Orientation.VERTICAL)
        self.box2.set_margin_start(20)
        self.box2.set_margin_end(20)
        self.box2.set_margin_top(20)
        self.box2.set_margin_bottom(20)
        self.main_box.append(self.box2) 

        # box2 label2
        self.label2 = Gtk.Label(label="Перед обновлением убедитесь, что не запущены другие программы установки и удаления ПО. Потребуется соединение с интернетом. Ноутбук необходимо подключить к питанию.")
        self.label2.set_wrap(True)
        self.label2.set_justify(Gtk.Justification.CENTER)
        self.label2.set_xalign(0.5)
        self.box2.append(self.label2)

        # box3
        self.box3 = Gtk.Box(spacing=10, orientation=Gtk.Orientation.VERTICAL)
        self.box2.set_margin_start(20)
        self.box2.set_margin_end(20)
        self.box2.set_margin_top(20)
        self.box2.set_margin_bottom(20)
        self.main_box.append(self.box3)

        # box3 box_buttons
        self.box_buttons = Gtk.Box(spacing=5, orientation=Gtk.Orientation.HORIZONTAL)
        self.box_buttons.set_margin_top(20)
        self.box3.append(self.box_buttons)
        self.box3.set_margin_bottom(20)

        self.button_cancel = Gtk.Button(label="Установить обновление позже")
        self.button_cancel.set_hexpand(True)
        self.button_cancel.set_halign(Gtk.Align.END)
        self.button_cancel.connect("clicked", self.on_button_cancel_clicked)
        self.box_buttons.append(self.button_cancel)

        self.button_apply = Gtk.Button(label="Обновить Uncom OS (около 15 минут)")
        self.button_apply.set_hexpand(True)
        self.button_apply.set_halign(Gtk.Align.START)
        self.button_apply.connect("clicked", self.on_button_update_clicked)
        self.box_buttons.append(self.button_apply)

        # box3 spinner
        self.spinner = Gtk.Spinner()
        self.box3.append(self.spinner)
        self.spinner.set_visible(True)
        self.spinner.set_vexpand(True)
        # self.spinner.start()

        # box3 label_A
        self.label_A = Gtk.Label(label="Вы не сможете вернуться на предыдущую версию системы.")
        self.label_A.set_valign(Gtk.Align.END)
        self.label_A.set_wrap(True)
        self.box3.append(self.label_A)

        # box3 button_support
#        self.button_support = Gtk.LinkButton(uri=SUPPORT_URL, label="Техподдержка")
#        self.label_A.set_valign(Gtk.Align.END)
#        self.box3.append(self.button_support)

        # end of UI formation

        self.header = Gtk.HeaderBar()
        self.set_titlebar(self.header)

        action = Gio.SimpleAction.new("show_about_dialog", None)
        action.connect("activate", self.show_about_dialog)
        self.add_action(action)

        # Create a new menu, containing that action
        menu = Gio.Menu.new()
        menu.append("О приложении", "win.show_about_dialog")

        # Create a popover
        self.popover = Gtk.PopoverMenu()  # Create a new popover menu
        self.popover.set_menu_model(menu)

        # Create a menu button
        self.hamburger = Gtk.MenuButton()
        self.hamburger.set_popover(self.popover)
        self.hamburger.set_icon_name("open-menu-symbolic")  # Give it a nice icon

        # Add menu button to the header bar
        self.header.pack_start(self.hamburger)


        # Update picture color when the theme variant changes
        Gtk.Settings.get_default().connect("notify::gtk-theme-name", self.on_theme_name_changed)
        self.update_picture_theme(Gtk.Settings().get_default().get_property("gtk-theme-name"))

    def on_theme_name_changed(self, settings, gparam):
        print("Theme name:", settings.get_property("gtk-theme-name"))
        self.update_picture_theme(settings.get_property("gtk-theme-name"))

    def update_picture_theme(self, themename):
        if "dark" in themename:
            self.picture1.set_filename(IMAGES_DIR + IMAGE_DARK)
        else:
            self.picture1.set_filename(IMAGES_DIR + IMAGE_LIGHT)

    def show_message_info(self, message, command):
        self.dialog = Gtk.MessageDialog(
            transient_for=self,
            message_type=Gtk.MessageType.INFO,
            buttons=Gtk.ButtonsType.OK_CANCEL,
            text=message,
        )

        self.dialog.connect('response', self.on_dialog_response_with_parameter(command))
        self.dialog.set_modal(True)
        self.dialog.show()

    def show_message_info_no_action(self, message):
        self.dialog = Gtk.MessageDialog(
            transient_for=self,
            message_type=Gtk.MessageType.INFO,
            buttons=Gtk.ButtonsType.OK,
            text=message,
        )
        def close_dialog(self, app):
             self.destroy()

        self.dialog.connect('response', close_dialog)
        self.dialog.set_modal(True)
        self.dialog.show()

    def on_dialog_response_with_parameter(self, additional_parameter):
        def close_dialog(dialog, response_id):
            if response_id == Gtk.ResponseType.OK:
                print("OK button clicked")
                thread = threading.Thread(target=self.run_command)
                thread.start()
            elif response_id == Gtk.ResponseType.CANCEL:
                print("Cancel button clicked")
                # Enable UI back
                self.button_apply.set_sensitive(True)
                self.button_cancel.set_sensitive(True)
                self.label_A.set_visible(True)
                self.spinner.stop()

            dialog.destroy()
        return close_dialog

    def show_about_dialog(self, action, param):
        print("Show about dialog")    
        self.about = Gtk.AboutDialog()
        self.about.set_program_name("Обновление системы")
        self.about.set_transient_for(self)  # Makes the dialog always appear in from of the parent window
        self.about.set_modal(self)  # Makes the parent window unresponsive while dialog is showing

        self.about.set_copyright("Copyright 2023 Uncom OS")
        self.about.set_license_type(Gtk.License.GPL_3_0)
        self.about.set_website("http://uncom.tech")
        self.about.set_website_label("Веб-страница")
        self.about.set_version("1.0")
        # The icon will need to be added to appropriate location
        # E.g. /usr/share/icons/hicolor/scalable/apps/org.example.example.svg
        self.about.set_logo_icon_name("uncom-kernel68-update")
        self.about.set_visible(True)

    def on_work_done(self):
        print("on_work_done: work is done!")
        with open('/var/log/uncom-kernel68-update.log') as f:
            for line in f:
                pass
            last_line = line
        logging.debug("on_work_done: last_line = %s", last_line)
        if "Final_success" in last_line:
            self.show_message_info_no_action("Обновление успешно установлено")
            self.label_A.set_label("Обновление успешно установлено, перезагрузите компьютер.")
        else:
            self.show_message_info_no_action("Обновление завершено с ошибками: /var/log/uncom-kernel68-update.log")
            self.label_A.set_label("Обновление завершено с ошибками: /var/log/uncom-kernel68-update.log")

    def on_button_cancel_clicked(self, button):
        print("Cancel clicked")
        exit(0)


    def run_command(self):
        # Disable sleep mode
        # subprocess.call(["echo lock_me > /sys/power/wake_lock"])
        # Run the command asynchronously
        subprocess.run(["/sbin/do-uncom-kernel68-update-launcher"])
        self.workDone = True
        # After the command finishes, hide the progress bar and re-enable the button
        # GLib.idle_add(lambda: self.spinner.set_visible(False))
        GLib.idle_add(lambda: self.spinner.stop())
        GLib.idle_add(lambda: self.button_apply.set_sensitive(True))
        GLib.idle_add(lambda: self.button_apply.set_label("Перезагрузить сейчас"))
        GLib.idle_add(lambda: self.button_cancel.set_sensitive(True))
        GLib.idle_add(lambda: self.button_cancel.set_label("Перезагрузить позже"))
        GLib.idle_add(lambda: self.label_A.set_visible(True))
        GLib.idle_add(lambda: self.on_work_done())
        # Enable sleep mode back
        # subprocess.call(["echo lock_me > /sys/power/wake_unlock"])

    def run_reboot(self):
        print("Rebooting...")
        logging.debug('Rebooting')
        subprocess.run(["/sbin/reboot"])


    def on_button_update_clicked(self, button):
        print("Apply clicked, workDone =", self.workDone)
        logging.debug("Apply clicked, workDone = %s", self.workDone)

        # Disable the button while the command is running
        self.button_apply.set_sensitive(False)
        self.button_cancel.set_sensitive(False)

        # Hide the label and show the spinner
        self.label_A.set_visible(False)
        # self.spinner.set_visible(True)
        self.spinner.start()

        if self.workDone:
            ok_to_start = 0
        else:
            ok_to_start = subprocess.call(["/sbin/check_kernel68_update_conditions"], shell=True, universal_newlines=True)
            print("check_update_conditions return code =", ok_to_start)

        logging.debug('ok_to_start = %s', ok_to_start)

        if (ok_to_start != 0):
            print("Can't start!")
            if (ok_to_start == 1):
                text_explain="Не удается обновить: нужен запуск от администратора."
            elif (ok_to_start == 2):
                text_explain="Не удается обновить: недоступен репозиторий Uncom OS, проверьте интернет."
            elif (ok_to_start == 3):
                text_explain="Не удается обновить: идёт работа с пакетами в другой программе."
            elif (ok_to_start == 4):
                text_explain="Не удается обновить: ядро версии 6.8 уже установлено."
            elif (ok_to_start == 5):
                text_explain="Не удается обновить: установлены пакеты i386. Uncom 2.4 не поддерживает данную архитектуру.\nЗапустите sudo remove_i386_packs для удаления пакетов."
            else:
                text_explain="Не удается обновить: неизвестная ошибка."

            print("Can't start, text_explain =", text_explain)
            self.label_A.set_label(text_explain)
            self.label_A.set_visible(True)
            self.spinner.stop()
            self.button_apply.set_sensitive(True)
            self.button_cancel.set_sensitive(True)
            self.show_message_info_no_action(text_explain)
            return

        check_nvidia = subprocess.call(["/sbin/check_nvidia"], shell=True, universal_newlines=True)
        print("check_nvidia return code =", check_nvidia)
        if (check_nvidia == 0 and not self.workDone):
            print("You have nvidia drivers installed")
            self.show_message_info("У Вас установлены проприетарные драйверы NVidia.\nПосле обновления потребуется установить новую версию в программе Драйверы. Продолжить обновление?", "command")
            return

        # Start the command execution in a separate thread

        if self.workDone:
            thread = threading.Thread(target=self.run_reboot)
            thread.start()
            exit(0)
        else:
            thread = threading.Thread(target=self.run_command)
            thread.start()



class MyApp(Gtk.Application):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.connect('activate', self.on_activate)

    def on_activate(self, app):
        self.win = MainWindow(application=app)
        self.win.present()

print("Running application as: " + os.getlogin())
logging.basicConfig(filename='/var/log/uncom-kernel68-update.log', encoding='utf-8', level=logging.DEBUG)

main_app = MyApp(application_id="tech.uncom.kernel68-update")
main_app.run()
