// SPDX-License-Identifier: Unlicense

#ifndef GLOBAL_H
#define GLOBAL_H

#include "const.h"
#include "helper.h"
#include "highway.h"
#include "hud.h"
#include "psarc.h"
#include "settings.h"
#include "shred.h"
#include "song.h"
#include "type.h"

#include <atomic>
#include <mutex>
#include <queue>
#include <string>
#include <vector>
#include <unordered_map>

#ifdef SHR3D_OPENXR
#include <openxr/openxr.h>
#endif // SHR3D_OPENXR

#ifdef SHR3D_WINDOW_SDL
struct SDL_Window;
typedef void* SDL_GLContext;
typedef struct _SDL_GameController SDL_GameController;
typedef struct SDL_Cursor SDL_Cursor;
#endif // SHR3D_WINDOW_SDL
#ifdef SHR3D_WINDOW_WIN32
typedef struct HWND__* HWND;
typedef struct HDC__* HDC;
typedef struct HGLRC__* HGLRC;
typedef struct tagWINDOWPLACEMENT WINDOWPLACEMENT;
typedef struct tagPIXELFORMATDESCRIPTOR PIXELFORMATDESCRIPTOR;
#endif // SHR3D_WINDOW_WIN32

typedef struct HMIDIIN__* HMIDIIN;

#ifdef __ANDROID__
typedef struct _JavaVM JavaVM;
#endif // __ANDROID__

#ifdef SHR3D_OPENXR
#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
typedef struct XrSpace_T* XrSpace;
#else
typedef uint64_t XrSpace;
#endif
#endif // SHR3D_OPENXR

#ifdef SHR3D_SFX_CORE_EXTENSION_V2
struct SfxCoreExtensionV2Base;
#endif // SHR3D_SFX_CORE_EXTENSION_V2

namespace Global
{
#ifdef SHR3D_WINDOW_SDL
  extern SDL_Window* window;
  extern SDL_GLContext glContext;
  extern SDL_GameController* gameController;
  //#ifdef SHR3D_CUSTOM_CURSOR
  extern SDL_Cursor* defaultCursor;
  //#endif // SHR3D_CUSTOM_CURSOR
#ifdef SHR3D_COOP
  extern u32 coopWindowId;
  extern SDL_Window* coopWindow;
#endif // SHR3D_COOP
#endif // SHR3D_WINDOW_SDL
#ifdef SHR3D_BENCHMARK_FPS
  extern u64 benchmarkScore;
  extern u64 benchmarkScoreLast;
  extern f32 benchmarkAvgFPS;
#endif // SHR3D_BENCHMARK_FPS
#ifdef SHR3D_WINDOW_WIN32
  extern HWND window;
  extern HDC hdc;
  extern HGLRC glContext;
  extern HGLRC pluginGlContext; // some vst plugins misbehave and corrupt our glContext
  extern WINDOWPLACEMENT lastMainWindowPlacement;
  extern PIXELFORMATDESCRIPTOR pixelFormatDescriptor;
  extern int pixelFormat;
#ifdef SHR3D_COOP
  extern HWND coopWindow;
  extern HDC coopHdc;
#endif // SHR3D_COOP
#endif // SHR3D_WINDOW_WIN32
#ifdef SHR3D_COOP
  extern i32 coopResolutionWidth;
  extern i32 coopResolutionHeight;
  extern ArrangementIndex coopSelectedArrangementIndex;
  extern FullscreenMode coopWindowFullscreenMode;
  extern Highway::Ctx coopHighwayCtx;
  extern Hud::Ctx coopHudCtx;
#endif // SHR3D_COOP
#ifdef __ANDROID__
  extern JavaVM* g_JVM;
  extern void* androidActivity;
  extern std::u8string rootPath;
  extern bool androidRecordMicrophonePermissionGranted;
#endif // __ANDROID__
  extern bool appQuit;
  extern i32 resolutionWidth;
  extern i32 resolutionHeight;
  extern Hud::Ctx hudCtx;
  extern Highway::Ctx highwayCtx;
  extern KeyState inputA;
  extern KeyState inputD;
  extern KeyState inputW;
  extern KeyState inputS;
  extern KeyState inputE;
  extern KeyState inputC;
  extern KeyState inputQ;
  extern KeyState inputLeft;
  extern KeyState inputRight;
  extern KeyState inputMute;
#ifdef SHR3D_COOP
  extern KeyState inputCoop;
#endif // SHR3D_COOP
  extern KeyState inputKPDivide;
  extern KeyState inputKPMultiply;
  extern KeyState inputKPPlus;
  extern KeyState inputKPMinus;
  extern KeyState inputKP0;
  extern KeyState inputKP1;
  extern KeyState inputKP2;
  extern KeyState inputKP3;
  extern KeyState inputKP4;
  extern KeyState inputKP5;
  extern KeyState inputKP6;
  extern KeyState inputKP7;
  extern KeyState inputKP8;
  extern KeyState inputKP9;
  extern KeyState inputLevel;
  extern KeyState inputStrumDirection;
  extern KeyState inputEject;
  extern KeyState inputMetronome;
  extern KeyState inputSwitchInstrument;
  extern KeyState inputFreezeHighway;
#ifdef SHR3D_RECORDER
  extern KeyState inputRecorder;
#endif // SHR3D_RECORDER
  extern KeyState inputTuner;
  extern KeyState inputSfxChain;
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_LINUX)
  extern KeyState inputWireframe;
#endif // PLATFORM_WINDOWS || PLATFORM_LINUX
  extern KeyState inputDebugInfo;
  extern KeyState inputHideMenu;
  extern KeyState inputNoclip;
  extern KeyState inputQuickRepeater;
  extern KeyState inputQuickRepeaterJumpBegin;
  extern KeyState inputEnvironmentMilk;
  extern KeyState inputDelete;
  extern KeyState inputShift;
  extern KeyState inputCtrl;
  extern KeyState inputAlt;
  extern KeyState inputReturn;
  extern KeyState inputLmb;
  extern i32 inputWheelDelta;
#ifdef SHR3D_OPENXR
#ifdef SHR3D_OPENXR_PCVR
  extern bool xrInitialized;
#endif // SHR3D_OPENXR_PCVR
  extern i32 xrResolutionWidth;
  extern i32 xrResolutionHeight;
#endif // SHR3D_OPENXR
  extern std::u8string pathSettingsIni;
  extern InstallMode installMode;
#ifdef SHR3D_AUDIO_AAUDIO
  extern std::vector<std::string> audioAAudioDevicesInput;
  extern std::vector<std::string> audioAAudioDevicesOutput;
#endif // SHR3D_AUDIO_AAUDIO
#ifdef SHR3D_AUDIO_ASIO
  extern std::vector<std::string> audioAsioDevices;
  extern i32 audioAsioInputCount;
  extern i32 audioAsioOutputCount;
  extern i32 audioAsioBufferMinSize;
  extern i32 audioAsioBufferMaxSize;
  extern i32 audioAsioBufferPreferredSize;
  extern i32 audioAsioBufferGranularity;
  extern i32 audioAsioInputLatencyFrames;
  extern i32 audioAsioOutputLatencyFrames;
  extern i32 audioAsioSampleRate;
  extern i32 audioAsioBlockSize;
#ifdef SHR3D_SFX_CORE_HEXFIN_DIVIDED
#ifdef SHR3D_AUDIO_ASIO_SECOND_DEVICE_FOR_TUNER_DIVIDED
  extern i32 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
  extern bool audioJackLibraryLoaded;
#endif // SHR3D_AUDIO_JACK_NO_DLOPEN
  extern i32 audioJackBlockSize;
  extern i32 audioJackSampleRate;
  extern std::vector<std::string> audioJackDevicesInput;
  extern std::vector<std::string> audioJackDevicesOutput;
#endif // SHR3D_AUDIO_JACK
#ifdef SHR3D_AUDIO_PIPEWIRE
#ifndef SHR3D_AUDIO_PIPEWIRE_NO_DLOPEN
  extern bool audioPipewireLibraryLoaded;
#endif // SHR3D_AUDIO_PIPEWIRE_NO_DLOPEN
  extern i32 audioPipewireBlockSize;
  extern i32 audioPipewireSampleRate;
  extern std::vector<std::string> audioPipewireDevicesInput;
  extern std::vector<std::string> audioPipewireDevicesOutput;
#endif // SHR3D_AUDIO_PIPEWIRE
#ifdef SHR3D_AUDIO_SDL
  extern std::vector<std::string> audioSdlDevicesInput;
  extern std::vector<std::string> audioSdlDevicesOutput;
#endif // SHR3D_AUDIO_SDL
#ifdef SHR3D_AUDIO_SUPERPOWERED
  extern i32 audioSuperpoweredInputCount;
  extern i32 audioSuperpoweredOutputCount;
  extern std::vector<std::u8string> audioSuperpoweredCofiguration;
  extern std::vector<std::u8string> audioSuperpoweredDevicesInput;
  extern std::vector<std::u8string> audioSuperpoweredDevicesOutput;
  extern std::vector<std::u8string> audioSuperpoweredInputPaths;
  extern std::vector<std::u8string> audioSuperpoweredTruPaths;
  extern std::vector<std::u8string> audioSuperpoweredOutputPaths;
  extern i32* audioSuperpoweredInputPathIndex;
  extern std::vector<f32> audioSuperpoweredInputMinVolumes;
  extern std::vector<f32> audioSuperpoweredInputMaxVolumes;
  extern i32* audioSuperpoweredOutputPathIndex;
  extern std::vector<f32> audioSuperpoweredOutputMinVolumes;
  extern std::vector<f32> audioSuperpoweredOutputMaxVolumes;
#endif // SHR3D_AUDIO_SUPERPOWERED
#ifdef SHR3D_AUDIO_WASAPI
  extern std::vector<std::string> audioWasapiDevicesInput;
  extern std::vector<std::string> audioWasapiDevicesOutput;
  //extern u32 audioWasapiInputLatencyFrames;
  //extern u32 audioWasapiOutputLatencyFrames;
  extern u32 audioWasapiBlockSize;
#endif // SHR3D_AUDIO_WASAPI
  extern std::mutex songInfosMutex;
  extern std::unordered_map<SongIndex, std::u8string> songFilePath;
#ifdef SHR3D_PSARC
  extern std::unordered_map<SongIndex, Psarc::Info> psarcInfos;
#endif // SHR3D_PSARC
#ifdef SHR3D_SHRED
  extern std::unordered_map<SongIndex, Shred::Info> shredInfos;
#endif // SHR3D_SHRED
  extern std::unordered_map<SongIndex, Song::Info> songInfos;
  extern std::unordered_map<SongIndex, GLuint> albumCoverTexture;
#ifdef PLATFORM_EMSCRIPTEN
  extern std::unordered_map<SongIndex, std::u8string> iniUrl;
  extern std::unordered_map<SongIndex, std::u8string> albumCoverUrl;
#endif // PLATFORM_EMSCRIPTEN
  extern std::unordered_map<ArrangementIndex, Song::Track> songTracks;
  extern std::unordered_map<ArrangementIndex, Song::TrackLevelAdjusted> songTrackLevelAdjusted;
  extern std::unordered_map<ArrangementIndex, Level> songLevels;
  extern SongIndex selectedSongIndex;
  extern SongIndex selectedSongIndexForToneWindow;
  extern ArrangementIndex selectedArrangementIndex;
  extern i32 selectedTone;
  extern std::vector<Song::Vocal> songVocals;
  extern f32 oldHighscore;
  extern f32 newHighscore;
  extern f32* musicDoubleBuffer[2];
  extern MusicDoubleBufferStatus musicDoubleBufferStatus;
  extern i64 musicBufferLength;
  extern f32* musicPlaybackBufferLR[2];
  extern i64 musicPlaybackLength;
  extern i64 musicPlaybackPosition;
  extern TimeNS musicPlaybackSeekerTimeNS;
#ifdef SHR3D_MUSIC_STRETCHER
  extern f32* musicStretcherDoubleBuffer[2];
  extern i64 musicStretcherInputBegin;
  extern TimeNS musicStretcherInputBeginTimeNS;
  extern i64 musicStretcherInputCurrent;
#endif // SHR3D_MUSIC_STRETCHER
  extern TimeNS musicTimeElapsedNS;
  extern f32 musicStretchRatio;
  extern TimeNS quickRepeaterBeginTimeNS;
  extern TimeNS quickRepeaterEndTimeNS;
#ifdef SHR3D_RECORDER
  extern std::vector<u8> recorderBuffer;
#endif // SHR3D_RECORDER
  extern SfxToneIndex activeSfxToneIndex;
  extern TimeNS sfxToneTime;
  extern i32 sfxToneAutoSwitchOffset;
  extern f32 score;
  extern std::unordered_map<std::u8string, SongStats> songStats;
  extern SfxBankIndex firstEmptyNegativeToneBank;
  extern i32 selectedSongArrangementToneBank;
#ifdef SHR3D_SFX
#ifdef SHR3D_SFX_CORE_EXTENSION_V2
  extern std::vector<SfxCoreExtensionV2Base*> sfxCoreExtensionV2SortedByRegistrationOrder;
  extern std::map<std::u8string, i32> sfxCoreExtensionV2SortedByName;
  //extern i32 sfxCoreExtensionIndexBegin;
#endif // SHR3D_SFX_CORE_EXTENSION_V2
#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER
  extern std::vector<std::u8string> NeuralAmpModeler_IrFiles;
  extern std::vector<std::u8string> NeuralAmpModeler_NamFiles;
#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
#ifdef SHR3D_SFX_PLUGIN_CLAP
  //extern i32 sfxPluginClapIndexBegin;
#endif // SHR3D_SFX_PLUGIN_CLAP
#ifdef SHR3D_SFX_PLUGIN_LV2
  //extern i32 sfxPluginLv2IndexBegin;
#endif // SHR3D_SFX_PLUGIN_LV2
#ifdef SHR3D_SFX_PLUGIN_VST
  //extern i32 sfxPluginVstIndexBegin;
#ifdef SHR3D_SFX_PLUGIN_VST_BENCHMARK
  extern std::vector<SfxBenchmarkResult> sfxPluginVstBenchmarkResults;
#endif // SHR3D_SFX_PLUGIN_VST_BENCHMARK
#endif // SHR3D_SFX_PLUGIN_VST
#ifdef SHR3D_SFX_PLUGIN_VST3
  //extern i32 sfxPluginVst3IndexBegin;
#ifdef SHR3D_SFX_PLUGIN_VST3_BENCHMARK
  extern std::vector<SfxBenchmarkResult> sfxPluginVst3BenchmarkResults;
#endif // SHR3D_SFX_PLUGIN_VST_BENCHMARK
#endif // SHR3D_SFX_PLUGIN_VST3
#ifdef SHR3D_SFX_CORE_HEXFIN
  extern f32 a4ReferenceFrequency;
  extern f32 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
  extern f32 frequency[6];
  extern f32 volume[6];
#endif // SHR3D_SFX_CORE_HEXFIN_DIVIDED
#endif // SHR3D_SFX_CORE_HEXFIN
  extern SfxChainEffect effectChain[16];
#ifdef SHR3D_COOP
  extern SfxChainEffect effectChainCoop[ARRAY_SIZE(Global::effectChain)];
#endif // SHR3D_COOP
  extern bool effectChainWindowOpened[ARRAY_SIZE(Global::effectChain)];
#ifdef SHR3D_SFX_PLUGIN
  extern Shr3DWindow effectChainWindowPluginParentWindow[ARRAY_SIZE(Global::effectChain)];
  extern bool effectChainWindowPluginHideUi[ARRAY_SIZE(Global::effectChain)];
  extern vec2 effectChainWindowPluginPosition[ARRAY_SIZE(Global::effectChain)];
#endif // SHR3D_SFX_PLUGIN
  //extern i32 pluginWindowEffectChainPluginIndex;
  extern std::unordered_map<SfxToneIndex, std::u8string> sfxToneNames;
  extern std::unordered_map<SfxToneIndex, SfxChainEffect[ARRAY_SIZE(Global::effectChain)]> sfxTone;
  extern std::unordered_map<SfxToneIndex, std::u8string[ARRAY_SIZE(Global::effectChain)]> sfxParameters;
  extern std::unordered_map<SfxBankIndex, SongIndex> bankIndex2SongIndex;
  extern SfxId tunerPlugin;
  extern i32 tunerMidiDevice;
  extern SfxId tunerMidiPlugin;
  extern bool midiFromAudioWindowOpen;
#endif // SHR3D_SFX
  extern std::mutex playedNotesFromAudioMutex;
  extern std::atomic<i32> playedNotesFromAudioIndex;
  extern MidiMessage playedNotesFromAudio[Const::playedNotesFromAudioMaxCount];
  extern bool uiAboutWindowOpen;
#ifdef SHR3D_MIDI
  extern bool uiMidiWindowOpen;
  extern i32 midiDeviceCount;
#ifdef PLATFORM_ANDROID_SDL
  extern bool midiConnectedDevices[Const::midiMaxDeviceCount];
#endif // PLATFORM_ANDROID_SDL
#ifdef PLATFORM_WINDOWS
  extern HMIDIIN midiConnectedDevices[Const::midiMaxDeviceCount];
#endif // PLATFORM_WINDOWS
  extern std::u8string midiDeviceNames[Const::midiMaxDeviceCount];
  extern u8 midiLearnNote;
  extern u8 midiNoteBinding[128];
  extern MidiNoteMode midiNoteMode[128];
  extern f32 midiAudioMusicVolumeCoarse;
  extern f32 midiAudioMusicVolumeFine;
  extern f32 midiAudioEffectVolumeCoarse;
  extern f32 midiAudioEffectVolumeFine;
  extern f32 midiAudioEffectVolumeCoopCoarse;
  extern f32 midiAudioEffectVolumeCoopFine;
  extern f32 midiMetronomeVolumeCoarse;
  extern f32 midiMetronomeVolumeFine;
  extern f32 midiHighwayScrollSpeedCoarse;
  extern f32 midiHighwayScrollSpeedFine;
  //extern f32 midiMusicPlaybackPositionCoarse;
  //extern f32 midiMusicPlaybackPositionFine;
#endif // SHR3D_MIDI
#ifdef SHR3D_ENVIRONMENT_MILK
  extern std::vector<std::u8string> milkPresetNames;
  extern i32 milkCurrentPresetIndex;
  extern std::vector<i32> milkActivePresets;
#endif // SHR3D_ENVIRONMENT_MILK
  extern bool skipFetchingCollection;
#ifdef PLATFORM_EMSCRIPTEN
  extern std::u8string autoFetchUrl;
  extern SongIndex downloadSongIndexInProgress;
  extern SongIndex downloadSongIndexFinished;
  extern u64 downloadBytesInProgress;
  extern u64 downloadBytesFinished;
  extern std::u8string downloadAutoplayArrangement;
  extern std::unordered_map<std::u8string, File::Type> ipfsFileTypeCache;
#endif // PLATFORM_EMSCRIPTEN
#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER
#if defined PLATFORM_EMSCRIPTEN
  extern std::unordered_map<std::u8string, std::vector<u8>> namFileCache; // hack to work around file read in the webbrowser
#endif // PLATFORM_EMSCRIPTEN
#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
#ifdef SHR3D_ENVIRONMENT_SKYBOX
  extern std::vector<std::u8string> environmentSkyboxNames;
#endif // SHR3D_ENVIRONMENT_SKYBOX
#ifdef SHR3D_ENVIRONMENT_STAGE
  extern std::vector<std::u8string> environmentStageNames;
  extern std::vector<StageModel> environmentStageModels;
  extern std::vector<f32> environmentStageVertexData;
  extern std::vector<u32> environmentStageIndexData;
  extern std::vector<GLuint> modelTexture;
#endif // SHR3D_ENVIRONMENT_STAGE

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

  extern f32 inputVolumeMono;
  extern f32 effectVolumeLeft;
  extern f32 effectVolumeRight;
  extern f32 outputVolumeLeft;
  extern f32 outputVolumeRight;

  extern f32 highwayVUMeterEffectPeakVolumeDBLeft;
  extern TimeNS highwayVUMeterEffectPeakTimeLeft;
  extern f32 highwayVUMeterEffectPeakVolumeDBRight;
  extern TimeNS highwayVUMeterEffectPeakTimeRight;
  extern f32 highwayVUMeterOutputPeakVolumeDBLeft;
  extern TimeNS highwayVUMeterOutputPeakTimeLeft;
  extern f32 highwayVUMeterOutputPeakVolumeDBRight;
  extern TimeNS highwayVUMeterOutputPeakTimeRight;

  extern time_t startupTimestamp;
  extern TimeNS frameDelta;
  extern TimeNS time_;

  extern bool inputUseController;
  extern i32 inputCursorPosX;
  extern i32 inputCursorPosY;
#ifdef SHR3D_OPENXR_PCVR
  extern i32 inputCursorPosXrX; // For XR. Independent from win32 window rect.
  extern i32 inputCursorPosXrY;
#endif // SHR3D_OPENXR_PCVR
#ifdef SHR3D_OPENXR
  extern XrActiveController inputXRActiveController; // 0 left, 1 right, 2 mouse&keyboard
  extern f32 inputXrControllerLeftPosX;
  extern f32 inputXrControllerLeftPosY;
  extern f32 inputXrControllerRightPosX;
  extern f32 inputXrControllerRightPosY;
  extern XrControllerEvent inputXRControllerEvent[2];
  extern mat4 mControllerModel[2];
  extern XrPosef mAimPose[2];
  extern mat4 mAimModel[2];
  extern XrPosef mStagePose;
  extern mat4 mStageModel;
  extern XrActionStatePose mControllerPoseState[2];
#endif // SHR3D_OPENXR
  extern GLuint staticDrawVao; // for drawing static things like note rects
  extern GLuint dynamicDrawVao; // for drawing custom geometry like slides
  extern GLuint stageDrawVao; // for drawing the 3d environment
  extern GLuint vbo; // default vbo
  extern GLuint ebo;
  extern GLuint texture;
#ifdef SHR3D_PARTICLE
  extern std::vector<TimeNS> particleDeathTime;
  extern std::vector<Particle_> particle;
#endif // SHR3D_PARTICLE
#ifdef SHR3D_ENVIRONMENT_SKYBOX
  extern GLuint skyboxTexture;
  extern SkyboxTextureType skyboxTextureType;
#endif // SHR3D_ENVIRONMENT_SKYBOX
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_LINUX)
  extern std::u8string clipboardLastPlayedArrangementName;
#endif // PLATFORM_WINDOWS || PLATFORM_LINUX

#ifndef NDEBUG
  extern f32 debugValue[4];
#endif // NDEBUG
}

i32& blockSize();
i32& sampleRate();
//f32 noteFrequency(MidiNote midiNote);
f32 noteFrequency(MidiNote midiNote, f32 a4referenceFrequency);
MidiNote getStringNoteZeroTuningHighway(const Highway::Ctx& ctx, const i32 string);
MidiNote getStringNoteZeroTuning(const Highway::Ctx& ctx, const i32 string);

f32 tunerDBFromVolume(f32 volume);
#ifdef SHR3D_SFX_CORE_HEXFIN
MidiNote tunerNote(f32 cf);
f32 tunerReferenceFrequency(f32 cf, f32 a4ReferenceFrequency);
f32 tunerCents(f32 frequency, f32 a4ReferenceFrequency);
#endif // SHR3D_SFX_CORE_HEXFIN

namespace Global
{
  void init();
}

#endif // GLOBAL_H
