#!/usr/bin/env python3

import gettext
import gi
import os
import setproctitle
import subprocess
import sys
from subprocess import DEVNULL, PIPE

gi.require_version('Gdk', '3.0')
from gi.repository import Gdk

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

gi.require_version('Notify', '0.7')
from gi.repository import Notify

try:
    gi.require_version('AppIndicator3', '0.1')
    from gi.repository import AppIndicator3
    indicator_available = True
except:
    indicator_available = False

# i18n
gettext.install("mate-optimus", "/usr/share/locale")

def get_active_gpu():
    process = subprocess.Popen(['prime-select', 'query'], stdout=PIPE)
    out, err = process.communicate()
    return out.decode('utf-8')


def gpu_switch_notify(gpu):
    Notify.init(_('GPU Configuration Switched'))
    notify=Notify.Notification.new (_('GPU Configuration Switched'),_('Log out and log back in to complete the enablement of the default GPU to ' + gpu), 'dialog-information')
    notify.show()


class Indicator:
    def __init__(self):
        self.icon = AppIndicator3.Indicator.new('mate-optimus', '', AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
        self.icon.set_status(AppIndicator3.IndicatorStatus.ACTIVE)

        menu = Gtk.Menu()

        self.active_gpu = get_active_gpu()

        if ('nvidia' in self.active_gpu):
            self.icon.set_icon("optimus-tray-nvidia")
            self.icon.set_title(_("Active graphics card: NVIDIA"))
        elif ('intel' in self.active_gpu):
            self.icon.set_icon("optimus-tray-intel")
            self.icon.set_title(_("Active graphics card: Intel"))
        else:
            self.icon.set_icon("optimus-tray-intel")
            self.icon.set_title(_("Active graphics card: " + self.active_gpu))

        if 'intel' in self.active_gpu:
            item = Gtk.MenuItem(label=_("Switch to nvidia"))
            item.connect("activate", self.switch_to_nvidia)
            menu.append(item)

        if 'nvidia' in self.active_gpu:
            item = Gtk.MenuItem(label=_("Switch to Intel"))
            item.connect("activate", self.switch_to_intel)
            menu.append(item)

        item = Gtk.MenuItem(label=_("NVIDIA Settings"))
        item.connect("activate", self.run_nvidia_settings)
        menu.append(item)

        menu.append(Gtk.SeparatorMenuItem())

        item = Gtk.MenuItem(label=_("Quit"))
        item.connect("activate", self.terminate)
        menu.append(item)
        menu.show_all()
        self.icon.set_menu(menu)

    def run_nvidia_settings(self, arg=None):
        subprocess.Popen(["nvidia-settings", "-page", "PRIME Profiles"])

    def switch_to_nvidia(self, arg=None):
        subprocess.call(["pkexec", "prime-select", "nvidia"])
        gpu_switch_notify('nvidia')

    def switch_to_intel(self, arg=None):
        subprocess.call(["pkexec", "prime-select", "intel"])
        gpu_switch_notify('Intel')

    def terminate(self, window = None, data = None):
        Gtk.main_quit()


class Tray:
    def __init__(self):
        self.icon = Gtk.StatusIcon()
        self.icon.set_title("mate-optimus")
        self.icon.connect("popup-menu", self.on_popup_menu)
        self.icon.connect("activate", self.on_activate)

        self.active_gpu = get_active_gpu()

        if ('nvidia' in self.active_gpu):
            self.icon.set_from_icon_name("optimus-tray-nvidia")
            self.icon.set_tooltip_text(_("Active graphics card: NVIDIA"))
        elif ('intel' in self.active_gpu):
            self.icon.set_from_icon_name("optimus-tray-intel")
            self.icon.set_tooltip_text(_("Active graphics card: Intel"))
        else:
            self.icon.set_from_icon_name("optimus-tray-intel")
            self.icon.set_tooltip_text(_("Active graphics card: " + self.active_gpu))

    def on_activate(self, icon, data=None):
        self.run_nvidia_settings();

    def run_nvidia_settings(self, arg=None):
        subprocess.Popen(["nvidia-settings", "-page", "PRIME Profiles"])

    def switch_to_nvidia(self, arg=None):
        subprocess.call(["pkexec", "prime-select", "nvidia"])
        gpu_switch_notify('nvidia')

    def switch_to_intel(self, arg=None):
        subprocess.call(["pkexec", "prime-select", "intel"])
        gpu_switch_notify('Intel')

    def on_popup_menu(self, icon, button, time, data = None):
        menu = Gtk.Menu()

        def position_menu_cb(m, x, y=None, i=None):
            try:
                return Gtk.StatusIcon.position_menu(menu, x, y, icon)
            except (AttributeError, TypeError):
                return Gtk.StatusIcon.position_menu(menu, icon)

        if 'intel' in self.active_gpu:
            item = Gtk.MenuItem(label=_("Switch to nvidia"))
            item.connect("activate", self.switch_to_nvidia)
            menu.append(item)

        if 'nvidia' in self.active_gpu:
            item = Gtk.MenuItem(label=_("Switch to Intel"))
            item.connect("activate", self.switch_to_intel)
            menu.append(item)

        item = Gtk.MenuItem(label=_("NVIDIA Settings"))
        item.connect("activate", self.run_nvidia_settings)
        menu.append(item)

        menu.append(Gtk.SeparatorMenuItem())

        item = Gtk.MenuItem(label=_("Quit"))
        item.connect("activate", self.terminate)
        menu.append(item)

        menu.show_all()

        device = Gdk.Display.get_default().get_device_manager().get_client_pointer()
        menu.popup_for_device(device, None, None, position_menu_cb, icon, button, time)

    def terminate(self, window = None, data = None):
        Gtk.main_quit()

if __name__ == "__main__":
    setproctitle.setproctitle('mate-optimus')

    # If NVIDIA Prime is not installed or isn't supported then exit.
    if not (os.path.exists("/usr/bin/nvidia-settings") and os.path.exists("/usr/bin/prime-select")):
        print('No nvidia-settings and prime-select detected.')
        sys.exit(0)

    process = subprocess.Popen(['prime-supported'], stdout=PIPE, stderr=DEVNULL)
    out, err = process.communicate()
    prime_supported = out.decode('utf-8')
    if 'yes' not in prime_supported:
        print('Prime not supported.')
        sys.exit(0)

    if indicator_available:
        Indicator()
    else:
        Tray()

    Gtk.main()
