// SPDX-License-Identifier: Unlicense

#include "sfx.h"

#ifdef SHR3D_SFX

#include "base64.h"
#include "global.h"
#include "string_.h"
#include <string.h>

#ifdef SHR3D_SFX_CORE
#include "sfxCore.h"
#endif // SHR3D_SFX_CORE

#ifdef SHR3D_SFX_CORE_EXTENSION_V2
#include "sfxCore/sfxCoreExtensionV2/api.h"
#endif // SHR3D_SFX_CORE_EXTENSION_V2

#ifdef SHR3D_SFX_PLUGIN_CLAP
#include "sfxPluginClap.h"
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_VST
#include "sfxPluginVst.h"
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
#include "sfxPluginVst3.h"
#endif // SHR3D_SFX_PLUGIN_VST3

std::map<SfxSystem, std::vector<std::u8string>> Sfx::names{
#ifdef SHR3D_SFX_CORE
  {
    SfxSystem::core,
    {
#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER
      u8"Neural Amp Modeler",
#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
#ifdef SHR3D_SFX_CORE_HEXFIN
      u8"Hexfin",
#endif // SHR3D_SFX_CORE_HEXFIN
    }
  },
#ifdef SHR3D_SFX_CORE_AIRWINDOWS
  {
    SfxSystem::coreAirWindows,
    {
      u8"Acceleration",
      u8"Acceleration2",
      u8"ADClip7",
      u8"ADClip8",
      u8"ADT",
      u8"Air",
      u8"Air2",
      u8"Air3",
      u8"Apicolypse",
      u8"AQuickVoiceClip",
      u8"AtmosphereBuss",
      u8"AtmosphereChannel",
      u8"Aura",
      u8"AutoPan",
      u8"Average",
      u8"AverMatrix",
      u8"Balanced",
      u8"BassAmp",
      u8"BassDrive",
      u8"BassKit",
      u8"Baxandall",
      u8"Baxandall2",
      u8"Beam",
      u8"BigAmp",
      u8"Biquad",
      u8"Biquad2",
      u8"BiquadDouble",
      u8"BiquadHiLo",
      u8"BiquadNonLin",
      u8"BiquadOneHalf",
      u8"BiquadPlus",
      u8"BiquadStack",
      u8"BiquadTriple",
      u8"Bite",
      u8"BitGlitter",
      u8"BitShiftGain",
      u8"BitShiftPan",
      u8"BlockParty",
      u8"BrassRider",
      u8"BrightAmbience",
      u8"BrightAmbience2",
      u8"BrightAmbience3",
      u8"BuildATPDF",
      u8"BussColors4",
      u8"ButterComp",
      u8"ButterComp2",
      u8"C5RawBuss",
      u8"C5RawChannel",
      u8"Cabs",
      u8"Calibre",
      u8"Capacitor",
      u8"Capacitor2",
      u8"Chamber",
      u8"Chamber2",
      u8"Channel4",
      u8"Channel5",
      u8"Channel6",
      u8"Channel7",
      u8"Channel8",
      u8"Channel9",
      u8"Chorus",
      u8"ChorusEnsemble",
      u8"ChromeOxide",
      u8"Cider",
      u8"ClearCoat",
      u8"ClipOnly2",
      u8"ClipSoftly",
      u8"CloudCoat",
      u8"Coils",
      u8"Coils2",
      u8"Cojones",
      u8"Compresaturator",
      u8"Console0Buss",
      u8"Console0Channel",
      u8"Console4Buss",
      u8"Console4Channel",
      u8"Console5Buss",
      u8"Console5Channel",
      u8"Console5DarkCh",
      u8"Console6Buss",
      u8"Console6Channel",
      u8"Console7Buss",
      u8"Console7Cascade",
      u8"Console7Channel",
      u8"Console7Crunch",
      u8"Console8BussHype",
      u8"Console8BussIn",
      u8"Console8BussOut",
      u8"Console8ChannelHype",
      u8"Console8ChannelIn",
      u8"Console8ChannelOut",
      u8"Console8LiteBuss",
      u8"Console8LiteChannel",
      u8"Console8SubHype",
      u8"Console8SubIn",
      u8"Console8SubOut",
      u8"Console9Buss",
      u8"Console9Channel",
      u8"ConsoleLABuss",
      u8"ConsoleLAChannel",
      u8"ConsoleMCBuss",
      u8"ConsoleMCChannel",
      u8"ConsoleMDBuss",
      u8"ConsoleMDChannel",
      u8"ConsoleXBuss",
      u8"ConsoleXChannel",
      u8"ContentHideD",
      u8"CreamCoat",
      u8"Creature",
      u8"CrickBass",
      u8"CrunchCoat",
      u8"CrunchyGrooveWear",
      u8"Crystal",
      u8"CStrip",
      u8"CStrip2",
      u8"curve",
      u8"Dark",
      u8"DarkNoise",
      u8"DCVoltage",
      u8"DeBess",
      u8"Deckwrecka",
      u8"DeEss",
      u8"DeHiss",
      u8"Density",
      u8"Density2",
      u8"DeRez",
      u8"DeRez2",
      u8"DeRez3",
      u8"Desk",
      u8"Desk4",
      u8"DigitalBlack",
      u8"Dirt",
      u8"Discontinuity",
      u8"Distance",
      u8"Distance2",
      u8"Distance3",
      u8"Distortion",
      u8"Ditherbox",
      u8"DitherFloat",
      u8"DitherMeDiskers",
      u8"DitherMeTimbers",
      u8"Doublelay",
      u8"DoublePaul",
      u8"Drive",
      u8"DrumSlam",
      u8"DubCenter",
      u8"Dubly",
      u8"DubSub",
      u8"DustBunny",
      u8"Dynamics",
      u8"Dyno",
      u8"Edge",
      u8"EdIsDim",
      u8"Elation",
      u8"ElectroHat",
      u8"Energy",
      u8"Energy2",
      u8"Ensemble",
      u8"EQ",
      u8"EveryConsole",
      u8"EverySlew",
      u8"EveryTrim",
      u8"Exciter",
      u8"Facet",
      u8"FathomFive",
      u8"FinalClip",
      u8"FireAmp",
      u8"Flipity",
      u8"Floor",
      u8"Flutter",
      u8"Focus",
      u8"Fracture",
      u8"Fracture2",
      u8"FromTape",
      u8"Galactic",
      u8"Galactic2",
      u8"GalacticVibe",
      u8"Gatelope",
      u8"GlitchShifter",
      u8"GoldenSlew",
      u8"Golem",
      u8"GrindAmp",
      u8"Gringer",
      u8"GrooveWear",
      u8"GuitarConditioner",
      u8"HardVacuum",
      u8"Hermepass",
      u8"HermeTrim",
      u8"HighGlossDither",
      u8"HighImpact",
      u8"Highpass",
      u8"Highpass2",
      u8"Holt",
      u8"Holt2",
      u8"Hombre",
      u8"Huge",
      u8"Hull",
      u8"Hull2",
      u8"Hype",
      u8"Hypersonic",
      u8"HypersonX",
      u8"Infinity",
      u8"Infinity2",
      u8"Inflamer",
      u8"Infrasonic",
      u8"Interstage",
      u8"IronOxide5",
      u8"IronOxideClassic",
      u8"IronOxideClassic2",
      u8"Isolator",
      u8"Isolator2",
      u8"Kalman",
      u8"kCathedral",
      u8"kCathedral2",
      u8"kCathedral3",
      u8"kChamberAR",
      u8"kGuitarHall",
      u8"kPlate140",
      u8"kPlate240",
      u8"kPlateA",
      u8"kPlateB",
      u8"kPlateC",
      u8"kPlateD",
      u8"LeadAmp",
      u8"LeftoMono",
      u8"LilAmp",
      u8"Logical4",
      u8"Loud",
      u8"Lowpass",
      u8"Lowpass2",
      u8"LRFlipTimer",
      u8"Luxor",
      u8"MackEQ",
      u8"Mackity",
      u8"MatrixVerb",
      u8"Melt",
      u8"MidAmp",
      u8"MidSide",
      u8"Mojo",
      u8"Monitoring",
      u8"Monitoring2",
      u8"Monitoring3",
      u8"MoNoam",
      u8"MSFlipTimer",
      u8"MultiBandDistortion",
      u8"MV",
      u8"MV2",
      u8"NaturalizeDither",
      u8"NCSeventeen",
      u8"Neverland",
      u8"Nikola",
      u8"NodeDither",
      u8"Noise",
      u8"NonlinearSpace",
      u8"NotJustAnotherCD",
      u8"NotJustAnotherDither",
      u8"OneCornerClip",
      u8"OrbitKick",
      u8"Overheads",
      u8"Pafnuty",
      u8"Pafnuty2",
      u8"Parametric",
      u8"PaulDither",
      u8"PaulWide",
      u8"PDBuss",
      u8"PDChannel",
      u8"PeaksOnly",
      u8"Pear",
      u8"Pear2",
      u8"PhaseNudge",
      u8"PitchDelay",
      u8"PitchNasty",
      u8"PlatinumSlew",
      u8"PocketVerbs",
      u8"Pockey",
      u8"Pockey2",
      u8"Podcast",
      u8"PodcastDeluxe",
      u8"Point",
      u8"Pop",
      u8"Pop2",
      u8"Pop3",
      u8"PowerSag",
      u8"PowerSag2",
      u8"Precious",
      u8"Preponderant",
      u8"Pressure4",
      u8"Pressure5",
      u8"PurestAir",
      u8"PurestConsole2Buss",
      u8"PurestConsole2Channel",
      u8"PurestConsole3Buss",
      u8"PurestConsole3Channel",
      u8"PurestConsoleBuss",
      u8"PurestConsoleChannel",
      u8"PurestDrive",
      u8"PurestEcho",
      u8"PurestFade",
      u8"PurestGain",
      u8"PurestSquish",
      u8"PurestWarm",
      u8"PurestWarm2",
      u8"Pyewacket",
      u8"RawGlitters",
      u8"RawTimbers",
      u8"Recurve",
      u8"Remap",
      u8"ResEQ",
      u8"ResEQ2",
      u8"Reverb",
      u8"Righteous4",
      u8"RightoMono",
      u8"SampleDelay",
      u8"Shape",
      u8"ShortBuss",
      u8"SideDull",
      u8"Sidepass",
      u8"Silhouette",
      u8"Sinew",
      u8"SingleEndedTriode",
      u8"Slew",
      u8"Slew2",
      u8"Slew3",
      u8"SlewOnly",
      u8"SlewSonic",
      u8"Smooth",
      u8"SoftGate",
      u8"SpatializeDither",
      u8"Spiral",
      u8"Spiral2",
      u8"Srsly",
      u8"Srsly2",
      u8"Srsly3",
      u8"StarChild",
      u8"StarChild2",
      u8"StereoChorus",
      u8"StereoDoubler",
      u8"StereoEnsemble",
      u8"StereoFX",
      u8"Stonefire",
      u8"StoneFireComp",
      u8"StudioTan",
      u8"SubsOnly",
      u8"SubTight",
      u8"Surge",
      u8"SurgeTide",
      u8"Sweeten",
      u8"Swell",
      u8"Tape",
      u8"TapeDelay",
      u8"TapeDelay2",
      u8"TapeDither",
      u8"TapeDust",
      u8"TapeFat",
      u8"Texturize",
      u8"TexturizeMS",
      u8"Thunder",
      u8"ToneSlant",
      u8"ToTape5",
      u8"ToTape6",
      u8"ToVinyl4",
      u8"TPDFDither",
      u8"TPDFWide",
      u8"TransDesk",
      u8"Tremolo",
      u8"TremoSquare",
      u8"Trianglizer",
      u8"TripleSpread",
      u8"Tube",
      u8"Tube2",
      u8"TubeDesk",
      u8"uLawDecode",
      u8"uLawEncode",
      u8"Ultrasonic",
      u8"UltrasonicLite",
      u8"UltrasonicMed",
      u8"UltrasonX",
      u8"UnBox",
      u8"VariMu",
      u8"Verbity",
      u8"Verbity2",
      u8"Vibrato",
      u8"VinylDither",
      u8"VoiceOfTheStarship",
      u8"VoiceTrick",
      u8"Weight",
      u8"Wider",
      u8"Wolfbot",
      u8"XBandpass",
      u8"XHighpass",
      u8"XLowpass",
      u8"XNotch",
      u8"XRegion",
      u8"YBandpass",
      u8"YHighpass",
      u8"YLowpass",
      u8"YNotBandpass",
      u8"YNotch",
      u8"YNotHighpass",
      u8"YNotLowpass",
      u8"YNotNotch",
      u8"ZBandpass",
      u8"ZBandpass2",
      u8"ZHighpass",
      u8"ZHighpass2",
      u8"ZLowpass",
      u8"ZLowpass2",
      u8"ZNotch",
      u8"ZNotch2",
      u8"ZOutputStage",
      u8"ZRegion",
      u8"ZRegion2",

    }
  },
#endif // SHR3D_SFX_CORE_AIRWINDOWS
#ifdef SHR3D_SFX_CORE_RAKARRACK
  {
    SfxSystem::coreRakarrack,
    {

      u8"Reverb", // works
      u8"Echo", // works
      u8"Chorus", // works
      u8"Flanger", // works
      u8"Phaser", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Overdrive", // fails
      u8"Distorsion", // fails
#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"EQ", // fails
      u8"Parametric EQ", // fails
      u8"Cabinet", // fails
      u8"Compressor", // not sure
      u8"WahWah", // works
      u8"AlienWah", // works
      u8"Pan", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
#if 0
      u8"Harmonizer", // fails
#endif
#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Musical Delay", // works
      u8"NoiseGate", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Derelict", // fails
#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Analog Phaser", // works
      u8"Valve", // works
      u8"DualFlange", // fails
      u8"Ring", // fails
      u8"Exciter", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"DistBand", // fails
#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Arpie", // works
      u8"Expander", // works
      u8"Shuffle", // works
      u8"Synthfilter", // works
      u8"VaryBand", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_CONVOLOTRON
      u8"Convolotron", // works
#endif // SHR3D_SFX_CORE_RAKARRACK_CONVOLOTRON
      u8"MuTroMojo", // works
      u8"Echoverse", // works
      u8"Coil Crafter", // works
      u8"Shelf Boost", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Vocoder", // fails
#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Sustainer", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Sequence", // fails
      u8"Shifter", // fails
      u8"StompBox", // fails
      u8"Reverbtron", // fails
#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"Echotron", // works
#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"StereoHarm", // fails
#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE
      u8"CompBand", // fails
      u8"OpticalTrem", // works
      u8"Vibe", // works
    }
  }
#endif // SHR3D_SFX_CORE_RAKARRACK
#endif // SHR3D_SFX_CORE
};

SfxSystem Sfx::name2SfxSystem(const char8_t* name)
{
#ifdef SHR3D_SFX_CORE
  if (strcmp(reinterpret_cast<const char*>(name), "Core") == 0)
    return SfxSystem::core;
#endif // SHR3D_SFX_CORE

#ifdef SHR3D_SFX_CORE_AIRWINDOWS
  if (strcmp(reinterpret_cast<const char*>(name), "CoreAirWindows") == 0)
    return SfxSystem::coreAirWindows;
#endif // SHR3D_SFX_CORE_AIRWINDOWS

#ifdef SHR3D_SFX_CORE_RAKARRACK
  if (strcmp(reinterpret_cast<const char*>(name), "CoreRakarrack") == 0)
    return SfxSystem::coreRakarrack;
#endif // SHR3D_SFX_CORE_RAKARRACK

#ifdef SHR3D_SFX_CORE_EXTENSION_V2
  if (strcmp(reinterpret_cast<const char*>(name), "ExtensionV2") == 0)
    return SfxSystem::coreExtensionV2;
#endif // SHR3D_SFX_CORE_EXTENSION_V2

#ifdef SHR3D_SFX_PLUGIN_CLAP
  if (strcmp(reinterpret_cast<const char*>(name), "CLAP") == 0)
    return SfxSystem::clap;
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  if (strcmp(reinterpret_cast<const char*>(name), "LV2") == 0)
    return SfxSystem::lv2;
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  if (strcmp(reinterpret_cast<const char*>(name), "VST") == 0)
    return SfxSystem::vst;
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  if (strcmp(reinterpret_cast<const char*>(name), "VST3") == 0)
    return SfxSystem::vst3;
#endif // SHR3D_SFX_PLUGIN_VST3

  unreachable();
}

const char8_t* Sfx::sfxSystem2Name(SfxSystem sfxSystem)
{
  switch (sfxSystem)
  {
#ifdef SHR3D_SFX_CORE
  case SfxSystem::core:
    return u8"Core";
#endif // SHR3D_SFX_CORE

#ifdef SHR3D_SFX_CORE_AIRWINDOWS
  case SfxSystem::coreAirWindows:
    return u8"CoreAirWindows";
#endif // SHR3D_SFX_CORE_AIRWINDOWS

#ifdef SHR3D_SFX_CORE_RAKARRACK
  case SfxSystem::coreRakarrack:
    return u8"CoreRakarrack";
#endif // SHR3D_SFX_CORE_RAKARRACK

#ifdef SHR3D_SFX_CORE_EXTENSION_V2
  case SfxSystem::coreExtensionV2:
    return u8"ExtensionV2";
#endif // SHR3D_SFX_CORE_EXTENSION_V2

#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return u8"CLAP";
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return u8"LV2";
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return u8"VST";
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return u8"VST3";
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

static SfxId sfxIdFromSettings(const std::u8string& settingsValue)
{
  SfxId sfxId{};

  const std::vector<std::u8string> sfxSystemNameSplit = String::split(settingsValue, u8',');
  if (sfxSystemNameSplit.size() == 2)
  {
    const SfxSystem sfxSystem = Sfx::name2SfxSystem(sfxSystemNameSplit[0].c_str());

    for (SfxIndex i = 0; i < Sfx::names[sfxSystem].size(); ++i)
    {
      if (sfxSystemNameSplit[1] == Sfx::names[sfxSystem][i])
      {
        sfxId.system = sfxSystem;
        sfxId.sfxIndex = i;
        break;
      }
    }
  }

  return sfxId;
}

void Sfx::init()
{
#ifdef SHR3D_SFX_CORE
  SFXCore::init();
#endif // SHR3D_SFX_CORE

#ifdef SHR3D_SFX_PLUGIN_CLAP
  SfxPluginClap::init(Sfx::names[SfxSystem::clap]);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  SfxPluginLv2::init(Sfx::names);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  SfxPluginVst::init(Sfx::names[SfxSystem::vst]);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  SfxPluginVst3::init(Sfx::names[SfxSystem::vst3]);
#endif // SHR3D_SFX_PLUGIN_VST3

  Global::tunerPlugin = sfxIdFromSettings(Settings::tunerPlugin);
  Global::tunerMidiPlugin = sfxIdFromSettings(Settings::tunerMidiPlugin);

  //  for (const auto& [sfxSystem, sfxNames] : Sfx::names)
  //  {
  //    for (SfxIndex i = 0; i < i32(names.size()); ++i)
  //    {
  //#ifdef SHR3D_SFX_PLUGIN_VST
  //      if (sfxSystem == SfxSystem::vst && tunerMidiPluginName == sfxNames[i]) // Midi From Audio Plugin
  //      {
  //        const char8_t* parametersBase64 = reinterpret_cast<const char8_t*>(&Settings::tunerMidiPlugin.c_str()[tunerMidiPluginNameSplitPos + 1]);
  //        const u64 parametersBase64Length = Settings::tunerMidiPlugin.size() - tunerMidiPluginNameSplitPos - 1;
  //        const std::vector<u8> decodedData = Base64::decode(parametersBase64, parametersBase64Length);
  //        const std::u8string decodedParameters(reinterpret_cast<const char8_t*>(decodedData.data()), decodedData.size());
  //        loadParameters({ sfxSystem, i }, 0, decodedParameters);
  //        SfxPluginVst::setMidiFromAudioCallback(i, 0, true);
  //        //Global::tunerMidiPlugin.index = i;
  //      }
  //#endif // SHR3D_SFX_PLUGIN_VST
  //    }
  //  }
}

#ifdef SHR3D_SFX_PLUGIN
bool Sfx::hasSfxPluginWIndow(const SfxId sfxId)
{
  //ASSERT(index >= 0);

  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return SfxPluginClap::hasSfxPluginWIndow(sfxId.sfxIndex);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return SfxPluginLv2::hasSfxPluginWIndow(sfxId.sfxIndex);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return SfxPluginVst::hasSfxPluginWIndow(sfxId.sfxIndex);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return SfxPluginVst3::hasSfxPluginWIndow(sfxId.sfxIndex);
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

void Sfx::openSfxPluginWindow(const SfxId sfxId, i32 instance, Shr3DWindow parentWindow)
{
  //ASSERT(index >= 0);

  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    SfxPluginClap::openWindow(sfxId.sfxIndex, instance, parentWindow);
    return;
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    SfxPluginLv2::openWindow(sfxId.sfxIndex, instance, parentWindow);
    return;
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    SfxPluginVst::openWindow(sfxId.sfxIndex, instance, parentWindow);
    return;
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    SfxPluginVst3::openWindow(sfxId.sfxIndex, instance, parentWindow);
    return;
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

Size Sfx::getSfxPluginWIndowSize(const SfxId sfxId, i32 instance)
{
  //ASSERT(index >= 0);

  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return SfxPluginClap::getSfxPluginWIndowSize(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return SfxPluginLv2::getSfxPluginWIndowSize(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return SfxPluginVst::getSfxPluginWIndowSize(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return SfxPluginVst3::getSfxPluginWIndowSize(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

void Sfx::closeSfxPluginWindow(const SfxId sfxId, i32 instance)
{
  //ASSERT(index >= 0);

  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    SfxPluginClap::closeWindow(sfxId.sfxIndex, instance);
    return;
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    SfxPluginLv2::closeWindow(sfxId.sfxIndex, instance);
    return;
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    SfxPluginVst::closeWindow(sfxId.sfxIndex, instance);
    return;
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    SfxPluginVst3::closeWindow(sfxId.sfxIndex, instance);
    return;
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

i32 Sfx::numParams(const SfxId sfxId, i32 instance)
{
  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return SfxPluginClap::numParams(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return SfxPluginLv2::numParams(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return SfxPluginVst::numParams(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return SfxPluginVst3::numParams(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_VST3

  default:
    unreachable();
  };
}

void Sfx::getParameterProperties(const SfxId sfxId, i32 instance, i32 param, SfxParameterProperties& parameterProperties)
{
  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return SfxPluginClap::getParameterProperties(sfxId.sfxIndex, instance, param, parameterProperties);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return SfxPluginLv2::getParameterProperties(sfxId.sfxIndex, instance, param, parameterProperties);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return SfxPluginVst::getParameterProperties(sfxId.sfxIndex, instance, param, parameterProperties);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return SfxPluginVst3::getParameterProperties(sfxId.sfxIndex, instance, param, parameterProperties);
#endif // SHR3D_SFX_PLUGIN_VST3

  default:
    unreachable();
  };
}

f32 Sfx::getParameter(const SfxId sfxId, i32 instance, i32 param)
{
  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return SfxPluginClap::getParameter(sfxId.sfxIndex, instance, param);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return SfxPluginLv2::getParameter(sfxId.sfxIndex, instance, param);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return SfxPluginVst::getParameter(sfxId.sfxIndex, instance, param);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return SfxPluginVst3::getParameter(sfxId.sfxIndex, instance, param);
#endif // SHR3D_SFX_PLUGIN_VST3

  default:
    unreachable();
  };
}

void Sfx::setParameter(const SfxId sfxId, i32 instance, i32 param, f32 value)
{
  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return SfxPluginClap::setParameter(sfxId.sfxIndex, instance, param, value);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return SfxPluginLv2::setParameter(sfxId.sfxIndex, instance, param, value);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return SfxPluginVst::setParameter(sfxId.sfxIndex, instance, param, value);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return SfxPluginVst3::setParameter(sfxId.sfxIndex, instance, param, value);
#endif // SHR3D_SFX_PLUGIN_VST3

  default:
    unreachable();
  };
}
#endif // SHR3D_SFX_PLUGIN

// return true if outBlock was written
ProcessBlockResult Sfx::processBlock(const SfxId sfxId, i32 instance, f32** inBlock, f32** outBlock, i32 blockSize)
{
  //ASSERT(index >= 0);

  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_CORE
  case SfxSystem::core:
#ifdef SHR3D_SFX_CORE_AIRWINDOWS
  case SfxSystem::coreAirWindows:
#endif // SHR3D_SFX_CORE_AIRWINDOWS
#ifdef SHR3D_SFX_CORE_RAKARRACK
  case SfxSystem::coreRakarrack:
#endif // SHR3D_SFX_CORE_RAKARRACK
    return SFXCore::processBlock(sfxId, instance, inBlock, outBlock, blockSize);
#endif // SHR3D_SFX_CORE

#ifdef SHR3D_SFX_CORE_EXTENSION_V2
  case SfxSystem::coreExtensionV2:
    return Global::sfxCoreExtensionV2SortedByRegistrationOrder[sfxId.sfxIndex]->getInstance(instance)->processBlock(inBlock, outBlock, blockSize);
#endif // SHR3D_SFX_CORE_EXTENSION_V2

#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    SfxPluginClap::processBlock(sfxId.sfxIndex, instance, inBlock, outBlock, blockSize);
    return ProcessBlockResult::ProcessedInOutBlock;
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    SfxPluginLv2::processBlock(sfxId.sfxIndex, instance, inBlock, outBlock, blockSize);
    return ProcessBlockResult::ProcessedInOutBlock;
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    SfxPluginVst::processBlock(sfxId.sfxIndex, instance, inBlock, outBlock, blockSize);
    return ProcessBlockResult::ProcessedInOutBlock;
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    SfxPluginVst3::processBlock(sfxId.sfxIndex, instance, inBlock, outBlock, blockSize);
    return ProcessBlockResult::ProcessedInOutBlock;
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

std::u8string Sfx::saveParameters(const SfxId sfxId, i32 instance)
{
  //ASSERT(index > 0);

  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_CORE
  case SfxSystem::core:
#ifdef SHR3D_SFX_CORE_AIRWINDOWS
  case SfxSystem::coreAirWindows:
#endif // SHR3D_SFX_CORE_AIRWINDOWS
#ifdef SHR3D_SFX_CORE_RAKARRACK
  case SfxSystem::coreRakarrack:
    return SFXCore::saveParameters(sfxId, instance);
#endif // SHR3D_SFX_CORE_RAKARRACK
#endif // SHR3D_SFX_CORE

#ifdef SHR3D_SFX_CORE_EXTENSION_V2
  case SfxSystem::coreExtensionV2:
  {
    std::u8string str;
    const i32 parameterCount = Global::sfxCoreExtensionV2SortedByRegistrationOrder[sfxId.sfxIndex]->getParameterCount();
    if (parameterCount >= 1)
    {
      const SfxCoreExtensionV2Instance* sfx = Global::sfxCoreExtensionV2SortedByRegistrationOrder[sfxId.sfxIndex]->getInstance(instance);
      str = F32_To_String(sfx->getParameter(0));
      for (i32 i = 1; i < parameterCount; ++i)
        str += u8',' + F32_To_String(sfx->getParameter(i));
    }
    return str;
  }
#endif // SHR3D_SFX_CORE_EXTENSION_V2

#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return SfxPluginClap::saveParameters(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return SfxPluginLv2::saveParameters(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return SfxPluginVst::saveParameters(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return SfxPluginVst3::saveParameters(sfxId.sfxIndex, instance);
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

void Sfx::loadParameters(const SfxId sfxId, i32 instance, const std::u8string& parameters)
{
  //ASSERT(index > 0);

  switch (sfxId.system)
  {
#ifdef SHR3D_SFX_CORE
  case SfxSystem::core:
#ifdef SHR3D_SFX_CORE_AIRWINDOWS
  case SfxSystem::coreAirWindows:
#endif // SHR3D_SFX_CORE_AIRWINDOWS
#ifdef SHR3D_SFX_CORE_RAKARRACK
  case SfxSystem::coreRakarrack:
    //if (!parameters.empty())
    SFXCore::loadParameters(sfxId, instance, parameters);
    return;
#endif // SHR3D_SFX_CORE_RAKARRACK
#endif // SHR3D_SFX_CORE

#ifdef SHR3D_SFX_CORE_EXTENSION_V2
  case SfxSystem::coreExtensionV2:
    if (!parameters.empty())
    {
      const std::vector<std::u8string> params = String::split(parameters, u8',');
      ASSERT(Global::sfxCoreExtensionV2SortedByRegistrationOrder[sfxId.sfxIndex]->getParameterCount() == params.size());

      SfxCoreExtensionV2Instance* sfx = Global::sfxCoreExtensionV2SortedByRegistrationOrder[sfxId.sfxIndex]->getInstance(instance);
      for (i32 i = 0; i < params.size(); ++i)
        sfx->setParameter(i, f32(atof(reinterpret_cast<const char*>(params[i].c_str()))));
    }
    return;
#endif // SHR3D_SFX_CORE_EXTENSION_V2

#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    SfxPluginClap::loadParameters(sfxId.sfxIndex, instance, parameters);
    return;
#endif // SHR3D_SFX_PLUGIN_CLAP

#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    SfxPluginLv2::loadParameters(sfxId.sfxIndex, instance, parameters);
    return;
#endif // SHR3D_SFX_PLUGIN_LV2

#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    SfxPluginVst::loadParameters(sfxId.sfxIndex, instance, parameters);
    return;
#endif // SHR3D_SFX_PLUGIN_VST

#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    SfxPluginVst3::loadParameters(sfxId.sfxIndex, instance, parameters);
    return;
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    unreachable();
  }
}

#ifdef SHR3D_SFX_PLUGIN
bool Sfx::sfxSystemIsPlugin(const SfxSystem sfxSystem)
{
  switch (sfxSystem)
  {
#ifdef SHR3D_SFX_PLUGIN_CLAP
  case SfxSystem::clap:
    return true;
#endif // SHR3D_SFX_PLUGIN_CLAP
#ifdef SHR3D_SFX_PLUGIN_LV2
  case SfxSystem::lv2:
    return true;
#endif // SHR3D_SFX_PLUGIN_LV2
#ifdef SHR3D_SFX_PLUGIN_VST
  case SfxSystem::vst:
    return true;
#endif // SHR3D_SFX_PLUGIN_VST
#ifdef SHR3D_SFX_PLUGIN_VST3
  case SfxSystem::vst3:
    return true;
#endif // SHR3D_SFX_PLUGIN_VST3
  default:
    return false;
  }
}
#endif // SHR3D_SFX_PLUGIN

#endif // SHR3D_SFX
