#!/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')
from gi.repository import Gtk, GLib

import gettext as _gt, locale as _loc
TEXTDOMAIN = "lite-dpi"
try:
    _loc.setlocale(_loc.LC_ALL, "")
except _loc.Error:
    pass
_gt.bindtextdomain(TEXTDOMAIN, "/usr/share/locale")
_gt.textdomain(TEXTDOMAIN)
_ = _gt.translation(TEXTDOMAIN, "/usr/share/locale", fallback=True).gettext

import subprocess
import shutil


class HiDPISettingsWindow(Gtk.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")

        # Header bar (plain Gtk so the window follows the Linux-Lite theme)
        header = Gtk.HeaderBar()
        self.set_titlebar(header)

        # Info button in header — symbolic so it recolors to the headerbar
        # foreground (white on the Linux-Lite dark headerbar) in both modes.
        info_button = Gtk.Button(icon_name="help-about-symbolic")
        info_button.set_tooltip_text(_("DPI Info"))
        info_button.connect("clicked", self.on_info_button_clicked)
        header.pack_end(info_button)

        content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20,
                              margin_top=20, margin_bottom=20,
                              margin_start=20, margin_end=20)
        self.set_child(content_box)

        # Current DPI status
        status_title = Gtk.Label(label=_("Current Setting"))
        status_title.set_xalign(0)
        status_title.add_css_class("heading")
        content_box.append(status_title)

        current_dpi = self.get_current_dpi()
        status_listbox = Gtk.ListBox()
        status_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        status_listbox.add_css_class("boxed-list")
        status_row = Gtk.ListBoxRow()
        status_row.set_activatable(False)
        sbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12,
                       margin_top=10, margin_bottom=10, margin_start=12, margin_end=12)
        status_icon = Gtk.Image.new_from_icon_name("lite-dpi")
        status_icon.set_pixel_size(28)
        sbox.append(status_icon)
        stext = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
        stext.set_valign(Gtk.Align.CENTER)
        stext.set_hexpand(True)
        self._status_title_lbl = Gtk.Label(label=_("{value} DPI").format(value=current_dpi))
        self._status_title_lbl.set_xalign(0)
        self._status_sub_lbl = Gtk.Label(label=self.dpi_to_percentage(current_dpi))
        self._status_sub_lbl.set_xalign(0)
        self._status_sub_lbl.add_css_class("caption")
        stext.append(self._status_title_lbl)
        stext.append(self._status_sub_lbl)
        sbox.append(stext)
        status_row.set_child(sbox)
        status_listbox.append(status_row)
        content_box.append(status_listbox)

        # Scaling options
        scale_title = Gtk.Label(label=_("Scaling Factor"))
        scale_title.set_xalign(0)
        scale_title.add_css_class("heading")
        content_box.append(scale_title)
        scale_desc = Gtk.Label(label=_("Select a scaling factor below"))
        scale_desc.set_xalign(0)
        scale_desc.add_css_class("caption")
        content_box.append(scale_desc)

        # 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)
        content_box.append(scale_listbox)

        # Actions
        reset_listbox = Gtk.ListBox()
        reset_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        reset_listbox.add_css_class("boxed-list")
        reset_listbox.connect("row-activated", lambda lb, r: self.on_reset_clicked(r))
        reset_row = Gtk.ListBoxRow()
        reset_row.set_activatable(True)
        rbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12,
                       margin_top=10, margin_bottom=10, margin_start=12, margin_end=12)
        reset_icon = Gtk.Image.new_from_icon_name("system-restart")
        reset_icon.set_pixel_size(28)
        rbox.append(reset_icon)
        rtext = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
        rtext.set_valign(Gtk.Align.CENTER)
        rtext.set_hexpand(True)
        rt = Gtk.Label(label=_("Reset to Defaults"))
        rt.set_xalign(0)
        rs = Gtk.Label(label=_("Set DPI back to 96 (100%)"))
        rs.set_xalign(0)
        rs.add_css_class("caption")
        rtext.append(rt)
        rtext.append(rs)
        rbox.append(rtext)
        reset_row.set_child(rbox)
        reset_listbox.append(reset_row)
        content_box.append(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_title_lbl.set_label(_("{value} DPI").format(value=dpi_value))
            self._status_sub_lbl.set_label(self.dpi_to_percentage(dpi_value))
            self.show_notification(
                _("HiDPI Settings"),
                _("DPI set to {value} ({pct})").format(value=dpi_value, pct=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 = Gtk.Window(transient_for=self, modal=True)
        dialog.set_title(_("HiDPI Info"))
        dialog.set_default_size(320, 380)

        header = Gtk.HeaderBar()
        header.set_show_title_buttons(False)
        close_button = Gtk.Button(label=_("Close"))
        close_button.connect("clicked", lambda b: dialog.close())
        header.pack_end(close_button)
        dialog.set_titlebar(header)

        content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12,
                              margin_top=20, margin_bottom=20,
                              margin_start=20, margin_end=20)
        dialog.set_child(content_box)

        info_title = Gtk.Label(label=_("DPI / Percentages"))
        info_title.set_xalign(0)
        info_title.add_css_class("heading")
        content_box.append(info_title)
        info_desc = Gtk.Label(label=_("Reference table for DPI values"))
        info_desc.set_xalign(0)
        info_desc.add_css_class("caption")
        content_box.append(info_desc)

        # 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 = Gtk.ListBoxRow()
            row.set_activatable(False)
            rbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12,
                           margin_top=8, margin_bottom=8, margin_start=12, margin_end=12)
            rbox.append(Gtk.Image.new_from_icon_name("preferences-desktop-display"))
            rtext = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
            rtext.set_valign(Gtk.Align.CENTER)
            rtext.set_hexpand(True)
            t = Gtk.Label(label=_("{value} DPI").format(value=dpi))
            t.set_xalign(0)
            s = Gtk.Label(label=percent)
            s.set_xalign(0)
            s.add_css_class("caption")
            rtext.append(t)
            rtext.append(s)
            rbox.append(rtext)
            row.set_child(rbox)
            listbox.append(row)

        content_box.append(listbox)

        dialog.present()
    
    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 = Gtk.AlertDialog()
        dialog.set_modal(True)
        dialog.set_message(title)
        dialog.set_detail(message)
        dialog.set_buttons([_("OK")])
        dialog.set_default_button(0)
        dialog.set_cancel_button(0)
        dialog.show(self)


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

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


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


if __name__ == "__main__":
    main()
