#!/usr/bin/python3
import os, re, sys
import jwmkit_utils

# JWM Kit - A set of Graphical Apps to simplify use of JWM (Joe's Window Manager) <https://codeberg.org/JWMKit/JWM_Kit>
# Copyright © 2020-2022  Calvin Kent McNabb <apps.jwmkit@gmail.com>
#
# This file is part of JWM Kit.
#
# JWM Kit is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# JWM Kit 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 JWM Kit.  If not, see <https://www.gnu.org/licenses/>.


def terminal_fix(data, term):
    # work around for poorly made .desktop files with Terminal=True, but still adds the terminal in the exec= line.
    terms = ['xfce4-terminal', 'i3-sensible-terminal', 'lxterminal', 'gnome-terminal', 'roxterm', 'defaultterminal',
             'mate-terminal', 'Eterm', 'terminology', 'sakura', 'terminator', 'gnome-terminal', 'qterminal',
             'deepin-terminal', 'xterm', 'rxvt', 'uxterm', 'urxvt', 'x-terminal-emulator', term]
    if ' -e ' in data:
        for terminal in terms:
            if terminal in data:
                return False
    return True


class Main:

    def __init__(self):
        term = jwmkit_utils.find_terminal()
        home = os.path.expanduser('~')
        sys_appdir = '/usr/share/applications/'
        home_appdir = os.path.join(home, '.local/share/applications/')
        applications = os.listdir(sys_appdir)
        sys_applications = re.findall("'([^']+?desktop)'", str(applications))
        if os.path.isdir(home_appdir):
            applications = os.listdir(home_appdir)
            home_applications = re.findall("'([^']+?desktop)'", str(applications))
        else:
            home_applications = []
        sys_applications = [i for i in sys_applications if i not in home_applications]
        sys_applications = ['{}{}'.format(sys_appdir, i) for i in sys_applications]
        home_applications = ['{}{}'.format(home_appdir, i) for i in home_applications]
        applications = sys_applications + home_applications
        lang = os.getenv('LANG')
        locale = re.split('[_ .]', lang, 2)
        lang = '[{}]'.format(locale[0])
        locale = '[{}_{}]'.format(locale[0], locale[1])
        cat_menus = [[], [], [], [], [], [], [], [], [], [], [], [], []]
        nodubs, nokitmenu, static = False, False, False
        nokit, onroot = '', ''
        category_indexes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

        # work around to sort non-standard categories down to a small number of standard categories
        # mv_xxxx are apps that should move to a specific category.
        mv_settings = ['urxvtcontrol', 'bootmanager', 'ipinfo', 'wcpufreq', 'pup-sysinfo', 'hardinfo', 'loginmanager',
                       '/usr/local/bin/pschedule', 'pup-kview', 'sysinfo', '/usr/bin/hardinfo', 'video-info',
                       'eventmanager', 'firewall_ng', 'firewallstatus', '/usr/sbin/firewallstatusrestart', 'gpptp',
                       'janky_bt', 'samba.sh', 'pup-advert-blocker', 'clipit']
        mv_utility = ['midnight commander']
        software_blocklist = ['wmstartups', 'wizardwizard', 'pdesktop']
        software_installers = ['dl_chrome_opera.sh', 'install_brave_gui.sh', 'getflash',
                               'install_vivaldi_gui.sh', 'get_libreoffice']
        pk_managers = ['gpkgdialog', 'xpkgdialog', 'defaultterminal -e pkgdialog', '/usr/local/petget/pkg_chooser.sh']
        utility = ['Utility', 'X-Utility', 'Electronics', 'Emulator', 'Engineering', 'X-Utility-package', 'Archiving',
                   'Compression', 'X-Utility-shell', 'TerminalEmulator', 'Shell', 'X-Filesystem-filemanager',
                   'FileManager', 'X-Filesystem-mount', 'X-FilesystemMount', 'X-Filesystem-find', 'X-FilesystemFind',
                   'X-Document-edit', 'TextEditor', 'X-Business-calc', 'Calculator', 'X-CalculateUtility']

        education = ['Education', 'X-Personal-education', 'Languages', 'Science', 'ArtificialIntelligence', 'Astronomy',
                     'Biology', 'Chemistry', 'ComputerScience', 'DataVisualization', 'Economy', 'Electricity',
                     'Geography', 'Geology', 'Geoscience', 'History', 'ImageProcessing', 'Literature', 'Math',
                     'NumericalAnalysis', 'MedicalSoftware', 'Physics', 'Robotics', 'Sports', 'ParallelComputing']

        development = ['Development', 'X-Utility-development', 'Development', 'Building', 'Debugger', 'IDE',
                       'Profiling', 'ProjectManagement', 'RevisionControl', 'Translation', 'GUIDesigner']

        game = ['Game', 'Games', 'X-Fun', 'Game', 'Amusement', 'BlocksGame', 'KidsGame', 'Simulation', 'SportsGame',
                'X-Fun-adventure', 'ActionGame', 'AdventureGame', 'RolePlaying', 'X-Fun-boardgame', 'BoardGame',
                'X-Fun-cardgame', 'CardGame', 'X-Fun-puzzle', 'StrategyGame', 'LogicGame', 'X-Fun-shooter',
                'X-Fun-arcade', 'ArcadeGame']

        graphics = ['Graphics', 'X-Graphic', '2DGraphics', '3DGraphics', 'X-Graphic-paint', 'RasterGraphics',
                    'X-Graphic-draw', 'VectorGraphics', 'X-Graphic-flow,Chart', 'FlowChart', 'Presentation',
                    'X-Graphic-viewer,Viewer', 'X-Graphic-viewer', 'X-Graphic-camera', 'Photography',
                    'X-Graphic-scanner', 'Scanning,OCR', 'X-Graphic-print', 'X-Graphic-utility', 'X-GraphicUtility']

        network = ['Network', 'Internet', 'X-Network', 'X-Network-firewall', 'X-Network-phone', 'Dialup',
                   'X-Network-connect', 'HamRadio', 'RemoteAccess', 'X-Network-transfer', 'X-Network-utility',
                   'X-Internet', 'X-Internet-browser', 'WebBrowser', 'X-Internet-chat', 'Chat', 'InstantMessaging',
                   'VideoConference', 'X-Internet-mailnews', 'Email', 'News', 'X-Internet-transfer', 'Telephony',
                   'IRCClient', 'FileTransfer', 'X-Internet-login', 'X-Internet-block', 'X-Internet-utility']

        audiovideo = ['AudioVideo', 'X-Multimedia', 'Recorder', 'X-Multimedia-mediaplayer', 'Player',
                      'X-Multimedia-audiovideo', 'AudioVideoEditing', 'X-Multimedia-sound', 'Music', 'Audio', 'Midi',
                      'Mixer', 'Sequencer', 'X-Multimedia-video', 'Video', 'TV', 'X-Multimedia-optical', 'DiskBurning']

        office = ['Office', 'X-Document', 'X-Document-layout', 'Publishing', 'WordProcessor', 'WebDevelopment',
                  'X-Document-catalog', 'Dictionary', 'X-Document-viewer', 'Viewer', 'X-Document-utility',
                  'X-DocumentUtility', 'X-Business', 'X-Calculate', 'X-Business-spreadsheet', 'Spreadsheet',
                  'X-Business-planner', 'ProjectManagement', 'X-Business-finance', 'Finance', 'X-Personal',
                  'X-Personal-database', 'ContactManagement', 'X-Personal-date', 'Calendar', 'X-Personal-organizer',
                  'PDA', 'X-Personal-timing', 'X-PersonalUtility']

        settings = ['Settings', 'X-Desktop', 'X-Desktop-appearance', 'DesktopSettings', 'Accessibility', 'Printing',
                    'X-Desktop-windows', 'X-Desktop-sleep', 'Screensaver', 'X-Desktop-applet', 'X-DesktopApplets',
                    'Clock', 'X-Setup', 'X-Setup-puppy', 'X-SetupEntry', 'X-DesktopCountry', 'HardwareSettings',
                    'X-Setup-wizard', 'X-SetupWizard', 'X-Setup-installation', 'X-SetupUtility', 'X-Desktop-settings',
                    'X-XFCE-SettingsDialog', 'X-antiX']

        system = ['System', 'X-System', 'Core', 'X-System-process', 'Monitor',
                  'X-SystemSchedule', 'X-System-memory', 'X-SystemMemory', 'X-System-print', 'X-System-storage',
                  'X-System-security', 'Security', 'X-Filesystem', 'FileSystem', 'FileTools', 'X-Filesystem-storage',
                  'X-FilesystemUtility', 'X-Personal-security', 'PackageManager']

        categories = ['Utility', 'Education', 'Development', 'Game', 'Graphics', 'Network',
                      'AudioVideo', 'Office', 'Other', 'Settings', 'Science', 'System', 'JWM Kit']
        preferred_names = ['Accessories', 'Education', 'Development', 'Games', 'Graphics', 'Internet',
                           'Multimedia', 'Office', 'Other', 'Settings', 'Science', 'System', 'JWM Kit']
        icons = ['applications-utilities', 'applications-education', 'applications-development', 'applications-games',
                 'applications-graphics', 'applications-internet', 'applications-multimedia', 'applications-office',
                 'applications-other', 'preferences-system', 'applications-science', 'applications-system',
                 'config']

        # handle input arguments
        arg_length = len(sys.argv)
        if arg_length > 1:
            for i in range(arg_length):
                arguments = sys.argv[i]
                argument = arguments.split(':')
                if argument[0] == '-t':
                    term = argument[1]
                elif argument[0] == '-r':
                    if argument[1] in preferred_names:
                        preferred_names[preferred_names.index(argument[1])] = argument[2]
                elif argument[0] == '-hc':
                    for ii in range(len(argument)-1):
                        if argument[ii+1] in categories:
                            category_indexes[categories.index(argument[ii+1])] = 'hidden'
                elif argument[0] == '-ha':
                    for ii in range(len(argument)-1):
                        # work around to use use \\:\\ to escape :
                        if argument[ii+1].endswith('\\'):
                            if argument[ii+2].startswith('\\'):
                                software_blocklist.append(argument[ii+1][:-1].lower() + ':'
                                                          + argument[ii+2][1:].lower())
                        elif argument[ii + 1].startswith('\\'):
                            if argument[ii - 1].endswith('\\'):
                                break
                        else:
                            software_blocklist.append(argument[ii+1].lower())
                elif argument[0] == '-m':
                    for ii in range(len(argument)-2):
                        if argument[1] in categories and argument[ii+2] in categories:
                            category_indexes[categories.index(argument[ii+2])] = \
                                category_indexes[categories.index(argument[1])]
                elif argument[0] == '-nodubs':
                    nodubs = True
                elif argument[0] == '-nokit':
                    nokit = True
                elif argument[0] == '-nokitmenu':
                    nokitmenu = True
                elif argument[0] == '-l':
                    lang = '[{}]'.format(argument[1])
                elif argument[0] == '-lt':
                    locale = '[{}]'.format(argument[1])
                    lang = '{}]'.format(locale.split('_')[0])
                elif argument[0] == '-static':
                    static = True
                    try:
                        onroot = argument[1]
                    except IndexError:
                        print('Argument Incomplete: onroot not defined\nexamples\nLeft Mouse Button:  --static:1\n'
                              'Right Mouse Button: --static:3\nRt & Lt Mouse Buttons: --static:13')
                        exit()

        search_keys = [['Name{}'.format(re.escape(locale)), 'Name{}'.format(re.escape(lang)), 'Name'], 'Exec', 'Icon',
                       ['Comment{}'.format(re.escape(locale)), 'Comment{}'.format(re.escape(lang)), 'Comment'],
                       'Terminal', 'Categories']

        # Get data application data from .desktop files
        for application in applications:
            with open(application) as f:
                appdata = f.read()
            appdata = re.findall('(?s)(\[Desktop Entry\].+?(?=\[Desktop|\Z))', appdata, re.MULTILINE)[0]
            hidden = re.findall('NoDisplay=true', appdata)
            if len(hidden) == 0:
                data = []
                for key in search_keys:
                    if type(key) is list:
                        for name in key:
                            try:
                                data.append(re.findall('\n{}=(.*)'.format(name), appdata)[0])
                                break
                            except IndexError:
                                if name in ['Name', 'Comment']:
                                    data.append('')
                    else:
                        try:
                            item_data = re.findall('\n{}=(.*)'.format(key), appdata)[0]
                            if key == 'Exec':
                                while item_data[-3:] in [' %U', ' %u', ' %F', ' %f', ' %i', ' %c', ' %k']:
                                    item_data = '{}'.format(item_data[:-3])
                            data.append(item_data)
                        except IndexError:
                            data.append('')
                if data[4] == 'true':
                    term_command = '{} -T "{}" -e "{}"'.format(term, data[0], data[1])
                    if data[1] in mv_settings:
                        mv_settings[mv_settings.index(data[1])] = term_command.lower()
                    if terminal_fix(data[1], term):
                        # Fix for improperly made desktop files
                        if data[1].startswith('TERM='):
                            data[1] = re.findall('TERM=[^ ]+ (.+)', data[1])[0]
                        data[1] = term_command
                # replace Puppy's ptheme with ptheme-gtk since it conflicts with JWM Kit.
                if data[0].lower() == "puppy theme manager":
                    if data[1].lower() == "ptheme":
                        data[1] = "ptheme_gtk"
                        data[0] = "Theme Switcher"
                # Compare category data to determine which list(s) in the [list of list] to append the item
                indexes = []
                if data[1].lower() in mv_settings:
                    data[5] = 'Settings'
                elif data[1].lower() in pk_managers:
                    data[5] = 'System'
                elif data[1].lower() in software_blocklist:
                    data[5] = 'hidden'
                elif data[1].lower() in software_installers:
                    data[5] = 'hidden'
                if data[0].lower() in mv_utility:
                    data[5] = 'Utility'

                for i in data[5].split(';'):
                    if i in utility:
                        indexes.append(category_indexes[0])
                    if i in education:
                        indexes.append(category_indexes[1])
                    if i in development:
                        indexes.append(category_indexes[2])
                    if i in game:
                        indexes.append(category_indexes[3])
                    if i in graphics:
                        indexes.append(category_indexes[4])
                    if i in network:
                        indexes.append(category_indexes[5])
                    if i in audiovideo:
                        indexes.append(category_indexes[6])
                    if i in office:
                        indexes.append(category_indexes[7])
                    if i in settings:
                        # Find JWM Kit files for special placement
                        if data[0].lower().startswith('jwm kit'):
                            # if 'hidden' argument used index='hidden' as a signal to not add JWM Kit apps to menus
                            if nokit:
                                indexes.append('hidden')
                            # if 'nokitmenu' argument used put JWM Kit Apps in Settings (index=9)
                            elif nokitmenu:
                                indexes.append(category_indexes[9])
                            # Default: JWM Kit Apps are place in their own JWM Kit Menu.
                            else:
                                data[0] = data[0][8:]
                                indexes.append(category_indexes[12])
                        else:
                            if 'PackageManager' not in data[5].split(';'):
                                indexes.append(category_indexes[9])
                    if i == 'Science':
                        indexes.append(category_indexes[10])

                    if i in system:
                        indexes.append(category_indexes[11])
                    if i == 'hidden':
                        indexes.append('hidden')

                if not indexes: indexes.append(category_indexes[8])
                indexes = list(set(indexes))
                for index in indexes:
                    # skip JWM Kit Apps if nokit flag is used
                    if 'hidden' in indexes:
                        break
                    else:
                        cat_menus[index].append(data)
                        # break loop if nodubs flag is used
                        if nodubs:
                            break

        # Sort list in ABC order
        for category in cat_menus:
            category.sort()

        # create xml
        output = '<JWM>\n'
        if static:
            output +='  <RootMenu height="24" onroot="{}" label="Menu">\n'.format(onroot)
        menu_test = False
        for menu in enumerate(cat_menus):
            i = category_indexes[menu[0]]
            if len(menu[1]) != 0:
                if menu[0] != 0:
                    output = '{}    </Menu>\n'.format(output)
                output = '{}    <Menu icon="{}" label="{}">\n'.format(output, icons[menu[0]], preferred_names[menu[0]])
                menu_test = True
                for item in menu[1]:
                    output += '        <Program icon="{}" label="{}">{}</Program>\n'.format(item[2], item[0], item[1])
        if menu_test:
            output = '{}    </Menu>\n'.format(output)
        if static:
            output = '{}  </RootMenu>\n</JWM>'.format(output)
        else:
            output = '{}</JWM>'.format(output)
        print(output)


Main()

# TODO ----- Static Argument -----
# TODO write to file or print
# TODO option: add height and label arguments
# TODO option: add items to root menu

# TODO ----- General -----
#  - options use comments to make popup
