// SPDX-License-Identifier: Unlicense

#include "input.h"

#include "global.h"
#include "opengl.h"
#include "sound.h"
#include "type.h"

#ifdef SHR3D_WINDOW_SDL
#include <SDL_events.h>

#define K_SPACE SDLK_SPACE
#define K_BACKSPACE SDLK_BACKSPACE
#define K_HOME SDLK_HOME
#define K_q SDLK_q
#define K_a SDLK_a
#define K_d SDLK_d
#define K_w SDLK_w
#define K_s SDLK_s
#define K_e SDLK_e
#define K_c SDLK_c
#define K_LEFT SDLK_LEFT
#define K_RIGHT SDLK_RIGHT
#define K_KP_DIVIDE SDLK_KP_DIVIDE
#define K_KP_MULTIPLY SDLK_KP_MULTIPLY
#define K_KP_MINUS SDLK_KP_MINUS
#define K_KP_PLUS SDLK_KP_PLUS
#define K_KP_0 SDLK_KP_0
#define K_KP_1 SDLK_KP_1
#define K_KP_2 SDLK_KP_2
#define K_KP_3 SDLK_KP_3
#define K_KP_4 SDLK_KP_4
#define K_KP_5 SDLK_KP_5
#define K_KP_6 SDLK_KP_6
#define K_KP_7 SDLK_KP_7
#define K_KP_8 SDLK_KP_8
#define K_KP_9 SDLK_KP_9
#define K_LEFT SDLK_LEFT
#define K_RIGHT SDLK_RIGHT
#define K_UP SDLK_UP
#define K_DOWN SDLK_DOWN
#define K_TAB SDLK_TAB
#define K_F1 SDLK_F1
#define K_F2 SDLK_F2
#define K_F3 SDLK_F3
#define K_F4 SDLK_F4
#define K_F5 SDLK_F5
#define K_F6 SDLK_F6
#define K_F7 SDLK_F7
#define K_F8 SDLK_F8
#define K_F9 SDLK_F9
#define K_F10 SDLK_F10
#define K_F11 SDLK_F11
#define K_F12 SDLK_F12
#define K_PAUSE SDLK_PAUSE
#define K_ESCAPE SDLK_ESCAPE
#define K_RETURN SDLK_RETURN
#define K_END SDLK_END
// mod keys
#define K_SHIFT KMOD_SHIFT
#define K_LSHIFT SDLK_LSHIFT
#define K_RSHIFT SDLK_RSHIFT
#define K_CTRL KMOD_CTRL
#define K_LCTRL SDLK_LCTRL
#define K_RCTRL SDLK_RCTRL
#define K_ALT KMOD_ALT
#define K_LALT SDLK_LALT
#define K_RALT SDLK_RALT
// mouse
#define K_LMB SDL_BUTTON_LEFT

#endif // SHR3D_WINDOW_SDL

#ifdef SHR3D_WINDOW_WIN32

#define K_SPACE VK_SPACE
#define K_BACKSPACE VK_BACK
#define K_HOME VK_HOME
#define K_q 0x51
#define K_a 0x41
#define K_d 0x44
#define K_w 0x57
#define K_s 0x53
#define K_e 0x45
#define K_c 0x43
#define K_LEFT VK_LEFT
#define K_RIGHT VK_RIGHT
#define K_KP_DIVIDE VK_DIVIDE
#define K_KP_MULTIPLY VK_MULTIPLY
#define K_KP_MINUS VK_SUBTRACT
#define K_KP_PLUS VK_ADD
#define K_KP_0 VK_NUMPAD0
#define K_KP_1 VK_NUMPAD1
#define K_KP_2 VK_NUMPAD2
#define K_KP_3 VK_NUMPAD3
#define K_KP_4 VK_NUMPAD4
#define K_KP_5 VK_NUMPAD5
#define K_KP_6 VK_NUMPAD6
#define K_KP_7 VK_NUMPAD7
#define K_KP_8 VK_NUMPAD8
#define K_KP_9 VK_NUMPAD9
#define K_UP VK_UP
#define K_DOWN VK_DOWN
#define K_TAB VK_TAB
#define K_F1 VK_F1
#define K_F2 VK_F2
#define K_F3 VK_F3
#define K_F4 VK_F4
#define K_F5 VK_F5
#define K_F6 VK_F6
#define K_F7 VK_F7
#define K_F8 VK_F8
#define K_F9 VK_F9
#define K_F10 VK_F10
#define K_F11 VK_F11
#define K_F12 VK_F12
#define K_PAUSE VK_PAUSE
#define K_ESCAPE VK_ESCAPE
#define K_RETURN VK_RETURN
#define K_END VK_END
// mod keys
#define K_SHIFT VK_SHIFT
#define K_LSHIFT VK_LSHIFT
#define K_RSHIFT VK_RSHIFT
#define K_CTRL VK_CONTROL
#define K_LCTRL VK_LCONTROL
#define K_RCTRL VK_RCONTROL
#define K_ALT VK_MENU
#define K_LALT VK_LMENU
#define K_RALT VK_RMENU
// mouse
#define K_LMB VK_LBUTTON

#endif // SHR3D_WINDOW_WIN32

#ifdef SHR3D_WINDOW_X11

#define XK_LATIN1
#define XK_MISCELLANY
#include <X11/Xlib.h>
#include <X11/keysymdef.h>

#define K_SPACE XK_space
#define K_BACKSPACE XK_BackSpace
#define K_HOME XK_Home
#define K_q XK_q
#define K_a XK_a
#define K_d XK_d
#define K_w XK_w
#define K_s XK_s
#define K_e XK_e
#define K_c XK_c
#define K_LEFT XK_Left
#define K_RIGHT XK_Right
#define K_KP_DIVIDE XK_KP_Divide
#define K_KP_MULTIPLY XK_KP_Multiply
#define K_KP_MINUS XK_KP_Subtract
#define K_KP_PLUS XK_KP_Add
#define K_KP_0 XK_KP_0
#define K_KP_1 XK_KP_1
#define K_KP_2 XK_KP_2
#define K_KP_3 XK_KP_3
#define K_KP_4 XK_KP_4
#define K_KP_5 XK_KP_5
#define K_KP_6 XK_KP_6
#define K_KP_7 XK_KP_7
#define K_KP_8 XK_KP_8
#define K_KP_9 XK_KP_9
#define K_UP XK_Up
#define K_DOWN XK_Down
#define K_TAB XK_Tab
#define K_F1 XK_F1
#define K_F2 XK_F2
#define K_F3 XK_F3
#define K_F4 XK_F4
#define K_F5 XK_F5
#define K_F6 XK_F6
#define K_F7 XK_F7
#define K_F8 XK_F8
#define K_F9 XK_F9
#define K_F10 XK_F10
#define K_F11 XK_F11
#define K_F12 XK_F12
#define K_PAUSE XK_Pause
#define K_ESCAPE XK_Escape
#define K_RETURN XK_Return
#define K_END XK_End
// mod keys
//#define K_SHIFT XK_Shift
#define K_LSHIFT XK_Shift_L
#define K_RSHIFT XK_Shift_R
//#define K_CTRL XK_Control
#define K_LCTRL XK_Control_L
#define K_RCTRL XK_Control_R
//#define K_ALT XK_Alt
#define K_LALT XK_Alt_L
#define K_RALT XK_Alt_R
// mouse
#define K_LMB Button1

#endif // SHR3D_WINDOW_X11

static void handleInputBeginKeyInput(KeyState& keyState) {
  keyState.heldDownLastFrame = keyState.heldDown;
  keyState.pressed = keyState.clickedInUiOrMidi; // clicks in the UI lack behind one frame.
  keyState.clickedInUiOrMidi = false;
  keyState.released = false;
}

void Input::keyStateChange(u32 key, const bool down)
{
#ifndef PLATFORM_OPENXR_ANDROID
#ifndef SHR3D_WINDOW_ANDROID
  switch (key)
  {
  case K_q:
    Global::inputQ.heldDown = down;
    break;
  case K_a:
    Global::inputA.heldDown = down;
    break;
  case K_d:
    Global::inputD.heldDown = down;
    break;
  case K_w:
    Global::inputW.heldDown = down;
    break;
  case K_s:
    Global::inputS.heldDown = down;
    break;
  case K_e:
    Global::inputE.heldDown = down;
    break;
  case K_c:
    Global::inputC.heldDown = down;
    break;
  case K_LEFT:
    Global::inputLeft.heldDown = down;
    break;
  case K_RIGHT:
    Global::inputRight.heldDown = down;
    break;
  case K_KP_DIVIDE:
    Global::inputKPDivide.heldDown = down;
    break;
  case K_KP_MULTIPLY:
    Global::inputKPMultiply.heldDown = down;
    break;
  case K_KP_MINUS:
    Global::inputKPMinus.heldDown = down;
    break;
  case K_KP_PLUS:
    Global::inputKPPlus.heldDown = down;
    break;
  case K_KP_0:
    Global::inputKP0.heldDown = down;
    break;
  case K_KP_1:
    Global::inputKP1.heldDown = down;
    break;
  case K_KP_2:
    Global::inputKP2.heldDown = down;
    break;
  case K_KP_3:
    Global::inputKP3.heldDown = down;
    break;
  case K_KP_4:
    Global::inputKP4.heldDown = down;
    break;
  case K_KP_5:
    Global::inputKP5.heldDown = down;
    break;
  case K_KP_6:
    Global::inputKP6.heldDown = down;
    break;
  case K_KP_7:
    Global::inputKP7.heldDown = down;
    break;
  case K_KP_8:
    Global::inputKP8.heldDown = down;
    break;
  case K_KP_9:
    Global::inputKP9.heldDown = down;
    break;
  case K_UP:
    break;
  case K_DOWN:
    break;
  case K_TAB:
    break;
  case K_F1:
    Global::inputTuner.heldDown = down;
    break;
  case K_F2:
    Global::inputSfxChain.heldDown = down;
    break;
  case K_F3:
    Global::inputMetronome.heldDown = down;
    break;
  case K_F4:
    Global::inputSwitchInstrument.heldDown = down;
    break;
  case K_F5:
    Global::inputQuickRepeater.heldDown = down;
    break;
  case K_F6:
    Global::inputLevel.heldDown = down;
    break;
  case K_F7:
    Global::inputEnvironmentMilk.heldDown = down;
    break;
  case K_F8:
    Global::inputStrumDirection.heldDown = down;
    break;
  case K_F9:
    Global::inputDebugInfo.heldDown = down;
    break;
  case K_F10:
    Global::inputNoclip.heldDown = down;
    break;
  case K_F11:
    Global::inputFreezeHighway.heldDown = down;
    break;
  case K_F12:
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_LINUX)
    Global::inputWireframe.heldDown = down;
#endif // PLATFORM_WINDOWS || PLATFORM_LINUX
    break;
  case K_PAUSE:
    Global::inputMute.heldDown = down;
    break;
  case K_ESCAPE:
    Global::inputHideMenu.heldDown = down;
    break;
  case K_RETURN:
    Global::inputReturn.heldDown = down;
    break;
#ifdef SHR3D_COOP
  case K_END:
    Global::inputEject.heldDown = down;
    break;
#endif // SHR3D_COOP
  case K_SPACE:
    Global::inputQuickRepeaterJumpBegin.heldDown = down;
    break;
  case K_BACKSPACE:
    break;
#ifdef SHR3D_RECORDER
  case K_HOME:
    Global::inputRecorder.heldDown = down;
    break;
#endif // SHR3D_RECORDER
  case K_SHIFT:
  case K_LSHIFT:
  case K_RSHIFT:
    Global::inputShift.heldDown = down;
    break;
  case K_CTRL:
  case K_LCTRL:
  case K_RCTRL:
    Global::inputCtrl.heldDown = down;
    break;
  case K_ALT:
  case K_LALT:
  case K_RALT:
    Global::inputAlt.heldDown = down;
    break;
  case K_LMB:
    Global::inputLmb.heldDown = down;
    break;
  }
#endif // SHR3D_WINDOW_ANDROID
#endif // PLATFORM_OPENXR_ANDROID
}

void Input::handleInputBegin()
{
  handleInputBeginKeyInput(Global::inputQ);
  handleInputBeginKeyInput(Global::inputLeft);
  handleInputBeginKeyInput(Global::inputRight);
  handleInputBeginKeyInput(Global::inputKPDivide);
  handleInputBeginKeyInput(Global::inputKPMultiply);
  handleInputBeginKeyInput(Global::inputKPMinus);
  handleInputBeginKeyInput(Global::inputKPPlus);
  handleInputBeginKeyInput(Global::inputKP0);
  handleInputBeginKeyInput(Global::inputKP1);
  handleInputBeginKeyInput(Global::inputKP2);
  handleInputBeginKeyInput(Global::inputKP3);
  handleInputBeginKeyInput(Global::inputKP4);
  handleInputBeginKeyInput(Global::inputKP5);
  handleInputBeginKeyInput(Global::inputKP6);
  handleInputBeginKeyInput(Global::inputKP7);
  handleInputBeginKeyInput(Global::inputKP8);
  handleInputBeginKeyInput(Global::inputKP9);
  handleInputBeginKeyInput(Global::inputFreezeHighway);
  handleInputBeginKeyInput(Global::inputTuner);
  handleInputBeginKeyInput(Global::inputSfxChain);
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_LINUX)
  handleInputBeginKeyInput(Global::inputWireframe);
#endif // PLATFORM_WINDOWS || PLATFORM_LINUX
  handleInputBeginKeyInput(Global::inputDebugInfo);
  handleInputBeginKeyInput(Global::inputMute);
#ifdef SHR3D_COOP
  //handleInputBeginKeyInput(Global::inputCoop);
#endif // SHR3D_COOP
  handleInputBeginKeyInput(Global::inputLevel);
  handleInputBeginKeyInput(Global::inputStrumDirection);
  handleInputBeginKeyInput(Global::inputEject);
  handleInputBeginKeyInput(Global::inputQuickRepeaterJumpBegin);
#ifdef SHR3D_RECORDER
  handleInputBeginKeyInput(Global::inputRecorder);
#endif // SHR3D_RECORDER
  handleInputBeginKeyInput(Global::inputMetronome);
  handleInputBeginKeyInput(Global::inputSwitchInstrument);
  handleInputBeginKeyInput(Global::inputHideMenu);
  handleInputBeginKeyInput(Global::inputNoclip);
  handleInputBeginKeyInput(Global::inputQuickRepeater);
  handleInputBeginKeyInput(Global::inputEnvironmentMilk);
  handleInputBeginKeyInput(Global::inputDelete);
  handleInputBeginKeyInput(Global::inputShift);
  handleInputBeginKeyInput(Global::inputCtrl);
  handleInputBeginKeyInput(Global::inputAlt);
  handleInputBeginKeyInput(Global::inputReturn);
  handleInputBeginKeyInput(Global::inputLmb);

  Global::inputWheelDelta = 0;
}

static void handleInputEndKeyInput(KeyState& keyState)
{
  if (keyState.heldDown && !keyState.heldDownLastFrame)
  {
    keyState.pressed = true;
    keyState.toggled = !keyState.toggled;
  }
  else if (!keyState.heldDown && keyState.heldDownLastFrame)
  {
    keyState.released = true;
  }
}

void Input::handleInputEnd()
{
  handleInputEndKeyInput(Global::inputQ);
  handleInputEndKeyInput(Global::inputLeft);
  handleInputEndKeyInput(Global::inputRight);
  handleInputEndKeyInput(Global::inputKPDivide);
  handleInputEndKeyInput(Global::inputKPMultiply);
  handleInputEndKeyInput(Global::inputKPMinus);
  handleInputEndKeyInput(Global::inputKPPlus);
  handleInputEndKeyInput(Global::inputKP0);
  handleInputEndKeyInput(Global::inputKP1);
  handleInputEndKeyInput(Global::inputKP2);
  handleInputEndKeyInput(Global::inputKP3);
  handleInputEndKeyInput(Global::inputKP4);
  handleInputEndKeyInput(Global::inputKP5);
  handleInputEndKeyInput(Global::inputKP6);
  handleInputEndKeyInput(Global::inputKP7);
  handleInputEndKeyInput(Global::inputKP8);
  handleInputEndKeyInput(Global::inputKP9);
  handleInputEndKeyInput(Global::inputFreezeHighway);
  handleInputEndKeyInput(Global::inputTuner);
  handleInputEndKeyInput(Global::inputSfxChain);
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_LINUX)
  handleInputEndKeyInput(Global::inputWireframe);
#endif // PLATFORM_WINDOWS || PLATFORM_LINUX
  handleInputEndKeyInput(Global::inputDebugInfo);
  handleInputEndKeyInput(Global::inputMute);
#ifdef SHR3D_COOP
  //handleInputEndKeyInput(Global::inputCoop);
#endif // SHR3D_COOP
  handleInputEndKeyInput(Global::inputLevel);
  handleInputEndKeyInput(Global::inputStrumDirection);
  handleInputEndKeyInput(Global::inputEject);
  handleInputEndKeyInput(Global::inputQuickRepeaterJumpBegin);
#ifdef SHR3D_RECORDER
  handleInputEndKeyInput(Global::inputRecorder);
#endif // SHR3D_RECORDER
  handleInputEndKeyInput(Global::inputMetronome);
  handleInputEndKeyInput(Global::inputSwitchInstrument);
  handleInputEndKeyInput(Global::inputHideMenu);
  handleInputEndKeyInput(Global::inputNoclip);
  handleInputEndKeyInput(Global::inputQuickRepeater);
  handleInputEndKeyInput(Global::inputEnvironmentMilk);
  handleInputEndKeyInput(Global::inputDelete);
  handleInputEndKeyInput(Global::inputShift);
  handleInputEndKeyInput(Global::inputCtrl);
  handleInputEndKeyInput(Global::inputAlt);
  handleInputEndKeyInput(Global::inputReturn);
  handleInputEndKeyInput(Global::inputLmb);

#ifdef PLATFORM_ANDROID_SDL
  // double tab should count as escape and close the menu
  if (Global::inputLmb.pressed)
  {
    static TimeNS lastPressedTime;
    if (lastPressedTime + timeNS_From_Seconds(0.3f) > Global::time_)
      Global::inputHideMenu.toggled = !Global::inputHideMenu.toggled;
    lastPressedTime = Global::time_;
  }
#endif // PLATFORM_ANDROID_SDL
}

#ifdef SHR3D_WINDOW_SDL
static void toggleFullscreen()
{
  switch (Settings::graphicsFullscreen)
  {
  case FullscreenMode::windowed:
    Settings::graphicsFullscreen = FullscreenMode::borderless;
    SDL_SetWindowFullscreen(Global::window, SDL_WINDOW_FULLSCREEN_DESKTOP);
    break;
  case FullscreenMode::borderless:
    Settings::graphicsFullscreen = FullscreenMode::windowed;
    SDL_SetWindowFullscreen(Global::window, 0);
    break;
  default:
    unreachable();
  }
}
#endif // SHR3D_WINDOW_SDL

void Input::proccessInputEvents()
{
#ifdef SHR3D_WINDOW_SDL
  if (Global::inputReturn.pressed && Global::inputAlt.heldDown)
    toggleFullscreen();
#endif // SHR3D_WINDOW_SDL

#ifdef SHR3D_ENVIRONMENT_MILK
  if (Global::inputEnvironmentMilk.pressed)
    Settings::environmentMilk = !Settings::environmentMilk;
#endif // SHR3D_ENVIRONMENT_MILK

  if (Global::inputMetronome.pressed)
    Settings::metronomeEnabled = !Settings::metronomeEnabled;

  if (Global::inputSwitchInstrument.pressed)
  {
    const i32 arrangementIndexCount = i32(Global::songTracks.size());

    if (arrangementIndexCount >= 1)
    {
      Global::selectedArrangementIndex = (Global::selectedArrangementIndex + 1) % arrangementIndexCount;

      if (Global::songInfos[Global::selectedSongIndex].arrangementInfos[Global::selectedArrangementIndex].arrangementName == "Vocals")
        Global::selectedArrangementIndex = (Global::selectedArrangementIndex + 1) % arrangementIndexCount;
    }
  }

  if (Global::inputQ.pressed && Global::inputCtrl.heldDown)
  {
    Global::appQuit = true;
  }

  if (Global::inputLeft.pressed)
  {
    if (Global::selectedSongIndex >= 0)
    {
      const TimeNS nextMusicTimeElapsedNS = max_(-Global::songInfos[Global::selectedSongIndex].shredDelayBegin, Global::musicTimeElapsedNS - 10_s);
      Global::musicPlaybackPosition = i64(musicTimeElapsedToPlaybackPositionNonStretched(nextMusicTimeElapsedNS, f32(sampleRate())) * Global::musicStretchRatio);
      Global::musicTimeElapsedNS = nextMusicTimeElapsedNS;
    }
  }
  if (Global::inputRight.pressed)
  {
    Global::musicTimeElapsedNS += 10_s;
    Global::musicPlaybackPosition = i64(musicTimeElapsedToPlaybackPositionNonStretched(Global::musicTimeElapsedNS, f32(sampleRate())) * Global::musicStretchRatio);
  }

  if (Global::inputKPDivide.pressed)
    Settings::applicationToneSwitch = !Settings::applicationToneSwitch;

  if (Global::inputKPMultiply.pressed)
  {
    // use tone from song
    if (Global::selectedSongIndex >= 0 && Global::selectedArrangementIndex >= 0)
    {
      const SfxBankIndex sfxBankIndex = Global::songStats[Global::songInfos[Global::selectedSongIndex].arrangementInfos[Global::selectedArrangementIndex].persistentId].sfxBankIndex * Const::sfxToneTonesPerBank;
      //if (sfxBankIndex != -1 * Const::sfxToneTonesPerBank)
      Global::activeSfxToneIndex = Global::activeSfxToneIndex != sfxBankIndex ? sfxBankIndex : 0;
    }
    else
      Global::activeSfxToneIndex = 0;
  }
  if (Global::inputKPMinus.pressed)
  {
    // decrease SfxToneBank
    const i32 toneBankDecrease = Global::inputCtrl.heldDown ? 10 : 1;
    const i32 newBankIndex = max_((Global::firstEmptyNegativeToneBank + 1), (Global::activeSfxToneIndex / Const::sfxToneTonesPerBank) - toneBankDecrease);
    const i32 tone = (abs(Global::activeSfxToneIndex) % Const::sfxToneTonesPerBank);
    Global::activeSfxToneIndex = newBankIndex * Const::sfxToneTonesPerBank + (newBankIndex >= 0 ? tone : -tone);

  }
  if (Global::inputKPPlus.pressed)
  {
    // increase SfxToneBank
    const i32 toneBankIncrease = Global::inputCtrl.heldDown ? 10 : 1;
    const i32 newBankIndex = (Global::activeSfxToneIndex / Const::sfxToneTonesPerBank) + toneBankIncrease;
    const i32 tone = (abs(Global::activeSfxToneIndex) % Const::sfxToneTonesPerBank);
    Global::activeSfxToneIndex = newBankIndex * Const::sfxToneTonesPerBank + (newBankIndex >= 0 ? tone : -tone);
  }
  if (Global::inputKP0.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10;
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP1.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 10 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 1 : -1);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP2.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 20 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 2 : -2);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP3.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 30 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 3 : -3);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP4.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 40 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 4 : -4);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP5.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 50 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 5 : -5);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP6.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 60 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 6 : -6);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP7.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 70 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 7 : -7);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP8.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 80 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 8 : -8);
    }
#endif // SHR3D_SFX
  }
  if (Global::inputKP9.pressed)
  {
#ifdef SHR3D_SFX
    if (Global::inputCtrl.heldDown)
    {
      const i32 tone = 90 + abs(Global::activeSfxToneIndex) % 10;
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 100) * 100 + (Global::activeSfxToneIndex >= 0 ? tone : -tone);
    }
    else
    {
      Global::activeSfxToneIndex = (Global::activeSfxToneIndex / 10) * 10 + (Global::activeSfxToneIndex >= 0 ? 9 : -9);
    }
#endif // SHR3D_SFX
  }
}
