#!/usr/bin/env python3
#--------------------------------------------------------------------------------------------------------
# Name: Linux Lite - Lite DPI
# Architecture: amd64
# Author: Jerry Bezencon
# Website: https://www.linuxliteos.com
# Language: Python/GTK4
# Licence: GPLv2
#--------------------------------------------------------------------------------------------------------

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gio, GLib, Gdk
import subprocess
import shutil


class HiDPISettingsWindow(Adw.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.set_title("HiDPI Settings")
        self.set_default_size(420, -1)
        self.set_resizable(False)
        
        # Try to set window icon
        icon_path = "/usr/share/icons/Papirus/24x24/apps/lite-dpi.png"
        if shutil.os.path.exists(icon_path):
            self.set_icon_name("lite-dpi")
        
        # Main layout with header bar
        main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.set_content(main_box)
        
        # Header bar
        header = Adw.HeaderBar()
        header.set_show_end_title_buttons(True)
        
        # Info button in header
        info_button = Gtk.Button(icon_name="help-about")
        info_button.set_tooltip_text("DPI Info")
        info_button.connect("clicked", self.on_info_button_clicked)
        header.pack_end(info_button)
        
        main_box.append(header)
        
        # Content area with clamp for proper width
        clamp = Adw.Clamp()
        clamp.set_maximum_size(400)
        clamp.set_margin_top(20)
        clamp.set_margin_bottom(20)
        clamp.set_margin_start(20)
        clamp.set_margin_end(20)
        main_box.append(clamp)
        
        content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20)
        clamp.set_child(content_box)
        
        # Current DPI status group
        status_group = Adw.PreferencesGroup()
        status_group.set_title("Current Setting")
        content_box.append(status_group)
        
        current_dpi = self.get_current_dpi()
        self.status_row = Adw.ActionRow()
        self.status_row.set_title(f"{current_dpi} DPI")
        self.status_row.set_subtitle(self.dpi_to_percentage(current_dpi))
        self.status_row.add_prefix(Gtk.Image.new_from_icon_name("preferences-desktop-display"))
        status_group.add(self.status_row)
        
        # Scaling options group
        scale_group = Adw.PreferencesGroup()
        scale_group.set_title("Scaling Factor")
        scale_group.set_description("Select a scaling factor below")
        content_box.append(scale_group)
        
        # DPI options: (label, dpi_value)
        dpi_options = [
            ("75%", 72),
            ("100%", 96),
            ("125%", 120),
            ("150%", 144),
            ("175%", 168),
            ("200%", 192),
        ]
        
        # Create DPI buttons in a flow box
        flow_box = Gtk.FlowBox()
        flow_box.set_selection_mode(Gtk.SelectionMode.NONE)
        flow_box.set_homogeneous(True)
        flow_box.set_max_children_per_line(3)
        flow_box.set_min_children_per_line(3)
        flow_box.set_row_spacing(10)
        flow_box.set_column_spacing(10)
        
        for label, dpi in dpi_options:
            button = Gtk.Button(label=label)
            button.add_css_class("pill")
            button.set_size_request(100, 45)
            button.connect("clicked", self.on_dpi_button_clicked, dpi)
            flow_box.append(button)
        
        # Wrap flowbox in a ListBox row for consistent styling
        flow_row = Gtk.ListBoxRow()
        flow_row.set_selectable(False)
        flow_row.set_activatable(False)
        flow_row_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        flow_row_box.set_margin_top(10)
        flow_row_box.set_margin_bottom(10)
        flow_row_box.set_margin_start(10)
        flow_row_box.set_margin_end(10)
        flow_row_box.append(flow_box)
        flow_row.set_child(flow_row_box)
        
        scale_listbox = Gtk.ListBox()
        scale_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        scale_listbox.add_css_class("boxed-list")
        scale_listbox.append(flow_row)
        scale_group.add(scale_listbox)
        
        # Actions group
        action_group = Adw.PreferencesGroup()
        content_box.append(action_group)
        
        # Reset row
        reset_row = Adw.ActionRow()
        reset_row.set_title("Reset to Defaults")
        reset_row.set_subtitle("Set DPI back to 96 (100%)")
        reset_row.add_prefix(Gtk.Image.new_from_icon_name("edit-undo"))
        reset_row.set_activatable(True)
        reset_row.connect("activated", self.on_reset_clicked)
        
        reset_listbox = Gtk.ListBox()
        reset_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        reset_listbox.add_css_class("boxed-list")
        reset_listbox.append(reset_row)
        action_group.add(reset_listbox)
    
    def dpi_to_percentage(self, dpi):
        """Convert DPI value to percentage string"""
        dpi_map = {
            "72": "75%",
            "96": "100%",
            "120": "125%",
            "144": "150%",
            "168": "175%",
            "192": "200%",
        }
        return dpi_map.get(str(dpi), "Custom")
    
    def get_current_dpi(self):
        """Get current DPI setting from xrdb or xfconf"""
        try:
            # Try xrdb first
            result = subprocess.run(
                ["xrdb", "-query"],
                capture_output=True,
                text=True,
                timeout=5
            )
            for line in result.stdout.splitlines():
                if "dpi" in line.lower():
                    parts = line.split()
                    if len(parts) >= 2:
                        return parts[-1]
        except (subprocess.TimeoutExpired, FileNotFoundError):
            pass
        
        try:
            # Try xfconf-query
            result = subprocess.run(
                ["xfconf-query", "-c", "xsettings", "-p", "/Xft/DPI"],
                capture_output=True,
                text=True,
                timeout=5
            )
            if result.returncode == 0:
                return result.stdout.strip()
        except (subprocess.TimeoutExpired, FileNotFoundError):
            pass
        
        return "Unknown"
    
    def set_dpi(self, dpi_value):
        """Set DPI using xfconf-query"""
        try:
            subprocess.run(
                ["xfconf-query", "-c", "xsettings", "-p", "/Xft/DPI", "-s", str(dpi_value)],
                check=True,
                timeout=10
            )
            return True
        except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError) as e:
            print(f"Error setting DPI: {e}")
            return False
    
    def on_dpi_button_clicked(self, button, dpi_value):
        """Handle DPI button click"""
        if self.set_dpi(dpi_value):
            self.status_row.set_title(f"{dpi_value} DPI")
            self.status_row.set_subtitle(self.dpi_to_percentage(dpi_value))
            self.show_notification(
                "HiDPI Settings",
                f"DPI set to {dpi_value} ({self.dpi_to_percentage(dpi_value)})"
            )
            
            # Force window redraw after DPI change
            GLib.idle_add(self._force_redraw)
        else:
            self.show_message_dialog(
                "Error",
                "Failed to set DPI. Make sure xfconf-query is available.",
                is_error=True
            )
    
    def _force_redraw(self):
        """Force the window to redraw after DPI change"""
        # Hide and show to force complete redraw
        self.set_visible(False)
        GLib.timeout_add(50, self._show_window)
        return False
    
    def _show_window(self):
        """Show the window after brief hide"""
        self.set_visible(True)
        self.queue_draw()
        return False
    
    def on_reset_clicked(self, row):
        """Handle reset button click"""
        self.on_dpi_button_clicked(None, 96)
    
    def on_info_button_clicked(self, button):
        """Show DPI/Percentages info dialog"""
        dialog = Adw.Dialog()
        dialog.set_title("HiDPI Info")
        dialog.set_content_width(320)
        dialog.set_content_height(350)
        
        # Toolbar view for header
        toolbar_view = Adw.ToolbarView()
        dialog.set_child(toolbar_view)
        
        # Header bar
        header = Adw.HeaderBar()
        header.set_show_end_title_buttons(False)
        header.set_show_start_title_buttons(False)
        
        close_button = Gtk.Button(label="Close")
        close_button.connect("clicked", lambda b: dialog.close())
        header.pack_end(close_button)
        
        toolbar_view.add_top_bar(header)
        
        # Content
        clamp = Adw.Clamp()
        clamp.set_maximum_size(280)
        clamp.set_margin_top(20)
        clamp.set_margin_bottom(20)
        clamp.set_margin_start(20)
        clamp.set_margin_end(20)
        toolbar_view.set_content(clamp)
        
        content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15)
        clamp.set_child(content_box)
        
        # Info group
        info_group = Adw.PreferencesGroup()
        info_group.set_title("DPI / Percentages")
        info_group.set_description("Reference table for DPI values")
        content_box.append(info_group)
        
        # DPI/Percentage data
        dpi_data = [
            ("72", "75%"),
            ("96", "100%"),
            ("120", "125%"),
            ("144", "150%"),
            ("168", "175%"),
            ("192", "200%"),
        ]
        
        listbox = Gtk.ListBox()
        listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        listbox.add_css_class("boxed-list")
        
        for dpi, percent in dpi_data:
            row = Adw.ActionRow()
            row.set_title(f"{dpi} DPI")
            row.set_subtitle(percent)
            row.add_prefix(Gtk.Image.new_from_icon_name("preferences-desktop-display"))
            listbox.append(row)
        
        info_group.add(listbox)
        
        dialog.present(self)
    
    def show_notification(self, title, message):
        """Show an XFCE notification using notify-send"""
        try:
            subprocess.Popen([
                "notify-send",
                "-i", "preferences-desktop-display",
                title,
                message
            ])
        except FileNotFoundError:
            print(f"notify-send not found: {title} - {message}")
    
    def show_message_dialog(self, title, message, is_error=False):
        """Show a message dialog"""
        dialog = Adw.AlertDialog()
        dialog.set_heading(title)
        dialog.set_body(message)
        dialog.add_response("ok", "OK")
        dialog.set_default_response("ok")
        dialog.present(self)


class HiDPIApp(Adw.Application):
    def __init__(self, **kwargs):
        super().__init__(application_id="com.linuxlite.hidpi", **kwargs)

    def do_activate(self):
        self._apply_css()
        win = HiDPISettingsWindow(application=self)
        win.present()

    def _apply_css(self):
        css = b"""
        .dark-text { color: #1e1e1e; }
        .dim-label { opacity: 0.75; }
        """
        provider = Gtk.CssProvider()
        provider.load_from_data(css)
        Gtk.StyleContext.add_provider_for_display(
            Gdk.Display.get_default(), provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )


def main():
    app = HiDPIApp()
    app.run(None)


if __name__ == "__main__":
    main()
