import gi
import os
import grp
import platform
import subprocess
import psutil  # To fetch memory information
import getpass  # To fetch the Linux username
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Pango

class LinuxInfoDialog(Gtk.Dialog):
    def __init__(self, parent=None):
        super().__init__(title="About Linux", transient_for=parent, flags=0)
        self.set_default_size(400, 300)
        self.set_resizable(False)  # Adjust size to fit content snugly
        self.set_border_width(5)  # Reduce padding

        # Get real kernel version
        kernel_version = platform.uname().release

        # Get Xfce version
        xfce_version = self.get_xfce_version()

        # Get Linux distribution information
        os_info = self.get_os_info()

        # Get CPU information (model and speed in GHz)
        cpu_info = self.get_cpu_info()

        # Get memory usage
        memory_info = self.get_memory_info()

        # Get Linux username
        username = getpass.getuser()

        # Get the user's group information
        try:
            user_gid = os.getgid()  # Get the user's GID
            user_group = grp.getgrgid(user_gid).gr_name  # Get the group name from the GID
        except Exception as e:
            print(f"Error retrieving user group: {e}")
            user_group = "Unknown"

        # Get Window Manager name
        window_manager = self.get_window_manager()

        # Dialog layout
        box = self.get_content_area()

        # Box for Distributor Logo and Distribution Name (with larger font)
        info_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)  # Increased spacing

        # Load the distributor logo based on the distribution
        distributor_logo = self.get_logo_for_distribution(os_info, fallback="debian-logo.png")
        if distributor_logo:
            info_box.pack_start(distributor_logo, expand=False, fill=False, padding=0)
        else:
            placeholder_label = Gtk.Label(label="(Logo image could not be loaded)")
            info_box.pack_start(placeholder_label, expand=False, fill=False, padding=0)

        # OS Name Text in Large Font
        distro_name_label = Gtk.Label(label=f"{os_info}")  # Showing OS name again in text format
        distro_name_font = Pango.FontDescription("Sans 20")  # Larger font for OS name
        distro_name_label.modify_font(distro_name_font)
        info_box.pack_start(distro_name_label, expand=False, fill=False, padding=0)

        # Add the box to the dialog
        box.pack_start(info_box, expand=False, fill=False, padding=10)

        # Add a horizontal separator here
        separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
        box.pack_start(separator, expand=False, fill=True, padding=10)

        # Get the machine's architecture
        architecture = platform.machine()

        # Get compositor type
        compositor_type = self.get_compositor_type()

        # Get session type (X11 or Wayland)
        session_type = self.get_session_type()

        # GTK, Icon, and Cursor theme detection
        gtk_theme = self.get_gtk_theme()
        icon_theme = self.get_icon_theme()
        cursor_theme = self.get_cursor_theme()

        # System information
        info_label = Gtk.Label(label=(
            f"Operating System: {os_info} {architecture}\n"
            f"Kernel: {kernel_version}\n"
            f"Desktop Environment: Xfce {xfce_version} ({session_type})\n"
            f"Window Manager: {window_manager}\n"
            f"GTK Theme: {gtk_theme}  Icon Theme: {icon_theme}  Cursor Theme: {cursor_theme}\n"
            f"CPU: {self.get_cpu_info()}\n"
            f"GPU: {self.get_gpu_info()}\n"
            f"Filesystem: {self.get_filesystem_info()}"  # Added Filesystem information
        ))
        info_label.set_justify(Gtk.Justification.LEFT)  # Align text to the left
        info_label.set_margin_top(10)
        box.pack_start(info_label, expand=True, fill=True, padding=10)

        # Add license information here (above the username)
        license_label = Gtk.Label(label="This product is licensed under the terms of the (GNU License Agreement) to:")
        license_label.set_justify(Gtk.Justification.LEFT)
        license_label.set_margin_top(10)
        box.pack_start(license_label, expand=True, fill=True, padding=5)

        # Create a new box to align the username label to the left
        username_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        username_label = Gtk.Label(label=f"		User: {username}")
        username_box.pack_start(username_label, expand=False, fill=False, padding=0)  # Aligns to left by default
        box.pack_start(username_box, expand=True, fill=True, padding=1)

        # Create a new box to align the group label to the left
        group_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        group_label = Gtk.Label(label=f"		Group: {user_group}")
        group_box.pack_start(group_label, expand=False, fill=False, padding=0)  # Aligns to left by default
        box.pack_start(group_box, expand=True, fill=True, padding=1)

        # Horizontal Separator Bar
        separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
        box.pack_start(separator, expand=False, fill=True, padding=10)  # Increased padding here for space

#        # CPU info
#        cpu_info = self.get_cpu_info()
#        cpu_label = Gtk.Label(label=f"CPU: {cpu_info}")
#        cpu_label.set_justify(Gtk.Justification.LEFT)
#        cpu_label.set_margin_top(10)
#        box.pack_start(cpu_label, expand=True, fill=True, padding=1)

        # Memory information below the separator
        memory_label = Gtk.Label(label=f"Physical Memory Available to Linux: {memory_info}")
        memory_label.set_justify(Gtk.Justification.LEFT)
        memory_label.set_margin_top(10)
        box.pack_start(memory_label, expand=True, fill=True, padding=1)

#        # Get and display filesystem information
#        filesystem_info = self.get_filesystem_info()
#        filesystem_label = Gtk.Label(label=f"Filesystem: {filesystem_info}")
#        filesystem_label.set_justify(Gtk.Justification.LEFT)
#        filesystem_label.set_margin_top(1)
#        box.pack_start(filesystem_label, expand=True, fill=True, padding=1)
#
#        self.show_all()

        # Buttons
        self.add_button("OK", Gtk.ResponseType.OK)

        # Set the window icon to the "about" icon from the theme (if available)
        icon = self.get_icon_from_theme("gtk-about", fallback="gtk-about.png")
        if icon:
            # Extract Pixbuf from Gtk.Image to set as window icon
            pixbuf = icon.get_pixbuf()
            self.set_icon(pixbuf)

        self.show_all()

    def get_xfce_version(self):
        try:
            # Run xfce4-session --version and capture output
            result = subprocess.run(
                ["xfce4-session", "--version"],
                capture_output=True,
                text=True,
                check=True
            )
            # Extract the version from the output
            output = result.stdout.strip()
            for line in output.split("\n"):
                if "xfce4-session" in line.lower():
                    # Example output: "xfce4-session 4.16.0 (Xfce 4.16)"
                    version = line.split()[-1]  # Get the last word
                    version = version.rstrip(')')  # Strip ')' from the right side
                    return version
        except Exception as e:
            print(f"Error detecting Xfce version: {e}")
            return "Unknown"
        return "Unknown"

    def get_os_info(self):
        try:
            with open("/etc/os-release", "r") as f:
                os_data = {}
                for line in f:
                    if "=" in line:
                        key, value = line.strip().split("=", 1)
                        os_data[key] = value.strip('"')
                # Use PRETTY_NAME if available, otherwise fallback to NAME + VERSION
                return os_data.get("PRETTY_NAME", f"{os_data.get('NAME', 'Unknown')} {os_data.get('VERSION', '')}")
        except Exception as e:
            print(f"Error retrieving OS info: {e}")
            return "Unknown"

    def get_memory_info(self):
        try:
            mem = psutil.virtual_memory()
            used_mem = mem.used // (1024 * 1024)  # Convert to MB
            total_mem = mem.total // (1024 * 1024)  # Convert to MB
            return f"{used_mem} MB / {total_mem} MB"
        except Exception as e:
            print(f"Error retrieving memory information: {e}")
            return "Unknown"

#    # Fetch CPU information (model and speed in GHz)
#    def get_cpu_info(self):
#        try:
#            # Fetch CPU model and frequency (in GHz)
#            cpu_info = subprocess.check_output("lscpu | grep 'Model name' | awk -F ': ' '{print $2}'", shell=True).decode("utf-8").strip()
#            cpu_speed = subprocess.check_output("lscpu | grep 'CPU MHz' | awk -F ': ' '{print $2}'", shell=True).decode("utf-8").strip()
#            cpu_speed_ghz = f"{float(cpu_speed) / 1000:.2f} GHz"  # Convert MHz to GHz
#            return f"{cpu_info} ({cpu_speed_ghz})"
#        except Exception:
#            return "Unknown"

    def get_cpu_info(self):
        try:
            # Get CPU model information
            cpu_info = subprocess.check_output("lscpu", shell=True).decode("utf-8")
            for line in cpu_info.splitlines():
                if "Model name" in line:
                    return line.split(":")[1].strip()
        except Exception as e:
            print(f"Error retrieving CPU info: {e}")
            return "Unknown CPU"

    def get_gpu_info(self):
        try:
            gpu_info = subprocess.check_output("lspci | grep VGA", shell=True).decode("utf-8").strip()
            if gpu_info:
                return gpu_info.split(":")[2].strip()  # Extract model name after "VGA compatible controller:"
            else:
                return "Unknown GPU"
        except Exception as e:
            print(f"Error retrieving GPU info: {e}")
            return "Unknown GPU"

    def get_motherboard_info(self):
        try:
            # Retrieve motherboard info using dmidecode
            motherboard_info = subprocess.check_output("sudo dmidecode -t 2", shell=True).decode("utf-8")
            for line in motherboard_info.splitlines():
                if "Product Name" in line:
                    return line.split(":")[1].strip()
            return "Unknown Motherboard"
        except Exception as e:
            print(f"Error retrieving motherboard info: {e}")
            return "Unknown Motherboard"
    
    def get_filesystem_info(self):
        try:
            disk_info = psutil.disk_usage('/')
            total_disk_space = self.size_str(disk_info.total)
            used_disk_space = self.size_str(disk_info.used)
            free_disk_space = self.size_str(disk_info.free)
            # Modified format to show only the required information
            return f"{used_disk_space} / {total_disk_space} (Free: {free_disk_space})"
        except Exception as e:
            print(f"Error retrieving filesystem info: {e}")
            return "Unknown Filesystem"

    def get_gtk_theme(self):
        try:
            theme = subprocess.check_output("gsettings get org.gnome.desktop.interface gtk-theme", shell=True).decode().strip().strip("'")
            return theme
        except Exception as e:
            print(f"Error retrieving GTK theme: {e}")
            return "Unknown GTK Theme"

    def get_icon_theme(self):
        try:
            theme = subprocess.check_output("gsettings get org.gnome.desktop.interface icon-theme", shell=True).decode().strip().strip("'")
            return theme
        except Exception as e:
            print(f"Error retrieving Icon theme: {e}")
            return "Unknown Icon Theme"

    def get_cursor_theme(self):
        try:
            theme = subprocess.check_output("gsettings get org.gnome.desktop.interface cursor-theme", shell=True).decode().strip().strip("'")
            return theme
        except Exception as e:
            print(f"Error retrieving Cursor theme: {e}")
            return "Unknown Cursor Theme"

    def size_str(self, bytes_size):
        # Convert bytes to human-readable format
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if bytes_size < 1024.0:
                return f"{bytes_size:.2f} {unit}"
            bytes_size /= 1024.0
        return f"{bytes_size:.2f} PB"

    def get_window_manager(self):
        try:
            # Look for the window manager process by searching for common window manager names
            window_managers = ["xfwm4", "openbox", "i3", "kwin", "metacity", "compiz", "awesome"]
            for wm in window_managers:
                if wm in (p.name() for p in psutil.process_iter()):
                    return wm
            return "Unknown"
        except Exception as e:
            print(f"Error detecting window manager: {e}")
            return "Unknown"

    def get_compositor_type(self):
        try:
            # Look for common compositors in Xfce
            compositor_types = ["xfwm4", "compton", "picom", "mutter", "kwin", "compiz"]
            for compositor in compositor_types:
                if compositor in (p.name() for p in psutil.process_iter()):
                    return compositor
            return "Unknown"  # If no compositor is found
        except Exception as e:
            print(f"Error detecting compositor: {e}")
            return "Unknown"

    def get_session_type(self):
        try:
            # Check the `XDG_SESSION_TYPE` environment variable
            import os
            session_type = os.environ.get("XDG_SESSION_TYPE", "Unknown").capitalize()
            return session_type
        except Exception as e:
            print(f"Error detecting session type: {e}")
            return "Unknown"

    def get_logo_for_distribution(self, os_info, fallback=None):
        # Check for the distribution in the os_info and choose the corresponding logo
        distro_name = self.get_distro_name_from_os_info(os_info)
        
        # Try to load the corresponding logo from the icon theme or fallback image
        if distro_name == "Debian":
            return self.get_icon_from_theme("debian-logo", fallback="debian-logo.png")
        elif distro_name == "Devaun":
            return self.get_icon_from_theme("devaun-logo", fallback="devaun-logo.png")
        elif distro_name == "Armbian":
            return self.get_icon_from_theme("armbian-logo", fallback="armbian-logo.png")
        elif distro_name == "Arch Linux":
            return self.get_icon_from_theme("arch-logo", fallback="arch-logo.png")
        elif distro_name == "Gentoo":
            return self.get_icon_from_theme("gentoo-logo", fallback="gentoo-logo.png")
        elif distro_name == "Slackware":
            return self.get_icon_from_theme("antix-logo", fallback="slackware-logo.png")
        elif distro_name == "Ubuntu":
            return self.get_icon_from_theme("ubuntu-logo", fallback="ubuntu-logo.png")
        elif distro_name == "Kubuntu":
            return self.get_icon_from_theme("kubuntu-logo", fallback="kubuntu-logo.png")
        elif distro_name == "Xubuntu":
            return self.get_icon_from_theme("xubuntu-logo", fallback="xubuntu-logo.png")
        elif distro_name == "Lubuntu":
            return self.get_icon_from_theme("lubuntu-logo", fallback="lubuntu-logo.png")
        elif distro_name == "Linux Mint":
            return self.get_icon_from_theme("linuxmint-logo", fallback="linuxmint-logo.png")
        elif distro_name == "Fedora":
            return self.get_icon_from_theme("fedora-logo", fallback="fedora-logo.png")
        elif distro_name == "MX Linux":
            return self.get_icon_from_theme("mxlinux-logo", fallback="mxlinux-logo.png")
        elif distro_name == "Antix":
            return self.get_icon_from_theme("antix-logo", fallback="antix-logo.png")
        elif distro_name == "Q4OS":
            return self.get_icon_from_theme("q4os-logo", fallback="q4os-logo.png")
        elif distro_name == "Zorin OS":
            return self.get_icon_from_theme("zorin-logo", fallback="zorin-logo.png")
        elif distro_name == "Linux Lite":
            return self.get_icon_from_theme("linuxlite-logo", fallback="linuxlite-logo.png")
        else:
            return self.get_icon_from_theme("distributor-logo", fallback=fallback)

    def get_distro_name_from_os_info(self, os_info):
        if "Debian" in os_info:
            return "Debian"
        elif "Devuan" in os_info:
            return "Devuan"
        elif "Armbian" in os_info:
            return "Armbian"
        elif "Arch Linux" in os_info:
            return "Arch Linux"
        elif "Gentoo" in os_info:
            return "Gentoo"
        elif "Slackware" in os_info:
            return "Slackware"
        elif "Ubuntu" in os_info:
            return "Ubuntu"
        elif "Kubuntu" in os_info:
            return "Kubuntu"
        elif "Xubuntu" in os_info:
            return "Xubuntu"
        elif "Lubuntu" in os_info:
            return "Lubuntu"
        elif "Linux Mint" in os_info:
            return "Linux Mint"
        elif "Fedora" in os_info:
            return "Fedora"
        elif "MX Linux" in os_info:
            return "MX Linux"
        elif "Antix" in os_info:
            return "Antix"
        elif "Q4OS" in os_info:
            return "Q4OS"
        elif "Zorin OS" in os_info:
            return "Zorin OS"
        elif "Linux Lite" in os_info:
            return "Linux Lite"
        else:
            return "Unknown"

    def get_user_group(self):
        """Get the user's group name from the system."""
        try:
            # Get the user's GID
            user_gid = os.getgid()  
            # Get the group name from the GID
            user_group = grp.getgrgid(user_gid).gr_name  
            return user_group
        except Exception as e:
            print(f"Error retrieving user group: {e}")
            return "Unknown"

    def get_icon_from_theme(self, icon_name, fallback=None):
        icon_theme = Gtk.IconTheme.get_default()
        try:
            icon = icon_theme.load_icon(icon_name, 48, 0)  # 48px size, no flags
            if icon:
                return Gtk.Image.new_from_pixbuf(icon)
        except Exception as e:
            print(f"Error loading icon from theme: {e}")
        
        # If no icon is found, try loading the fallback image
        if fallback:
            try:
                return Gtk.Image.new_from_file(fallback)
            except Exception as e:
                print(f"Error loading fallback image: {e}")
        return None

# Application run
def main():
    win = Gtk.Window(title="Linux Info")
    win.set_default_size(200, 100)
    win.connect("destroy", Gtk.main_quit)

    dialog = LinuxInfoDialog(parent=win)
    dialog.run()
    dialog.destroy()

if __name__ == "__main__":
    main()
