#!/usr/bin/python3

import sys
import gi
import subprocess
import time
import os
import pwd
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, Gio
from PIL import Image
import random
import string
import gettext

text_domain = "uncom-icon"
gettext.bindtextdomain(text_domain, '/usr/share/uncom/uncom-setup/locale')
gettext.textdomain(text_domain)
_ = gettext.gettext


PATH_TO_ICONS = '~/.local/share/icons/hicolor/256x256/apps/'
PATH_TO_DESKTOP_FILES = '~/.local/share/applications/'

class MainWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.set_default_size(400, 250)
        self.set_title(_("Application Settings"))
        self.set_resizable(False)

        self.main_box = Gtk.Box(spacing=0, orientation=Gtk.Orientation.VERTICAL)
        self.set_child(self.main_box)
        self.main_box.set_margin_start(20)
        self.main_box.set_margin_end(20)
        self.main_box.set_margin_top(20)
        self.main_box.set_margin_bottom(20)

        self.box = Gtk.Box(spacing=5, orientation=Gtk.Orientation.VERTICAL)
        self.main_box.append(self.box)  
        
        self.drop = Gtk.DropTarget.new(Gdk.FileList, Gdk.DragAction.COPY)
        
        def on_drop(value, user_data, x, y):
            global path_to_original_icon
            filename = user_data.get_files()[0].get_path()
            path_to_original_icon = filename
            print("User dropped file: " + str(filename))
            if filename.endswith(".png") or filename.endswith(".jpeg") or filename.endswith(".jpg") or filename.endswith(".icns"):
                self.pix.set_from_file(str(filename))
            else:
                show_message_info(_("A file in .png, .jpeg, .jpg or .icns format is required."))

        def on_accept(drop, user_data):
            # Add filter for accepted file formats here
            return True

        self.drop.connect('drop', on_drop)
        self.drop.connect('accept', on_accept)
        self.drop.connect('enter', lambda drop_target,x,y: Gdk.DragAction.COPY)
        self.drop.connect('motion', lambda drop_target,x,y: Gdk.DragAction.COPY)
        self.drop.connect('leave', lambda user_data: None)

        def get_current_icon(filename):
            if filename:
                with open(passed_string, 'r') as file:
                    content = file.read()

                # Find the "Icon=" line and replace the entire line with the selected image file
                lines = content.splitlines()
                for i, line in enumerate(lines):
                    if line.strip().startswith('Icon='):
                        return lines[i][5:]

        def get_system_icon_or_image(name, fallback, size):
            if os.path.isfile(name):
                print ("This is an image, using file")
                return Gtk.Image.new_from_file(name)

            theme = Gtk.IconTheme.get_for_display(self.get_display())
            icon = Gtk.IconTheme.lookup_icon(theme, name, fallback, 256, 1, Gtk.TextDirection.NONE, Gtk.IconLookupFlags.FORCE_REGULAR)
            if icon:
                print("This is an icon with next path: " + icon.get_file().get_path())
                return Gtk.Image.new_from_file(icon.get_file().get_path())
            else:
                print("No available icon, show just empty space")
                return Gtk.Image.new()

        self.current_icon = get_current_icon(passed_string)
        print("Current icon path: " + self.current_icon)
        self.pix = get_system_icon_or_image(self.current_icon, "image-x-generic", Gtk.IconSize.LARGE)
        self.pix.set_size_request(128, 128)
        self.pix.add_controller(self.drop)
        self.box.append(self.pix)

        self.label = Gtk.Label(label=_("Drag the image here"))
        self.box.append(self.label)

        def show_icon_applied_message():
            global passed_string
            app_name = os.path.basename(passed_string)
            print("Application name: " + str(app_name))
            settings = Gio.Settings.new("org.gnome.shell")
            favorites = settings["favorite-apps"]
            print("List of favourite apps: " + str(settings["favorite-apps"]))
            
            if app_name in settings["favorite-apps"]:
                print("App is from favorites, icon will be refreshed right now")
                show_message_info(_("Icon successfully changed. The new icon will appear within a few seconds."))
            else:
                print("App is from app picker, icon will be refreshed during next login")
                show_message_info(_("Icon successfully changed. Please note that the settings will apply at the next system login."))

        def on_button_apply_clicked(self):
            global passed_string
            print("Applying icon...")
            if path_to_original_icon == "":
                show_message_info(_("An image must be selected for application."))
                return
            path_to_saved_icon = save_icon_to_system(path_to_original_icon)
            if path_to_saved_icon == "":
                show_message_info(_("Failed to save the icon."))
                return
            passed_string = change_icon(path_to_saved_icon)
            print("New desktop file path: " + passed_string)
            print("Icon applied successfully")
            show_icon_applied_message()

        self.button_apply = Gtk.Button(label=_("Apply"))
        self.box.append(self.button_apply)
        self.button_apply.set_margin_top(20)
        self.button_apply.set_margin_start(50)
        self.button_apply.set_margin_end(50)
        self.button_apply.connect("clicked", on_button_apply_clicked)

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


        def open_desktop_file(action, param):
            print("Opening .desktop file in text editor: " + passed_string)
            subprocess.Popen(["xdg-open", passed_string])

        def return_default_icon(action, param):
            global passed_string, path_to_original_icon
            print("Returning default icon")
            
            # Read content from .desktop file of application
            with open(passed_string, 'r') as file:
                content = file.read()

            was_already_modified = False

            # Find the Source Desktop File and Original Icon fields
            original_icon = ""
            current_icon = ""
            source_desktop_file = ""
            lines = content.splitlines()
            for i, line in enumerate(lines):
                if "[Uncom]" in line.strip():
                    was_already_modified = True
                    if "SourceDesktopFile" in line.strip():
                        source_desktop_file = line.strip().split("=")[1]
                    elif "OriginalIcon" in line.strip():
                        original_icon = line.strip().split("=")[1]
                    elif line.strip().startswith('Icon='):
                        current_icon = line.strip().split("=")[1]

            if not was_already_modified:
                print("This is an original desktop file, it can not be reveted")
                show_message_info(_("The icon for this application has not been changed before."))
            else:
                print("SourceDesktopFile: " + source_desktop_file)
                print("OriginalIcon: " + original_icon)

                if source_desktop_file == passed_string:
                    print("This desktop file is same as original, we need to revert the icon itself")
                    if original_icon == "":
                        print("Original icon is not defined in config, can not be reverted")
                        show_message_info(_("Sorry, but the previous icon is not defined, set a new icon."))
                    elif original_icon == current_icon:
                        print("Original icon is same as current, will not be reverted")
                        show_message_info(_("Sorry, but the previous icon matches the current one, nothing to revert."))
                    else:
                        print("Original icon is different from current, reverting it in same .desktop file")
                        for i, line in enumerate(lines):
                            if line.strip().startswith('Icon='):
                                lines[i] = f'Icon={original_icon}'
                        
                        # save changes into file
                        new_content = '\n'.join(lines)
                        with open(passed_string, 'w') as new_file:
                            new_file.write(new_content)
                        show_icon_applied_message()
                else:
                    print("This is a replacing desktop file, it can be just deleted")
                    os.remove(passed_string)
                    print("Current desktop file removed, original is in place")
                    passed_string = source_desktop_file
                    show_icon_applied_message()


        def show_about_dialog(action, param):
            print("Show about dialog")    
            self.about = Gtk.AboutDialog()
            self.about.set_program_name(_("Change Icon"))
            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(_("Web Page"))
            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("gnome-control-center")

            self.about.set_visible(True)

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

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

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

        # Create a new menu, containing that action
        menu = Gio.Menu.new()
        menu.append(_("Edit Application File"), "win.open_desktop_file")
        menu.append(_("Restore Original Icon"), "win.return_default_icon")
        menu.append(_("About"), "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)


        def show_message_info(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(self)
            self.dialog.show()

        def generate_random_string(length=15):
            letters = string.ascii_lowercase
            return ''.join(random.choice(letters) for i in range(length))
        
        def get_extension(filename):
            return os.path.splitext(filename)[1]
        
        def ensure_folder_exists(path):
            if not os.path.exists(path):
                os.makedirs(path)

        def save_icon_to_system(filename):
            image = Image.open(filename)
            image = image.resize((256, 256))
            home_dir = os.path.expanduser(PATH_TO_ICONS)
            file_name = "uncom-replace-" + generate_random_string() + get_extension(filename)
            new_file_path = os.path.join(home_dir, file_name)
            ensure_folder_exists(home_dir)
            image.save(new_file_path)
            print("Icon copy saved to: " + new_file_path)
            return new_file_path
        
        def change_icon(filename):
            if filename:
                # Read content from .desktop file of application
                with open(passed_string, 'r') as file:
                    content = file.read()

                was_already_modified = False

                # Find the "Icon=" line and replace the entire line with the selected image file
                original_icon = ""
                lines = content.splitlines()
                for i, line in enumerate(lines):
                    if line.strip().startswith('Icon='):
                        original_icon = line.strip().split("=")[1]
                        lines[i] = f'Icon={filename}'
                    if "[Uncom]" in line.strip():
                        was_already_modified = True

                new_content = '\n'.join(lines)

                # Save the modified content to the user's home directory
                file_name = os.path.basename(passed_string)
                print("New local .desktop file: ", file_name)
                home_dir = os.path.expanduser(PATH_TO_DESKTOP_FILES)
                new_file_path = os.path.join(home_dir, file_name)
                with open(new_file_path, 'w') as new_file:
                    if not was_already_modified:
                        new_file.write("# [Uncom] Modified automatically by Uncom Change App Icon application\n")
                        new_file.write("# [Uncom] SourceDesktopFile=" + passed_string + "\n")
                        new_file.write("# [Uncom] OriginalIcon=" + original_icon + "\n")
                    new_file.write(new_content)
                return new_file_path




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

    def on_activate(self, app):
        if is_app_supported:
            self.win = MainWindow(application=app)
            self.win.present()
        else:
            self.message = Gtk.MessageDialog(
                transient_for=Gtk.Window(application=app),
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text=_("Выбранную иконку пока поменять нельзя, мы работаем над новой версией."),
            )

            def close_dialog(self, app):
                self.destroy()
                main_app.quit()
            
            self.message.connect('response', close_dialog)
            self.message.set_modal(self)
            self.message.show()



print("Running application as: " + os.getlogin())
passed_string = "n/a"

# Given string firl .desktop file of the app
if len(sys.argv) > 1:
    passed_string = sys.argv[1]
    is_app_supported = True
else:
    #passed_string = "/usr/share/applications/org.gnome.clocks.desktop"
    #print("Debug mode, use Clocks app as test application")
    is_app_supported = False

print("Passed path to .desktop file: " + passed_string)

path_to_original_icon = "" # path to original icon file, selected by user

main_app = MyApp(application_id="tech.uncom.change-app-icon")
main_app.run()



