# turbo_gui.py
import time
import sys
import datetime as _dt
import math
from typing import Optional

from PySide6.QtCore import QSize, QTimer, Qt, QStandardPaths, Signal
from PySide6.QtGui import (
    QFontDatabase,
    QImage,
    QKeySequence,
    QPixmap,
    QShortcut,
)
from PySide6.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QFileDialog,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QPushButton,
    QStatusBar,
    QStyle,
    QVBoxLayout,
    QWidget,
    QLineEdit,
)
import numpy as np

from cupystorm.turbo_colors import (
    COLOR_MAPS,
    DEFAULT_CMAP_NAME,
    QT_COLOR_TABLES,
    QT_GRAY_TABLE,
    DISPLAY_NORM_K_STD,
)
from cupystorm.turbo_logic import TurboLogic
from cupystorm.turbo_wrapper import DnsSimulator

FUSION = "Fusion"


def _setup_shortcuts(self) -> None:
    def sc(key: str, fn):
        s = QShortcut(QKeySequence(key), self)
        s.setContext(Qt.ShortcutContext.ApplicationShortcut)
        s.activated.connect(fn)  # type: ignore[attr-defined]
        return s

    self._sc_v = sc(
        "V",
        lambda: self.variable_combo.setCurrentIndex(
            (self.variable_combo.currentIndex() + 1) % self.variable_combo.count()
        ),
    )
    self._sc_c = sc(
        "C",
        lambda: self.cmap_combo.setCurrentIndex(
            (self.cmap_combo.currentIndex() + 1) % self.cmap_combo.count()
        ),
    )
    self._sc_n = sc(
        "N",
        lambda: self.n_combo.setCurrentIndex(
            (self.n_combo.currentIndex() + 1) % self.n_combo.count()
        ),
    )
    self._sc_r = sc(
        "R",
        lambda: self.re_combo.setCurrentIndex(
            (self.re_combo.currentIndex() + 1) % self.re_combo.count()
        ),
    )
    self._sc_k = sc(
        "K",
        lambda: self.k0_combo.setCurrentIndex(
            (self.k0_combo.currentIndex() + 1) % self.k0_combo.count()
        ),
    )
    self._sc_l = sc(
        "L",
        lambda: self.cfl_combo.setCurrentIndex(
            (self.cfl_combo.currentIndex() + 1) % self.cfl_combo.count()
        ),
    )
    self._sc_s = sc(
        "S",
        lambda: self.steps_combo.setCurrentIndex(
            (self.steps_combo.currentIndex() + 1) % self.steps_combo.count()
        ),
    )
    self._sc_u = sc(
        "U",
        lambda: self.update_combo.setCurrentIndex(
            (self.update_combo.currentIndex() + 1) % self.update_combo.count()
        ),
    )


class ClickableLabel(QLabel):
    clicked = Signal(int, int)
    pressed = Signal(int, int)
    moved = Signal(int, int)
    released = Signal(int, int)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            p = event.position()
            x = int(p.x())
            y = int(p.y())
            self.pressed.emit(x, y)
            self.clicked.emit(x, y)
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.MouseButton.LeftButton:
            p = event.position()
            self.moved.emit(int(p.x()), int(p.y()))
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            p = event.position()
            self.released.emit(int(p.x()), int(p.y()))
        super().mouseReleaseEvent(event)


class MainWindow(QMainWindow, TurboLogic):
    _sc_v: Optional[QShortcut]
    _sc_c: Optional[QShortcut]
    _sc_n: Optional[QShortcut]
    _sc_r: Optional[QShortcut]
    _sc_k: Optional[QShortcut]
    _sc_l: Optional[QShortcut]
    _sc_s: Optional[QShortcut]
    _sc_u: Optional[QShortcut]

    def __init__(self, sim: DnsSimulator) -> None:
        super().__init__()

        self.sim = sim
        self.current_cmap_name = DEFAULT_CMAP_NAME

        self.sig: float = 20.0
        self.mu: float = 128.0

        # --- grain metrics (omega) ---
        self.kmax: Optional[float] = None
        self.high_k_fraction: Optional[float] = None
        self.palinstrophy_over_enstrophy_kmax2: Optional[float] = None

        # --- central image label ---
        self.image_label = ClickableLabel()
        self.image_label.setSizePolicy(
            self.image_label.sizePolicy().horizontalPolicy(),
            self.image_label.sizePolicy().verticalPolicy(),
        )
        self.image_label.setMinimumSize(1, 1)
        self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # drag-to-force
        self._force_dragging = False
        self._force_last_xy: Optional[tuple[int, int]] = None

        self.image_label.pressed.connect(self.on_image_pressed)
        self.image_label.moved.connect(self.on_image_moved)
        self.image_label.released.connect(self.on_image_released)

        # --- small icon buttons ---
        style = QApplication.style()

        self.start_button = QPushButton()
        self.start_button.setIcon(style.standardIcon(QStyle.StandardPixmap.SP_MediaPlay))
        self.start_button.setToolTip("Start simulation")
        self.start_button.setFixedSize(28, 28)
        self.start_button.setIconSize(QSize(14, 14))

        self.stop_button = QPushButton()
        self.stop_button.setIcon(style.standardIcon(QStyle.StandardPixmap.SP_MediaStop))
        self.stop_button.setToolTip("Stop simulation")
        self.stop_button.setFixedSize(28, 28)
        self.stop_button.setIconSize(QSize(14, 14))

        self.reset_button = QPushButton()
        self.reset_button.setIcon(style.standardIcon(QStyle.StandardPixmap.SP_BrowserReload))
        self.reset_button.setToolTip("Reset simulation")
        self.reset_button.setFixedSize(28, 28)
        self.reset_button.setIconSize(QSize(14, 14))

        self.save_button = QPushButton()
        self.save_button.setIcon(style.standardIcon(QStyle.StandardPixmap.SP_DialogSaveButton))
        self.save_button.setToolTip("Save current frame")
        self.save_button.setFixedSize(28, 28)
        self.save_button.setIconSize(QSize(14, 14))

        self.folder_button = QPushButton()
        self.folder_button.setIcon(style.standardIcon(QStyle.StandardPixmap.SP_DirIcon))
        self.folder_button.setToolTip("Save files")
        self.folder_button.setFixedSize(28, 28)
        self.folder_button.setIconSize(QSize(14, 14))

        # --- init/force mode buttons (row above image) ---
        self.init_pao_button = QPushButton("Navier-Stokes")
        self.init_pao_button.setToolTip("Initilize with PAO spectrum")

        self.init_highh_button = QPushButton("High-k + Ekman/Rayleigh")
        self.init_highh_button.setToolTip("High-k spectral forcing + Ekman/Rayleigh large-scale drag")

        self.init_circle_button = QPushButton("Circle")
        self.init_circle_button.setToolTip("Initialize with circle stirring")

        self.init_rain_button = QPushButton("Rain")
        self.init_rain_button.setToolTip("Initialize with random body force")

        self.init_mouse_button = QPushButton("Mouse")
        self.init_mouse_button.setToolTip("Initialize with mouse force")

        self._status_update_counter = 0
        self._update_intervall = 20

        # Variable selector
        self.variable_combo = QComboBox()
        self.variable_combo.setToolTip("V: Variable")
        self.variable_combo.addItems(["U", "V", "K", "Ω", "φ"])

        # Grid-size selector (N)
        self.n_combo = QComboBox()
        self.n_combo.setToolTip("N: Grid Size (N)")
        self.n_combo.addItems(
            ["128", "192", "256", "384", "512", "768", "1024", "2048", "3072", "4096", "6144", "7776",
             "8192", "9216", "16384", "18432", "20480", "24576", "32768"]
        )
        self.n_combo.setCurrentText(str(self.sim.N))

        # Reynolds selector (Re)
        self.re_combo = QComboBox()
        self.re_combo.setToolTip("R: Reynolds Number (Re)")
        self.re_combo.addItems(["10", "100", "1000", "10000", "100000", "1E6", "1E7", "1E8", "1E9"])
        self.re_combo.setCurrentText(str(int(self.sim.re)))

        # K0 selector
        self.k0_combo = QComboBox()
        self.k0_combo.setToolTip("K: Initial energy peak wavenumber (K0)")
        self.k0_combo.addItems(["2", "3", "4", "5", "6", "7", "8", "9", "10",
                                "15", "20", "25", "35", "50", "90"])
        self.k0_combo.setCurrentText(str(int(self.sim.k0)))

        # Colormap selector
        self.cmap_combo = QComboBox()
        self.cmap_combo.setToolTip("C: Colormaps")
        self.cmap_combo.addItems(list(COLOR_MAPS.keys()))
        idx = self.cmap_combo.findText(DEFAULT_CMAP_NAME)
        if idx >= 0:
            self.cmap_combo.setCurrentIndex(idx)

        # CFL selector
        self.cfl_combo = QComboBox()
        self.cfl_combo.setToolTip("L: Controlling Δt (CFL)")
        self.cfl_combo.addItems(["0.05", "0.1", "0.15", "0.2", "0.25", "0.3", "0.5", "0.75", "0.95"])
        self.cfl_combo.setCurrentText(str(self.sim.cfl))

        # Steps selector
        self.steps_combo = QComboBox()
        self.steps_combo.setToolTip("S: Max steps before reset/stop")
        self.steps_combo.addItems(["2000", "5000", "10000", "25000", "50000", "1E5", "3E5", "1E6", "1E7"])
        self.steps_combo.setCurrentText("1E5")

        # Update selector
        self.update_combo = QComboBox()
        self.update_combo.setToolTip("U: Update intervall")
        self.update_combo.addItems(["1", "2", "5", "10", "20", "50", "100", "200", "1000"])
        self.update_combo.setCurrentText("20")

        self.auto_reset_checkbox = QCheckBox()
        self.auto_reset_checkbox.setToolTip("If checked, simulation auto-resets")
        self.auto_reset_checkbox.setChecked(False)

        if sys.platform == "darwin":
            from PySide6.QtWidgets import QStyleFactory
            self.variable_combo.setStyle(QStyleFactory.create(FUSION))
            self.cmap_combo.setStyle(QStyleFactory.create(FUSION))
            self.n_combo.setStyle(QStyleFactory.create(FUSION))
            self.k0_combo.setStyle(QStyleFactory.create(FUSION))
            self.cfl_combo.setStyle(QStyleFactory.create(FUSION))
            self.steps_combo.setStyle(QStyleFactory.create(FUSION))
            self.update_combo.setStyle(QStyleFactory.create(FUSION))

        self._build_layout()

        # --- status bar ---
        self.status = QStatusBar()
        self.setStatusBar(self.status)

        mono = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
        self.status.setFont(mono)

        self.threads_label = QLabel(self)
        self.status.addPermanentWidget(self.threads_label)

        # Timer-based simulation (no QThread)
        self.timer = QTimer(self)
        self.timer.setInterval(0)
        self.timer.timeout.connect(self._on_timer)  # type: ignore[attr-defined]

        # signal connections
        self.start_button.clicked.connect(self.on_start_clicked)  # type: ignore[attr-defined]
        self.stop_button.clicked.connect(self.on_stop_clicked)  # type: ignore[attr-defined]
        self.reset_button.clicked.connect(self.on_reset_clicked)  # type: ignore[attr-defined]
        self.save_button.clicked.connect(self.on_save_clicked)  # type: ignore[attr-defined]
        self.folder_button.clicked.connect(self.on_folder_clicked)  # type: ignore[attr-defined]

        # ---- FIX: init buttons now *directly* set _force_mode and call on_reset_clicked()
        self.init_pao_button.clicked.connect(lambda _=False: self._set_force_mode_and_reset("pao"))  # type: ignore[attr-defined]
        self.init_highh_button.clicked.connect(lambda _=False: self._set_force_mode_and_reset("highh"))  # type: ignore[attr-defined]
        self.init_rain_button.clicked.connect(lambda _=False: self._set_force_mode_and_reset("rain"))  # type: ignore[attr-defined]
        self.init_circle_button.clicked.connect(lambda _=False: self._set_force_mode_and_reset("circle"))  # type: ignore[attr-defined]
        self.init_mouse_button.clicked.connect(lambda _=False: self._set_force_mode_and_reset("mouse"))  # type: ignore[attr-defined]

        self.variable_combo.currentIndexChanged.connect(self.on_variable_changed)  # type: ignore[attr-defined]
        self.cmap_combo.currentTextChanged.connect(self.on_cmap_changed)  # type: ignore[attr-defined]
        self.n_combo.currentTextChanged.connect(self.on_n_changed)  # type: ignore[attr-defined]
        self.re_combo.currentTextChanged.connect(self.on_re_changed)  # type: ignore[attr-defined]
        self.k0_combo.currentTextChanged.connect(self.on_k0_changed)  # type: ignore[attr-defined]
        self.cfl_combo.currentTextChanged.connect(self.on_cfl_changed)  # type: ignore[attr-defined]
        self.steps_combo.currentTextChanged.connect(self.on_steps_changed)  # type: ignore[attr-defined]
        self.update_combo.currentTextChanged.connect(self.on_update_changed)  # type: ignore[attr-defined]

        _setup_shortcuts(self)

        # window setup
        import importlib.util

        title_backend = "(SciPy)"
        if importlib.util.find_spec("cupy") is not None:
            import cupy as cp

            try:
                props = cp.cuda.runtime.getDeviceProperties(0)
                gpu_name = props["name"].decode(errors="replace")
                title_backend = f"(CuPy) {gpu_name}"
            except (RuntimeError, OSError, ValueError, IndexError):
                pass

        self.setWindowTitle(f"2D Turbulence {title_backend} © Mannetroll")
        disp_w, disp_h = self._display_size_px()
        self.resize(disp_w + 40, disp_h + 120)

        # Keep-alive buffers for QImage wrappers
        self._last_pixels_rgb: Optional[np.ndarray] = None
        self._last_pixels_u8: Optional[np.ndarray] = None

        # init logic state (default mode, injectors, etc.)
        self._logic_init_defaults()

        # initial draw (omega mode)
        self.sim.set_variable(self.sim.VAR_OMEGA)
        self.variable_combo.setCurrentIndex(3)

        self._update_image(self.sim.get_frame_pixels())
        self._update_status(self.sim.get_time(), self.sim.get_iteration(), None, None)

        # set combobox data
        self.on_steps_changed(self.steps_combo.currentText())
        self.on_update_changed(self.update_combo.currentText())
        self.on_start_clicked()

    # ------------------------------------------------------------------
    # single place to set mode + reset (drives TurboLogic.on_reset_clicked)
    def _set_force_mode_and_reset(self, mode: str) -> None:
        self._force_mode = mode
        self._update_force_mode_buttons()
        self.on_reset_clicked()

    # ------------------------------------------------------------------
    def _display_scale(self) -> float:
        """
        Must match _upscale_downscale_u8() scale logic.

        Convention (based on your existing mapping code):
          - scale < 1.0  => upscale by (1/scale) (integer)
          - scale > 1.0  => downscale by scale   (integer)

        Goal: displayed_size ~= N / down <= max_h   (for big N)
              displayed_size ~= N * up  <= max_h   (for small N)
        """
        N = int(self.sim.N)

        # MacBook Pro window height you mentioned:
        screen_h = 1024

        # Leave room for top buttons, status bar, margins, etc.
        # Tune this once if you want it a bit larger/smaller on screen.
        ui_margin = 320
        max_h = max(128, screen_h - ui_margin)

        if N >= max_h:
            down = int(math.ceil(N / max_h))  # integer downscale so N/down <= max_h
            return float(down)

        up = int(math.floor(max_h / N))  # integer upscale so N*up <= max_h
        if up < 1:
            up = 1
        return 1.0 / float(up)

    def _display_size_px(self) -> tuple[int, int]:
        scale = self._display_scale()
        w0 = int(self.sim.px)
        h0 = int(self.sim.py)

        if scale == 1.0:
            return w0, h0

        if scale < 1.0:
            up = int(round(1.0 / scale))
            return w0 * up, h0 * up

        s = int(scale)
        return max(1, w0 // s), max(1, h0 // s)

    @staticmethod
    def move_widgets(src_layout, dst_layout) -> None:
        while src_layout.count() > 0:
            item = src_layout.takeAt(0)
            w = item.widget()
            if w is not None:
                dst_layout.addWidget(w)

    def _update_force_mode_buttons(self) -> None:
        mode_to_btn = {
            "pao": self.init_pao_button,
            "highh": self.init_highh_button,
            "circle": self.init_circle_button,
            "rain": self.init_rain_button,
            "mouse": self.init_mouse_button,
        }

        current = getattr(self, "_force_mode", "")
        for mode, btn in mode_to_btn.items():
            f = btn.font()
            f.setBold(mode == current)
            btn.setFont(f)

    def _build_layout(self) -> None:
        old = self.centralWidget()
        if old is not None:
            old.setParent(None)

        central = QWidget()
        main = QVBoxLayout(central)
        main.setSpacing(3)

        row0 = QHBoxLayout()
        row0.addWidget(self.init_pao_button)
        row0.addWidget(self.init_highh_button)
        row0.addWidget(self.init_rain_button)
        row0.addWidget(self.init_circle_button)
        row0.addWidget(self.init_mouse_button)
        main.addLayout(row0)

        main.addWidget(self.image_label)

        row1 = QHBoxLayout()
        row1.addSpacing(10)
        row1.addWidget(self.start_button)
        row1.addWidget(self.stop_button)
        row1.addWidget(self.reset_button)
        row1.addWidget(self.save_button)
        row1.addWidget(self.folder_button)
        row1.addSpacing(10)
        row1.addWidget(self.n_combo)
        row1.addWidget(self.variable_combo)
        row1.addWidget(self.cmap_combo)
        row1.addWidget(self.re_combo)
        row1.addWidget(self.k0_combo)
        row1.addWidget(self.cfl_combo)
        row1.addWidget(self.update_combo)
        row1.addWidget(self.steps_combo)
        row1.addSpacing(5)
        row1.addWidget(self.auto_reset_checkbox)
        row1.addStretch(1)
        main.addLayout(row1)

        self.setCentralWidget(central)

    def _upscale_downscale_u8(self, pix: np.ndarray) -> np.ndarray:
        scale = self._display_scale()

        if scale == 1.0:
            return np.ascontiguousarray(pix)

        if scale < 1.0:
            up = int(round(1.0 / scale))
            return np.ascontiguousarray(np.repeat(np.repeat(pix, up, axis=0), up, axis=1))

        s = int(scale)
        return np.ascontiguousarray(pix[::s, ::s])

    def _update_image(self, pixels: np.ndarray) -> None:

        pix_f = pixels.astype(np.float32, copy=False)
        if self._force_mode == "rain":
            self.sig = 5.0
        elif self._force_mode in ("circle", "mouse"):
            self.sig = 10.0
        else:
            self.sig = float(pix_f.std())

        if self.sig < 1.0 and (self._force_mode == "pao" or self._force_mode == "highh"):
            self.on_stop_clicked()
            return

        # --- grain metrics for stability (from ω field, full grid) ---
        self.palinstrophy_over_enstrophy_kmax2 = self.pal_over_ens_kmax2()

        self.mu = float(pix_f.mean())
        k = float(DISPLAY_NORM_K_STD)
        lo = self.mu - k * self.sig
        hi = self.mu + k * self.sig
        inv = 255.0 / (hi - lo) if (hi - lo) != 0.0 else 0.0
        pixels = ((pix_f - lo) * inv).round().clip(0.0, 255.0).astype(np.uint8)
        pixels = self._upscale_downscale_u8(pixels)
        h, w = pixels.shape
        qimg = QImage(
            pixels.data,
            w,
            h,
            w,
            QImage.Format.Format_Indexed8,
        )
        table = QT_COLOR_TABLES.get(self.current_cmap_name, QT_GRAY_TABLE)
        qimg.setColorTable(table)
        pix = QPixmap.fromImage(qimg, Qt.ImageConversionFlag.NoFormatConversion)
        self.image_label.setPixmap(pix)

    def _update_status(self, t: float, it: int, fps: Optional[float], mspf: Optional[float]) -> None:
        fps_str = f"{fps:5.2f}" if fps is not None else " N/A"
        sig_str = f"{int(self.sig)}" if self.sig is not None else " N/A"

        # DPP = Display Pixel Percentage
        # dpp = int(100 / self._display_scale())

        elapsed_min = (time.time() - self._sim_start_time) / 60.0
        visc = float(self.sim.state.visc)
        dt = float(self.sim.state.dt)

        # Grain metrics row
        if self.palinstrophy_over_enstrophy_kmax2 is None:
            pr_str = "N/A"
        else:
            pr_str = f"{(10000*self.palinstrophy_over_enstrophy_kmax2):.1f}"

        txt = (
            f"   FPS: {fps_str} | pal/Zkmax²: {pr_str} | σ: {sig_str} | Iter: {it:5d} | T: {t:6.3f} | dt: {dt:.6f} "
            f"| {elapsed_min:4.1f} min | Visc: {visc:6g} | {_dt.datetime.now().strftime('%Y-%m-%d %H:%M')}"
        )
        self.status.showMessage(txt)

    # ------------------------------------------------------------------
    def on_save_clicked(self) -> None:
        var_name = self.variable_combo.currentText()
        cmap_name = self.cmap_combo.currentText()
        default_name = f"cupystorm_{var_name}_{cmap_name}.png"

        desktop = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DesktopLocation)
        initial_path = desktop + "/" + default_name

        path, _ = QFileDialog.getSaveFileName(
            self,
            "Save frame",
            initial_path,
            "PNG images (*.png);;All files (*)",
        )

        if path:
            pm = self.image_label.pixmap()
            if pm:
                pm.save(path, "PNG")

    def on_variable_changed(self, index: int) -> None:
        mapping = {
            0: self.sim.VAR_U,
            1: self.sim.VAR_V,
            2: self.sim.VAR_ENERGY,
            3: self.sim.VAR_OMEGA,
            4: self.sim.VAR_STREAM,
        }
        self.sim.set_variable(mapping.get(index, self.sim.VAR_U))
        self._update_image(self.sim.get_frame_pixels())

    def on_cmap_changed(self, name: str) -> None:
        if name in COLOR_MAPS:
            self.current_cmap_name = name
            self._update_image(self.sim.get_frame_pixels())

    def on_n_changed(self, value: str) -> None:
        N = int(value)
        self.sim.set_N(N)

        # 1) Update the image first
        self._update_image(self.sim.get_frame_pixels())

        # 2) Compute new geometry from the intended display size (NOT pixmap)
        disp_w, disp_h = self._display_size_px()
        new_w = disp_w + 40
        new_h = disp_h + 120

        print("Resize to:", new_w, new_h)

        # 3) Allow the window to shrink (RESET constraints)
        self.setMinimumSize(0, 0)
        self.setMaximumSize(16777215, 16777215)

        # 4) Now resize → Qt WILL shrink
        self.resize(new_w, new_h)

        # 5) Recenter
        screen = QApplication.primaryScreen().availableGeometry()
        g = self.geometry()
        g.moveCenter(screen.center())
        self.setGeometry(g)

        self._build_layout()
        self._sim_start_time = time.time()
        self._sim_start_iter = self.sim.get_iteration()

        # IMPORTANT: keep the current mode after changing N
        self.on_reset_clicked()

    def _desktop_path(self) -> str:
        return QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DesktopLocation)

    def _make_folder_dialog(self, title: str, start_dir: str) -> QFileDialog:
        # --- Create non-native dialog (macOS compatible) ---
        dlg = QFileDialog(self)
        dlg.setWindowTitle(title)
        dlg.setFileMode(QFileDialog.FileMode.Directory)
        dlg.setOption(QFileDialog.Option.ShowDirsOnly, True)
        dlg.setOption(QFileDialog.Option.DontUseNativeDialog, True)
        dlg.setDirectory(start_dir)

        # Prefill the directory edit field
        for lineedit in dlg.findChildren(QLineEdit):
            lineedit.setText(".")  # type: ignore[attr-defined]

        return dlg

    def keyPressEvent(self, event) -> None:
        key = event.key()

        if key == Qt.Key.Key_V:
            idx = self.variable_combo.currentIndex()
            self.variable_combo.setCurrentIndex((idx + 1) % self.variable_combo.count())
            return

        if key == Qt.Key.Key_C:
            idx = self.cmap_combo.currentIndex()
            self.cmap_combo.setCurrentIndex((idx + 1) % self.cmap_combo.count())
            return

        if key == Qt.Key.Key_N:
            idx = self.n_combo.currentIndex()
            self.n_combo.setCurrentIndex((idx + 1) % self.n_combo.count())
            return

        if key == Qt.Key.Key_R:
            idx = self.re_combo.currentIndex()
            self.re_combo.setCurrentIndex((idx + 1) % self.re_combo.count())
            return

        if key == Qt.Key.Key_K:
            idx = self.k0_combo.currentIndex()
            self.k0_combo.setCurrentIndex((idx + 1) % self.k0_combo.count())
            return

        if key == Qt.Key.Key_L:
            idx = self.cfl_combo.currentIndex()
            self.cfl_combo.setCurrentIndex((idx + 1) % self.cfl_combo.count())
            return

        if key == Qt.Key.Key_S:
            idx = self.steps_combo.currentIndex()
            self.steps_combo.setCurrentIndex((idx + 1) % self.steps_combo.count())
            return

        if key == Qt.Key.Key_U:
            idx = self.update_combo.currentIndex()
            self.update_combo.setCurrentIndex((idx + 1) % self.update_combo.count())
            return

        super().keyPressEvent(event)