// SPDX-License-Identifier: Unlicense

#include "global.h"

#ifdef SHR3D_WINDOW_WIN32
#include <Windows.h> // for WINDOWPLACEMENT
#endif // SHR3D_WINDOW_WIN32

#ifdef SHR3D_WINDOW_SDL
SDL_Window* Global::window = nullptr;
SDL_GLContext Global::glContext = nullptr;
SDL_GameController* Global::gameController = nullptr;
//#ifdef SHR3D_CUSTOM_CURSOR
SDL_Cursor* Global::defaultCursor = nullptr;
//#endif // SHR3D_CUSTOM_CURSOR
#ifdef SHR3D_COOP
u32 Global::coopWindowId;
SDL_Window* Global::coopWindow = nullptr;
#endif // SHR3D_COOP
#endif // SHR3D_WINDOW_SDL
#ifdef SHR3D_BENCHMARK_FPS
u64 Global::benchmarkScore;
u64 Global::benchmarkScoreLast;
f32 Global::benchmarkAvgFPS;
#endif // SHR3D_BENCHMARK_FPS
#ifdef SHR3D_WINDOW_WIN32
HWND Global::window = nullptr;
HDC Global::hdc = nullptr;
HGLRC Global::glContext = nullptr;
HGLRC Global::pluginGlContext = nullptr;
WINDOWPLACEMENT Global::lastMainWindowPlacement;
PIXELFORMATDESCRIPTOR Global::pixelFormatDescriptor;
int Global::pixelFormat;
#ifdef SHR3D_COOP
HWND Global::coopWindow = nullptr;
HDC Global::coopHdc = nullptr;
#endif // SHR3D_COOP
#endif // SHR3D_WINDOW_WIN32
#ifdef SHR3D_COOP
i32 Global::coopResolutionWidth = 320;
i32 Global::coopResolutionHeight = 240;
ArrangementIndex Global::coopSelectedArrangementIndex = 0;
FullscreenMode Global::coopWindowFullscreenMode = FullscreenMode::windowed;
Highway::Ctx Global::coopHighwayCtx;
Hud::Ctx Global::coopHudCtx;
#endif // SHR3D_COOP
#ifdef __ANDROID__
JavaVM* Global::g_JVM = nullptr;
void* Global::androidActivity = nullptr;
std::u8string Global::rootPath =
#ifdef PLATFORM_OPENXR_ANDROID
#ifdef PLATFORM_QUEST_3
u8"/data/data/app.shr3d.quest3/";
#else // PLATFORM_QUEST_3
u8"/data/data/app.shr3d.pico4/";
#endif // PLATFORM_QUEST_3
#else // PLATFORM_OPENXR_ANDROID
u8"/data/user/0/it.shr3d/";
#endif // PLATFORM_OPENXR_ANDROID
bool Global::androidRecordMicrophonePermissionGranted = false;
#endif // __ANDROID__
bool Global::appQuit = false;
i32 Global::resolutionWidth;
i32 Global::resolutionHeight;
Hud::Ctx Global::hudCtx;
Highway::Ctx Global::highwayCtx;
KeyState Global::inputA;
KeyState Global::inputD;
KeyState Global::inputW;
KeyState Global::inputS;
KeyState Global::inputE;
KeyState Global::inputC;
KeyState Global::inputQ;
KeyState Global::inputLeft;
KeyState Global::inputRight;
KeyState Global::inputMute;
#ifdef SHR3D_COOP
KeyState Global::inputCoop;
#endif // SHR3D_COOP
KeyState Global::inputKPDivide;
KeyState Global::inputKPMultiply;
KeyState Global::inputKPPlus;
KeyState Global::inputKPMinus;
KeyState Global::inputKP0;
KeyState Global::inputKP1;
KeyState Global::inputKP2;
KeyState Global::inputKP3;
KeyState Global::inputKP4;
KeyState Global::inputKP5;
KeyState Global::inputKP6;
KeyState Global::inputKP7;
KeyState Global::inputKP8;
KeyState Global::inputKP9;
KeyState Global::inputLevel;
KeyState Global::inputStrumDirection;
KeyState Global::inputEject;
KeyState Global::inputMetronome;
KeyState Global::inputSwitchInstrument;
KeyState Global::inputFreezeHighway;
#ifdef SHR3D_RECORDER
KeyState Global::inputRecorder;
#endif // SHR3D_RECORDER
KeyState Global::inputTuner;
KeyState Global::inputSfxChain;
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_LINUX)
KeyState Global::inputWireframe;
#endif // PLATFORM_WINDOWS || PLATFORM_LINUX
KeyState Global::inputDebugInfo;
KeyState Global::inputHideMenu;
KeyState Global::inputNoclip;
KeyState Global::inputQuickRepeater;
KeyState Global::inputQuickRepeaterJumpBegin;
KeyState Global::inputEnvironmentMilk;
KeyState Global::inputDelete;
KeyState Global::inputShift;
KeyState Global::inputCtrl;
KeyState Global::inputAlt;
KeyState Global::inputReturn;
KeyState Global::inputLmb;
i32 Global::inputWheelDelta = 0;
#ifdef SHR3D_OPENXR
#ifdef SHR3D_OPENXR_PCVR
bool Global::xrInitialized = false;
#endif // SHR3D_OPENXR_PCVR
i32 Global::xrResolutionWidth = 2404;
i32 Global::xrResolutionHeight = 2404;
#endif // SHR3D_OPENXR

std::u8string Global::pathSettingsIni = u8"settings.ini";
InstallMode Global::installMode =
#if defined(PLATFORM_EMSCRIPTEN)
InstallMode::continueWithoutSaving;
#elif defined(__ANDROID__)
InstallMode::installed;
#else
InstallMode::showInstaller;
#endif
#ifdef SHR3D_AUDIO_AAUDIO
std::vector<std::string> Global::audioAAudioDevicesInput{ "Default" };
std::vector<std::string> Global::audioAAudioDevicesOutput{ "Default" };
#endif // SHR3D_AUDIO_AAUDIO
#ifdef SHR3D_AUDIO_ASIO
std::vector<std::string> Global::audioAsioDevices{ "" };
i32 Global::audioAsioInputCount;
i32 Global::audioAsioOutputCount;
i32 Global::audioAsioBufferMinSize = 16;
i32 Global::audioAsioBufferMaxSize = Const::audioMaximumPossibleBlockSize;
i32 Global::audioAsioBufferPreferredSize = 128;
i32 Global::audioAsioBufferGranularity = -1;
i32 Global::audioAsioInputLatencyFrames;
i32 Global::audioAsioOutputLatencyFrames;
i32 Global::audioAsioSampleRate = 48000;
i32 Global::audioAsioBlockSize = 128;
#ifdef SHR3D_SFX_CORE_HEXFIN_DIVIDED
#ifdef SHR3D_AUDIO_ASIO_SECOND_DEVICE_FOR_TUNER_DIVIDED
i32 Global::audioAsioSecondDeviceForTunerDividedInputCount;
#endif // SHR3D_AUDIO_ASIO_SECOND_DEVICE_FOR_TUNER_DIVIDED
#endif // SHR3D_SFX_CORE_HEXFIN_DIVIDED
#endif // SHR3D_AUDIO_ASIO
#ifdef SHR3D_AUDIO_JACK
#ifndef SHR3D_AUDIO_JACK_NO_DLOPEN
bool Global::audioJackLibraryLoaded = false;
#endif // SHR3D_AUDIO_JACK_NO_DLOPEN
i32 Global::audioJackBlockSize = 128;
i32 Global::audioJackSampleRate = 48000;
std::vector<std::string> Global::audioJackDevicesInput{ "" };
std::vector<std::string> Global::audioJackDevicesOutput{ "" };
#endif // SHR3D_AUDIO_JACK
#ifdef SHR3D_AUDIO_PIPEWIRE
#ifndef SHR3D_AUDIO_PIPEWIRE_NO_DLOPEN
bool Global::audioPipewireLibraryLoaded = false;
#endif // SHR3D_AUDIO_PIPEWIRE_NO_DLOPEN
i32 Global::audioPipewireBlockSize = 128;
i32 Global::audioPipewireSampleRate = 48000;
std::vector<std::string> Global::audioPipewireDevicesInput{ "" };
std::vector<std::string> Global::audioPipewireDevicesOutput{ "" };
#endif // SHR3D_AUDIO_PIPEWIRE
#ifdef SHR3D_AUDIO_SDL
std::vector<std::string> Global::audioSdlDevicesInput{ "Default" };
std::vector<std::string> Global::audioSdlDevicesOutput{ "Default" };
#endif // SHR3D_AUDIO_SDL
#ifdef SHR3D_AUDIO_SUPERPOWERED
i32 Global::audioSuperpoweredInputCount;
i32 Global::audioSuperpoweredOutputCount;
std::vector<std::u8string> Global::audioSuperpoweredCofiguration;
std::vector<std::u8string> Global::audioSuperpoweredDevicesInput;
std::vector<std::u8string> Global::audioSuperpoweredDevicesOutput;
std::vector<std::u8string> Global::audioSuperpoweredInputPaths;
std::vector<std::u8string> Global::audioSuperpoweredTruPaths;
std::vector<std::u8string> Global::audioSuperpoweredOutputPaths;
i32* Global::audioSuperpoweredInputPathIndex;
std::vector<f32> Global::audioSuperpoweredInputMinVolumes = { 0.0f, 0.0f, 0.0f };
std::vector<f32> Global::audioSuperpoweredInputMaxVolumes = { 0.0f, 0.0f, 0.0f };
i32* Global::audioSuperpoweredOutputPathIndex;
std::vector<f32> Global::audioSuperpoweredOutputMinVolumes = { 0.0f, 0.0f, 0.0f };
std::vector<f32> Global::audioSuperpoweredOutputMaxVolumes = { 0.0f, 0.0f, 0.0f };
#endif // SHR3D_AUDIO_SUPERPOWERED
#ifdef SHR3D_AUDIO_WASAPI
std::vector<std::string> Global::audioWasapiDevicesInput{ "Default" };
std::vector<std::string> Global::audioWasapiDevicesOutput{ "Default" };
//u32 Global::audioWasapiInputLatencyFrames;
//u32 Global::audioWasapiOutputLatencyFrames;
u32 Global::audioWasapiBlockSize = 480;
#endif // SHR3D_AUDIO_WASAPI
std::mutex Global::songInfosMutex;
std::unordered_map<SongIndex, std::u8string> Global::songFilePath;
#ifdef SHR3D_PSARC
std::unordered_map<SongIndex, Psarc::Info> Global::psarcInfos; // unordered_map instead of vector to avoid reallocation of data
#endif // SHR3D_PSARC
#ifdef SHR3D_SHRED
std::unordered_map<SongIndex, Shred::Info> Global::shredInfos;
#endif // SHR3D_SHRED
std::unordered_map<SongIndex, Song::Info> Global::songInfos;
std::unordered_map<SongIndex, GLuint> Global::albumCoverTexture;
#ifdef PLATFORM_EMSCRIPTEN
std::unordered_map<SongIndex, std::u8string> Global::iniUrl;
std::unordered_map<SongIndex, std::u8string> Global::albumCoverUrl;
#endif // PLATFORM_EMSCRIPTEN
std::unordered_map<ArrangementIndex, Song::Track> Global::songTracks;
std::unordered_map<ArrangementIndex, Song::TrackLevelAdjusted> Global::songTrackLevelAdjusted;
std::unordered_map<ArrangementIndex, Level> Global::songLevels;
SongIndex Global::selectedSongIndex = -1;
SongIndex Global::selectedSongIndexForToneWindow = -1;
ArrangementIndex Global::selectedArrangementIndex = 0;
i32 Global::selectedTone = -1;
std::vector<Song::Vocal> Global::songVocals;
f32 Global::oldHighscore = 0.0f;
f32 Global::newHighscore = 0.0f;
f32* Global::musicDoubleBuffer[2];
MusicDoubleBufferStatus Global::musicDoubleBufferStatus = MusicDoubleBufferStatus::inital;
i64 Global::musicBufferLength;
f32* Global::musicPlaybackBufferLR[2];
i64 Global::musicPlaybackLength = 0;
i64 Global::musicPlaybackPosition = I64::max;
TimeNS Global::musicPlaybackSeekerTimeNS = 0;
#ifdef SHR3D_MUSIC_STRETCHER
f32* Global::musicStretcherDoubleBuffer[2];
i64 Global::musicStretcherInputBegin = 0;
TimeNS Global::musicStretcherInputBeginTimeNS = 0;
i64 Global::musicStretcherInputCurrent = 0;
#endif // SHR3D_MUSIC_STRETCHER
TimeNS Global::musicTimeElapsedNS = 0;
f32 Global::musicStretchRatio = 1.0f;
TimeNS Global::quickRepeaterBeginTimeNS = I64::max;
TimeNS Global::quickRepeaterEndTimeNS = I64::max;
#ifdef SHR3D_RECORDER
std::vector<u8> Global::recorderBuffer;
#endif // SHR3D_RECORDER
SfxToneIndex Global::activeSfxToneIndex = 0;
TimeNS Global::sfxToneTime = 0;
i32 Global::sfxToneAutoSwitchOffset = 0;
f32 Global::score = 0.0f;
std::unordered_map<std::u8string, SongStats> Global::songStats;
SfxBankIndex Global::firstEmptyNegativeToneBank = -2;
i32 Global::selectedSongArrangementToneBank = -1;
#ifdef SHR3D_SFX
#ifdef SHR3D_SFX_CORE_EXTENSION_V2
std::vector<SfxCoreExtensionV2Base*> Global::sfxCoreExtensionV2SortedByRegistrationOrder;
std::map<std::u8string, i32> Global::sfxCoreExtensionV2SortedByName;
//i32 Global::sfxCoreExtensionIndexBegin;
#endif // SHR3D_SFX_CORE_EXTENSION_V2
#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER
std::vector<std::u8string> Global::NeuralAmpModeler_IrFiles;
std::vector<std::u8string> Global::NeuralAmpModeler_NamFiles;
#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
#ifdef SHR3D_SFX_PLUGIN_CLAP
//i32 Global::sfxPluginClapIndexBegin;
#endif // SHR3D_SFX_PLUGIN_CLAP
#ifdef SHR3D_SFX_PLUGIN_LV2
//i32 Global::sfxPluginLv2IndexBegin
#endif // SHR3D_SFX_PLUGIN_LV2
#ifdef SHR3D_SFX_PLUGIN_VST
//i32 Global::sfxPluginVstIndexBegin;
#ifdef SHR3D_SFX_PLUGIN_VST_BENCHMARK
std::vector<SfxBenchmarkResult> Global::sfxPluginVstBenchmarkResults;
#endif // SHR3D_SFX_PLUGIN_VST_BENCHMARK
#endif // SHR3D_SFX_PLUGIN_VST
#ifdef SHR3D_SFX_PLUGIN_VST3
//i32 Global::sfxPluginVst3IndexBegin;
#ifdef SHR3D_SFX_PLUGIN_VST3_BENCHMARK
std::vector<SfxBenchmarkResult> Global::sfxPluginVst3BenchmarkResults;
#endif // SHR3D_SFX_PLUGIN_VST3_BENCHMARK
#endif // SHR3D_SFX_PLUGIN_VST3
#ifdef SHR3D_SFX_CORE_HEXFIN
f32 Global::a4ReferenceFrequency = 440.0f;
f32 Global::frequencyMono; // these are read and written by two threads. I guess it is not important to make it threadsafe
#ifdef SHR3D_SFX_CORE_HEXFIN_DIVIDED
f32 Global::frequency[6];
f32 Global::volume[6];
#endif // SHR3D_SFX_CORE_HEXFIN_DIVIDED
#endif // SHR3D_SFX_CORE_HEXFIN
SfxChainEffect Global::effectChain[16];
#ifdef SHR3D_COOP
SfxChainEffect Global::effectChainCoop[ARRAY_SIZE(Global::effectChain)];
#endif // SHR3D_COOP
bool Global::effectChainWindowOpened[ARRAY_SIZE(Global::effectChain)];
#ifdef SHR3D_SFX_PLUGIN
Shr3DWindow Global::effectChainWindowPluginParentWindow[ARRAY_SIZE(Global::effectChain)];
bool Global::effectChainWindowPluginHideUi[ARRAY_SIZE(Global::effectChain)];
vec2 Global::effectChainWindowPluginPosition[ARRAY_SIZE(Global::effectChain)];
#endif // SHR3D_SFX_PLUGIN
//i32 Global::pluginWindowEffectChainPluginIndex;
std::unordered_map<SfxToneIndex, std::u8string> Global::sfxToneNames = { { 0, u8"Default" } };
std::unordered_map<SfxToneIndex, SfxChainEffect[ARRAY_SIZE(Global::effectChain)]> Global::sfxTone;
std::unordered_map<SfxToneIndex, std::u8string[ARRAY_SIZE(Global::effectChain)]> Global::sfxParameters;
std::unordered_map<SfxBankIndex, SongIndex> Global::bankIndex2SongIndex;
SfxId Global::tunerPlugin;
i32 Global::tunerMidiDevice = 0;
SfxId Global::tunerMidiPlugin;
bool Global::midiFromAudioWindowOpen = false;
#endif // SHR3D_SFX
std::mutex Global::playedNotesFromAudioMutex;
std::atomic<i32> Global::playedNotesFromAudioIndex;
MidiMessage Global::playedNotesFromAudio[Const::playedNotesFromAudioMaxCount];
bool Global::uiAboutWindowOpen = false;
#ifdef SHR3D_MIDI
bool Global::uiMidiWindowOpen = false;
i32 Global::midiDeviceCount = 0;
#ifdef PLATFORM_ANDROID_SDL
bool Global::midiConnectedDevices[Const::midiMaxDeviceCount];
#endif // PLATFORM_ANDROID_SDL
#ifdef PLATFORM_WINDOWS
HMIDIIN Global::midiConnectedDevices[Const::midiMaxDeviceCount] = {};
#endif // PLATFORM_WINDOWS
std::u8string Global::midiDeviceNames[Const::midiMaxDeviceCount];
u8 Global::midiLearnNote = 0xFF;
u8 Global::midiNoteBinding[128] = { ARRAY_SET128(0xFF) };
MidiNoteMode Global::midiNoteMode[128];
f32 Global::midiAudioMusicVolumeCoarse = 0.0f;
f32 Global::midiAudioMusicVolumeFine = 0.0f;
f32 Global::midiAudioEffectVolumeCoarse = 0.0f;
f32 Global::midiAudioEffectVolumeFine = 0.0f;
f32 Global::midiAudioEffectVolumeCoopCoarse = 0.0f;
f32 Global::midiAudioEffectVolumeCoopFine = 0.0f;
f32 Global::midiMetronomeVolumeCoarse = 0.0f;
f32 Global::midiMetronomeVolumeFine = 0.0f;
f32 Global::midiHighwayScrollSpeedCoarse = 0.0f;
f32 Global::midiHighwayScrollSpeedFine = 0.0f;
//f32 Global::midiMusicPlaybackPositionCoarse = 0.0f;
//f32 Global::midiMusicPlaybackPositionFine = 0.0f;
#endif // SHR3D_MIDI
#ifdef SHR3D_ENVIRONMENT_MILK
std::vector<std::u8string> Global::milkPresetNames;
i32 Global::milkCurrentPresetIndex = -1;
std::vector<i32> Global::milkActivePresets;
#endif // SHR3D_ENVIRONMENT_MILK
bool Global::skipFetchingCollection = false;
#ifdef PLATFORM_EMSCRIPTEN
SongIndex Global::downloadSongIndexInProgress = -1;
SongIndex Global::downloadSongIndexFinished = -1;
u64 Global::downloadBytesInProgress = 0;
u64 Global::downloadBytesFinished = 0;
std::u8string Global::downloadAutoplayArrangement;
std::u8string Global::autoFetchUrl = u8"urls.txt";
std::unordered_map<std::u8string, File::Type> Global::ipfsFileTypeCache;
#endif // PLATFORM_EMSCRIPTEN
#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER
#ifdef PLATFORM_EMSCRIPTEN
std::unordered_map<std::u8string, std::vector<u8>> Global::namFileCache;
#endif // PLATFORM_EMSCRIPTEN
#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
#ifdef SHR3D_ENVIRONMENT_SKYBOX
std::vector<std::u8string> Global::environmentSkyboxNames;
#endif // SHR3D_ENVIRONMENT_SKYBOX
#ifdef SHR3D_ENVIRONMENT_STAGE
std::vector<std::u8string> Global::environmentStageNames;
std::vector<StageModel> Global::environmentStageModels;
std::vector<f32> Global::environmentStageVertexData;
std::vector<u32> Global::environmentStageIndexData;
std::vector<GLuint> Global::modelTexture;
#endif // SHR3D_ENVIRONMENT_STAGE

#ifdef PLATFORM_QUEST_3
std::vector<f32> Global::graphicsRefreshRates;
#endif // PLATFORM_QUEST_3

f32 Global::inputVolumeMono;
f32 Global::effectVolumeLeft;
f32 Global::effectVolumeRight;
f32 Global::outputVolumeLeft;
f32 Global::outputVolumeRight;

f32 Global::highwayVUMeterEffectPeakVolumeDBLeft = -F32::inf;
TimeNS Global::highwayVUMeterEffectPeakTimeLeft;
f32 Global::highwayVUMeterEffectPeakVolumeDBRight = -F32::inf;
TimeNS Global::highwayVUMeterEffectPeakTimeRight;
f32 Global::highwayVUMeterOutputPeakVolumeDBLeft = -F32::inf;
TimeNS Global::highwayVUMeterOutputPeakTimeLeft;
f32 Global::highwayVUMeterOutputPeakVolumeDBRight = -F32::inf;
TimeNS Global::highwayVUMeterOutputPeakTimeRight;

time_t Global::startupTimestamp = time(nullptr);
TimeNS Global::frameDelta = 0;
TimeNS Global::time_ = 0;

bool Global::inputUseController;
i32 Global::inputCursorPosX = 0;
i32 Global::inputCursorPosY = 0;
#ifdef SHR3D_OPENXR_PCVR
i32 Global::inputCursorPosXrX = 0;
i32 Global::inputCursorPosXrY = 0;
#endif // SHR3D_OPENXR_PCVR
#ifdef SHR3D_OPENXR
XrActiveController Global::inputXRActiveController = XrActiveController::left;
f32 Global::inputXrControllerLeftPosX = 0.0f;
f32 Global::inputXrControllerLeftPosY = 0.0f;
f32 Global::inputXrControllerRightPosX = 0.0f;
f32 Global::inputXrControllerRightPosY = 0.0f;
XrControllerEvent Global::inputXRControllerEvent[2];
mat4 Global::mControllerModel[2];
XrPosef Global::mAimPose[2];
mat4 Global::mAimModel[2];
XrPosef Global::mStagePose;
mat4 Global::mStageModel;
XrActionStatePose Global::mControllerPoseState[2] = { {XR_TYPE_ACTION_STATE_POSE}, {XR_TYPE_ACTION_STATE_POSE} };
#endif // SHR3D_OPENXR
GLuint Global::staticDrawVao = 0;
GLuint Global::dynamicDrawVao = 0;
GLuint Global::stageDrawVao = 0;
GLuint Global::vbo = 0;
GLuint Global::ebo = 0;
GLuint Global::texture = 0;
#ifdef SHR3D_PARTICLE
std::vector<TimeNS> Global::particleDeathTime;
std::vector<Particle_> Global::particle;
#endif // SHR3D_PARTICLE
#ifdef SHR3D_ENVIRONMENT_SKYBOX
GLuint Global::skyboxTexture = 0;
SkyboxTextureType Global::skyboxTextureType = SkyboxTextureType::skysphere360degree;
#endif // SHR3D_ENVIRONMENT_SKYBOX
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_LINUX)
std::u8string Global::clipboardLastPlayedArrangementName = u8"Lead";
#endif // PLATFORM_WINDOWS || PLATFORM_LINUX

#ifndef NDEBUG
f32 Global::debugValue[4] = { 0.0, 0.0, 0.0, 0.0 };
#endif // NDEBUG

i32& blockSize()
{
  switch (Settings::audioSystem)
  {
#ifdef SHR3D_AUDIO_AAUDIO
  case AudioSystem::AAudio:
    return Settings::audioAAudioBlockSize;
#endif // SHR3D_AUDIO_AAUDIO
#ifdef SHR3D_AUDIO_ASIO
  case AudioSystem::ASIO:
    return Global::audioAsioBlockSize;
#endif // SHR3D_AUDIO_ASIO
#ifdef SHR3D_AUDIO_JACK
  case AudioSystem::JACK:
    return Global::audioJackBlockSize;
#endif // SHR3D_AUDIO_JACK
#ifdef SHR3D_AUDIO_PIPEWIRE
  case AudioSystem::PipeWire:
    return Global::audioPipewireBlockSize;
#endif // SHR3D_AUDIO_PIPEWIRE
#ifdef SHR3D_AUDIO_SDL
  case AudioSystem::SDL:
    return Settings::audioSdlBlockSize;
#endif // SHR3D_AUDIO_SDL
#ifdef SHR3D_AUDIO_SUPERPOWERED
  case AudioSystem::Superpowered:
    return Settings::audioSuperpoweredBlockSize;
#endif // SHR3D_AUDIO_SUPERPOWERED
#ifdef SHR3D_AUDIO_WASAPI
  case AudioSystem::WASAPI:
    return (i32&)Global::audioWasapiBlockSize;
#endif // SHR3D_AUDIO_WASAPI
#ifdef SHR3D_AUDIO_WEBAUDIO
    //case AudioSystem::WebAudio:
    // return 128;
#endif // SHR3D_AUDIO_WEBAUDIO 
  default:
    unreachable();
  }
}

i32& sampleRate()
{
  switch (Settings::audioSystem)
  {
#ifdef SHR3D_AUDIO_AAUDIO
  case AudioSystem::AAudio:
    return Settings::audioAAudioSampleRate;
#endif // SHR3D_AUDIO_AAUDIO
#ifdef SHR3D_AUDIO_ASIO
  case AudioSystem::ASIO:
    return Global::audioAsioSampleRate;
#endif // SHR3D_AUDIO_ASIO
#ifdef SHR3D_AUDIO_JACK
  case AudioSystem::JACK:
    return Global::audioJackSampleRate;
#endif // SHR3D_AUDIO_JACK
#ifdef SHR3D_AUDIO_PIPEWIRE
  case AudioSystem::PipeWire:
    return Global::audioPipewireSampleRate;
#endif // SHR3D_AUDIO_PIPEWIRE
#ifdef SHR3D_AUDIO_SDL
  case AudioSystem::SDL:
    return Settings::audioSdlSampleRate;
#endif // SHR3D_AUDIO_SDL
#ifdef SHR3D_AUDIO_SUPERPOWERED
  case AudioSystem::Superpowered:
    static i32 super = 48000; // TODO: fix this
    return super;
#endif // SHR3D_AUDIO_SUPERPOWERED
#ifdef SHR3D_AUDIO_WASAPI
  case AudioSystem::WASAPI:
    return Settings::audioWasapiSampleRate;
#endif // SHR3D_AUDIO_WASAPI
#ifdef SHR3D_AUDIO_WEBAUDIO
  case AudioSystem::WebAudio:
    return Settings::audioWebAudioSampleRate;
#endif // SHR3D_AUDIO_WEBAUDIO 
  default:
    unreachable();
  }
}

//f32 noteFrequency(const MidiNote midiNote)
//{
//  ASSERT(midiNote >= MidiNote(0));
//  ASSERT(midiNote < MidiNote::COUNT);
//
//  static const f32 frequency[] =
//  {
//       8.176f,    8.662f,    9.177f,    9.723f,    10.301f,    10.913f,    11.562f,    12.250f,   12.978f,   13.750f,   14.568f,   15.434f,
//      16.352f,   17.324f,   18.354f,   19.445f,    20.602f,    21.827f,    23.125f,    24.500f,   25.957f,   27.500f,   29.135f,   30.868f,
//      32.703f,   34.648f,   36.708f,   38.891f,    41.203f,    43.654f,    46.249f,    48.999f,   51.913f,   55.000f,   58.270f,   61.735f,
//      65.406f,   69.296f,   73.416f,   77.782f,    82.407f,    87.307f,    92.499f,    97.999f,  103.826f,  110.000f,  116.541f,  123.471f,
//     130.813f,  138.591f,  146.832f,  155.563f,   164.814f,   174.614f,   184.997f,   195.998f,  207.652f,  220.000f,  233.082f,  246.942f,
//     261.626f,  277.183f,  293.665f,  311.127f,   329.628f,   349.228f,   369.994f,   391.995f,  415.305f,  440.000f,  466.164f,  493.883f,
//     523.251f,  554.365f,  587.330f,  622.254f,   659.255f,   698.456f,   739.989f,   783.991f,  830.609f,  880.000f,  932.328f,  987.767f,
//    1046.502f, 1108.731f, 1174.659f, 1244.508f,  1318.510f,  1396.913f,  1479.978f,  1567.982f, 1661.219f, 1760.000f, 1864.655f, 1975.533f,
//    2093.005f, 2217.461f, 2349.318f, 2489.016f,  2637.020f,  2793.826f,  2959.955f,  3135.963f, 3322.438f, 3520.000f, 3729.310f, 3951.066f,
//    4186.009f, 4434.922f, 4698.636f, 4978.032f,  5274.041f,  5587.652f,  5919.911f,  6271.927f, 6644.875f, 7040.000f, 7458.620f, 7902.133f,
//    8372.018f, 8869.844f, 9397.273f, 9956.063f, 10548.080f, 11175.300f, 11839.820f, 12543.850f
//  };
//
//  return frequency[to_underlying_(midiNote)];
//}

float noteFrequency(const MidiNote midiNote, const f32 a4referenceFrequency)
{
  ASSERT(midiNote >= MidiNote(0));
  ASSERT(midiNote < MidiNote::COUNT);

  // Calculate the frequency using the formula
  return a4referenceFrequency * exp2f(f32(to_underlying_(midiNote) - to_underlying_(MidiNote::A_4)) / 12.0f);
}

MidiNote getStringNoteZeroTuningHighway(const Highway::Ctx& ctx, const i32 string)
{
  // .psarc files only support 6 strings.
  // when a song is tuned too low we draw 7 strings
  // in this case the high e-string will get the same tuning as the b-string
  const i32 shredString = max_(0, string - ctx.instrumentStringOffset);
  return getStringNoteZeroTuning(ctx, shredString);
}

MidiNote getStringNoteZeroTuning(const Highway::Ctx& ctx, const i32 string)
{
  ASSERT(string >= 0);

  if (ctx.arrangement != nullptr)
  {
    return MidiNote(
      to_underlying_(ctx.arrangement->isBass ? Const::stringStandardTuningBassZeroNote[string] : Const::stringStandardTuningGuitarZeroNote[string])
      + ctx.arrangement->tuning.string[string]);
  }
  return MidiNote(to_underlying_(Settings::applicationInstrument == Instrument::BassGuitar ? Const::stringStandardTuningBassZeroNote[string] : Const::stringStandardTuningGuitarZeroNote[string]));
}

f32 tunerDBFromVolume(const f32 volume)
{
  return 20.0f * log10f(volume);
}

#ifdef SHR3D_SFX_CORE_HEXFIN
MidiNote tunerNote(const f32 cf)
{
  return MidiNote(round(cf) + f32(to_underlying_(MidiNote::A_4)));
}
f32 tunerReferenceFrequency(const f32 cf, const f32 a4ReferenceFrequency)
{
  return a4ReferenceFrequency * pow(2.0f, round(cf) / 12.0f);
}
f32 tunerCents(const f32 frequency, const f32 a4ReferenceFrequency)
{
  return 1200.0f * std::log2(frequency / a4ReferenceFrequency);
}
#endif // SHR3D_SFX_CORE_HEXFIN

void Global::init()
{
  // on g++ and emscripten having large globals will increase the executeable size by a lot.
  // better allocate them at runtime
  Global::songFilePath.reserve(10000);
  Global::songInfos.reserve(10000);
  Global::particleDeathTime.resize(Const::highwayParticleMaxCount);
  Global::particle.resize(Const::highwayParticleMaxCount);
}
