#!/usr/bin/env python3

# This file is part of I38.
                                                                                                                                                                          
# I38 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
                                                                                                                                                                          
# I38 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
                                                                                                                                                                          
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.

import os
import configparser
from pathlib import Path
from collections import defaultdict
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

def read_desktop_files(paths):
    desktopEntries = []
    for path in paths:
        for file in Path(path).rglob('*.desktop'):
            config = configparser.ConfigParser(interpolation=None)
            config.read(file)
            desktopEntries.append(config)
    return desktopEntries

userApplicationsPath = Path.home() / '.local/share/applications'
systemApplicationsPath = Path('/usr/share/applications')
userFlatpakApplicationsPath = Path.home() / '.local/share/flatpak/exports/share/applications'
systemFlatpakApplicationsPath = Path('/var/lib/flatpak/exports/share/applications')
desktopEntries = read_desktop_files([userApplicationsPath, systemApplicationsPath, userFlatpakApplicationsPath, systemFlatpakApplicationsPath])

# Combine some of the categories
categoryMap = {
    "2DGraphics": "Office",
    "Accessibility": "Settings",
    "Audio": "Audio and Video",
    "AudioVideo": "Audio and Video",
    "AudioVideoEditing": "Audio and Video",
    "ActionGame": "Games",
    "Building": "Development",
    "Calendar": "Office",
    "Chat": "Communication",
    "Database": "Office",
    "DesktopSettings": "Settings",
    "Emulator": "Games",
    "FlowChart": "Office",
    "Game": "Games",
    "HardwareSettings": "Settings",
    "IDE": "Development",
    "InstantMessaging": "Communication",
    "Math": "Education",
    "Midi": "Audio and Video",
    "Mixer": "Audio and Video",
    "Player": "Audio and Video",
    "Presentation": "Office",
    "Recorder": "Audio and Video",
    "Science": "Education",
    "Spreadsheet": "Office",
    "Telephony": "Communication",
    "Terminal": "Utilities",
    "TerminalEmulator": "Utilities",
    "TV": "Audio and Video",
    "Utility": "Utilities",
    "VectorGraphics": "Office",
    "Video": "Audio and Video",
    "WebDevelopment": "Development",
    "WordProcessor": "Office",
    "X-Alsa": "Audio and Video",
    "X-Fedora": "Utilities",
    "X-Jack": "Audio and Video",
    "X-LXDE-Settings": "Settings",
    "X-MATE-NetworkSettings": "Settings",
    "X-MATE-PersonalSettings": "Settings",
    "X-Red-Hat-Base": "Utilities",
    "X-SuSE-Core-Office": "Office",
    "X-XFCE": "XFCE",
    "X-XFCE-HardwareSettings": "Settings",
    "X-XFCE-PersonalSettings": "Settings",
    "X-XFCE-SettingsDialog": "Settings",
    "X-Xfce": "XFCE",
    "X-Xfce-Toplevel": "XFCE",
}

categories = defaultdict(set)
for entry in desktopEntries:
    try:
        entryCategories = entry.get('Desktop Entry', 'Categories').split(';')
        for category in entryCategories:
            combinedCategory = categoryMap.get(category, category)
            name = entry.get('Desktop Entry', 'Name')
            execCommand = entry.get('Desktop Entry', 'Exec')
            # Use a tuple of name and execCommand as a unique identifier
            if (name, execCommand) not in categories[combinedCategory]:
                categories[combinedCategory].add((name, execCommand))
    except configparser.NoOptionError:
        continue

class Xdg_Menu_Window(Gtk.Window):
    def __init__(self):
        super().__init__(title="I38 Menu")
        self.set_default_size(400, 300)
        self.set_border_width(10)

        self.store = Gtk.TreeStore(str, str)  # Columns: Category/Application Name, Exec Command

        sortedCategories = sorted(categories.items())  # Sort categories alphabetically

        for category, entries in sortedCategories:
            if category == "":
                continue
            categoryIter = self.store.append(parent=None, row=[category, None])
            sortedEntries = sorted(entries, key=lambda e: e[0])  # Sort entries by name
            for name, execCommand in sortedEntries:
                self.store.append(parent=categoryIter, row=[name, execCommand])

        self.treeview = Gtk.TreeView(model=self.store)
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Applications", renderer, text=0)
        self.treeview.append_column(column)
        self.treeview.set_headers_visible(False)

        self.treeview.connect("row-activated", self.on_row_activated)
        self.treeview.connect("key-press-event", self.on_key_press)

        self.add(self.treeview)
        self.connect("key-press-event", self.on_key_press)
        self.treeview.connect("focus-out-event", self.on_focus_out)

        self.treeview.grab_focus()
        self.show_all()

    def on_row_activated(self, treeview, path, column):
        model = treeview.get_model()
        iter = model.get_iter(path)
        execCommand = model.get_value(iter, 1)
        if execCommand:
            os.system(execCommand)

    def on_focus_out(self, widget, event):
        Gtk.main_quit()

    def on_key_press(self, widget, event):
        keyval = event.keyval
        state = event.state
        if keyval == Gdk.KEY_Escape:
            Gtk.main_quit()
        elif keyval == Gdk.KEY_Left:
            path = self.treeview.get_cursor()[0]
            if path:
                iter = self.store.get_iter(path)
                if self.treeview.row_expanded(path):
                    self.treeview.collapse_row(path)
                else:
                    parent_iter = self.store.iter_parent(iter)
                    if parent_iter:
                        parent_path = self.store.get_path(parent_iter)
                        self.treeview.collapse_row(parent_path)
                    else:
                        self.treeview.collapse_row(path)
        elif keyval == Gdk.KEY_Right:
            path = self.treeview.get_cursor()[0]
            if path:
                if not self.treeview.row_expanded(path):
                    self.treeview.expand_row(path, open_all=False)

win = Xdg_Menu_Window()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()