#!/usr/bin/env python3
#
# switch.py
# Brings up a GUI for switching between
# Nvidia (discrete) and Intel (integrated) GPUs
#
# Copyright (c) 2026 Erich Eickmeyer
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import os
import sys
import uuid
import subprocess
import tempfile
from pathlib import Path

VALID_OPTIONS = ("nvidia", "intel", "on-demand")
SWITCH_PY = "/usr/share/plasma/plasmoids/org.kde.plasma.plasma-optimus/contents/ui/switch.py"


def run_prime_select(option, token):
    """Elevated mode: validate token, run prime-select. Called via pkexec."""
    if option not in VALID_OPTIONS:
        sys.exit(1)

    token_path = Path(tempfile.gettempdir()) / f"plasma-optimus-{token}"
    if not token_path.is_file():
        print("This cannot be executed outside of Plasma Optimus", file=sys.stderr)
        sys.exit(1)

    token_path.unlink()
    os.execvp("prime-select", ["prime-select", option])


GPU_MAP = {
    "nvidia": ("Nvidia GPU (Performance Mode)", "nvidia"),
    "intel": ("Intel GPU (Power Save Mode)", "intel"),
    "on-demand": ("Hybrid GPU (On-Demand Mode)<br><br>Not Recommended if Using<br>External Monitors", "hybrid"),
}


def switch_graphics(option, icon_name):
    """Run pkexec switch.py --prime-select with a progress dialog, then reboot."""
    token = str(uuid.uuid4())
    token_path = Path(tempfile.gettempdir()) / f"plasma-optimus-{token}"
    token_path.touch()

    icon_path = f"/usr/share/pixmaps/plasma-optimus-{icon_name}.png"

    # Import Qt only in GUI mode
    from PyQt6.QtWidgets import QApplication, QProgressDialog
    from PyQt6.QtGui import QIcon
    from PyQt6.QtCore import Qt, QProcess, QEventLoop

    progress = QProgressDialog(
        "Please wait while your graphics system is configured.\n"
        "This may take a while (updating initramfs)...",
        None,  # no cancel button
        0, 0,  # indeterminate
    )
    progress.setWindowTitle("Plasma Optimus")
    progress.setWindowIcon(QIcon(icon_path))
    progress.setCancelButton(None)
    progress.setWindowModality(Qt.WindowModality.ApplicationModal)
    progress.setMinimumDuration(0)
    progress.show()
    QApplication.processEvents()

    # Use QProcess so the event loop keeps running and the throbber animates
    loop = QEventLoop()
    proc = QProcess()
    proc.finished.connect(loop.quit)
    proc.start("pkexec", [SWITCH_PY, "--prime-select", option, token])

    if not proc.waitForStarted(30000):
        progress.close()
        sys.exit(1)

    loop.exec()
    progress.close()

    if proc.exitCode() != 0:
        sys.exit(1)

    # Show Plasma reboot dialog via D-Bus
    subprocess.run([
        "qdbus6", "org.kde.LogoutPrompt", "/LogoutPrompt",
        "promptReboot",
    ])


def logout_dialog(option, gpu_str, icon_name):
    """Show confirmation dialog, then switch if accepted."""
    icon_path = f"/usr/share/pixmaps/plasma-optimus-{icon_name}.png"

    from PyQt6.QtWidgets import QMessageBox
    from PyQt6.QtGui import QIcon
    from PyQt6.QtCore import Qt

    text = f"""<html>
<style>
    table{{width:100%;margin:0}}
    td{{padding:4px 16px 4px 0;}}
</style>
<table align="center">
    <tr><td width="200px">
        <p align="center">Switching To</p>
        <h3 style="color:#1d99f3;text-align:center">{gpu_str}</h3>
        <p>You must reboot to apply the new GPU settings.
        <br>Make sure to save all work before continuing.</p>
        <p>Do you wish to reboot now?</p>
    </td><td width="72px" align="center">
        <img src='{icon_path}'></img>
    </td></tr>
</table>
</html>"""

    msg = QMessageBox()
    msg.setWindowTitle("Plasma Optimus")
    msg.setWindowIcon(QIcon(icon_path))
    msg.setTextFormat(Qt.TextFormat.RichText)
    msg.setText(text)
    msg.setStandardButtons(
        QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
    )
    msg.setDefaultButton(QMessageBox.StandardButton.No)

    if msg.exec() == QMessageBox.StandardButton.Yes:
        switch_graphics(option, icon_name)


def main():
    if len(sys.argv) < 2:
        sys.exit(1)

    # Elevated mode: pkexec switch.py --prime-select <option> <token>
    if sys.argv[1] == "--prime-select":
        if len(sys.argv) != 4:
            sys.exit(1)
        run_prime_select(sys.argv[2], sys.argv[3])
        return

    # GUI mode: switch.py <option>
    option = sys.argv[1]
    if option not in GPU_MAP:
        sys.exit(1)

    gpu_str, icon_name = GPU_MAP[option]

    from PyQt6.QtWidgets import QApplication
    app = QApplication(sys.argv)
    logout_dialog(option, gpu_str, icon_name)
    sys.exit(0)


if __name__ == "__main__":
    main()
