/**********************************************************************

  Audacity: A Digital Audio Editor

  LoadVamp.cpp

  Chris Cannam

**********************************************************************/



#if defined(USE_VAMP)
#include "LoadVamp.h"
#include "../../ModuleManager.h"

#include <wx/filename.h>

#include "VampEffect.h"

#include <iostream>
#include <map>

using namespace Vamp;
using namespace Vamp::HostExt;
using namespace Vamp::HostExt;

// ============================================================================
// Module registration entry point
//
// This is the symbol that Audacity looks for when the module is built as a
// dynamic library.
//
// When the module is builtin to Audacity, we use the same function, but it is
// declared static so as not to clash with other builtin modules.
// ============================================================================
DECLARE_MODULE_ENTRY(AudacityModule)
{
   // Create and register the importer
   // Trust the module manager not to leak this
   return safenew VampEffectsModule();
}

// ============================================================================
// Register this as a builtin module
// ============================================================================
DECLARE_BUILTIN_MODULE(VampsEffectBuiltin);

///////////////////////////////////////////////////////////////////////////////
//
// VampEffectsModule
//
///////////////////////////////////////////////////////////////////////////////

VampEffectsModule::VampEffectsModule()
{
}

VampEffectsModule::~VampEffectsModule()
{
}

// ============================================================================
// ComponentInterface implementation
// ============================================================================

PluginPath VampEffectsModule::GetPath()
{
   return {};
}

ComponentInterfaceSymbol VampEffectsModule::GetSymbol()
{
   return XO("Vamp Effects");
}

VendorSymbol VampEffectsModule::GetVendor()
{
   return XO("The Tenacity Team and contributors");
}

wxString VampEffectsModule::GetVersion()
{
   // This "may" be different if this were to be maintained as a separate DLL
   return VAMPEFFECTS_VERSION;
}

TranslatableString VampEffectsModule::GetDescription()
{
   return XO("Provides Vamp Effects support to Tenacity");
}

// ============================================================================
// ModuleInterface implementation
// ============================================================================

bool VampEffectsModule::Initialize()
{
   // Nothing to do here
   return true;
}

void VampEffectsModule::Terminate()
{
   // Nothing to do here
   return;
}

EffectFamilySymbol VampEffectsModule::GetOptionalFamilySymbol()
{
#if USE_VAMP
   return VAMPEFFECTS_FAMILY;
#else
   return {};
#endif
}

const FileExtensions &VampEffectsModule::GetFileExtensions()
{
   static FileExtensions empty;
   return empty;
}

bool VampEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm))
{
   return false;
}

PluginPaths VampEffectsModule::FindPluginPaths(PluginManagerInterface & WXUNUSED(pm))
{
   PluginPaths names;

   PluginLoader *loader = PluginLoader::getInstance();

   PluginLoader::PluginKeyList keys = loader->listPlugins();

   for (PluginLoader::PluginKeyList::iterator i = keys.begin(); i != keys.end(); ++i)
   {
      std::unique_ptr<Plugin> vp{ PluginLoader::getInstance()->loadPlugin(*i, 48000) }; // rate doesn't matter here
      if (!vp)
      {
         continue;
      }

      // We limit the listed plugin outputs to those whose results can
      // readily be displayed in an Audacity label track.
      //
      // - Any output whose features have no values (time instants only),
      //   with or without duration, is fine
      //
      // - Any output whose features have more than one value, or an
      //   unknown or variable number of values, is right out
      //
      // - Any output whose features have exactly one value, with
      //   variable sample rate or with duration, should be OK --
      //   this implies a sparse feature, of which the time and/or
      //   duration are significant aspects worth displaying
      //
      // - An output whose features have exactly one value, with
      //   fixed sample rate and no duration, cannot be usefully
      //   displayed -- the value is the only significant piece of
      //   data there and we have no good value plot

      Plugin::OutputList outputs = vp->getOutputDescriptors();

      int output = 0;

      for (Plugin::OutputList::iterator j = outputs.begin(); j != outputs.end(); ++j)
      {
         if (j->sampleType == Plugin::OutputDescriptor::FixedSampleRate ||
               j->sampleType == Plugin::OutputDescriptor::OneSamplePerStep ||
               !j->hasFixedBinCount ||
               j->binCount > 1)
         {
            // All of these qualities disqualify (see notes above)

            ++output;
            continue;
         }

         wxString name = wxString::FromUTF8(vp->getName().c_str());

         if (outputs.size() > 1)
         {
            // This is not the plugin's only output.
            // Use "plugin name: output name" as the effect name,
            // unless the output name is the same as the plugin name
            wxString outputName = wxString::FromUTF8(j->name.c_str());
            if (outputName != name)
            {
               name = wxString::Format(wxT("%s: %s"),
                                       name, outputName);
            }
         }

         wxString path = wxString::FromUTF8(i->c_str()) + wxT("/") + name;
         names.push_back(path);

         ++output;
      }
   }

   return names;
}

unsigned VampEffectsModule::DiscoverPluginsAtPath(
   const PluginPath & path, TranslatableString &errMsg,
   const RegistrationCallback &callback)
{
   errMsg = {};
   int output;
   bool hasParameters;

   auto vp = FindPlugin(path, output, hasParameters);
   if (vp)
   {
      VampEffect effect(std::move(vp), path, output, hasParameters);
      if (callback)
         callback( this, &effect );

      return 1;
   }

   errMsg = XO("Could not load the library");
   return 0;
}

bool VampEffectsModule::IsPluginValid(const PluginPath & path, bool bFast)
{
   int output;
   bool hasParameters;
   if( bFast )
      return true;

   auto vp = FindPlugin(path, output, hasParameters);
   return bool(vp);
}

std::unique_ptr<ComponentInterface>
VampEffectsModule::CreateInstance(const PluginPath & path)
{
   // Acquires a resource for the application.
   int output;
   bool hasParameters;

   if (auto vp = FindPlugin(path, output, hasParameters))
      return std::make_unique<VampEffect>(std::move(vp), path, output, hasParameters);
   return nullptr;
}

// VampEffectsModule implementation

std::unique_ptr<Vamp::Plugin> VampEffectsModule::FindPlugin(const PluginPath & path,
                                      int & output,
                                      bool & hasParameters)
{
   PluginLoader::PluginKey key = path.BeforeLast(wxT('/')).ToUTF8().data();

   std::unique_ptr<Plugin> vp{ PluginLoader::getInstance()->loadPlugin(key, 48000) }; // rate doesn't matter here
   if (!vp)
   {
      return nullptr;
   }

   // We limit the listed plugin outputs to those whose results can
   // readily be displayed in an Audacity label track.
   //
   // - Any output whose features have no values (time instants only),
   //   with or without duration, is fine
   //
   // - Any output whose features have more than one value, or an
   //   unknown or variable number of values, is right out
   //
   // - Any output whose features have exactly one value, with
   //   variable sample rate or with duration, should be OK --
   //   this implies a sparse feature, of which the time and/or
   //   duration are significant aspects worth displaying
   //
   // - An output whose features have exactly one value, with
   //   fixed sample rate and no duration, cannot be usefully
   //   displayed -- the value is the only significant piece of
   //   data there and we have no good value plot

   Plugin::OutputList outputs = vp->getOutputDescriptors();

   output = 0;

   hasParameters = !vp->getParameterDescriptors().empty();

   for (Plugin::OutputList::iterator j = outputs.begin(); j != outputs.end(); ++j)
   {
      if (j->sampleType == Plugin::OutputDescriptor::FixedSampleRate ||
            j->sampleType == Plugin::OutputDescriptor::OneSamplePerStep ||
            !j->hasFixedBinCount ||
            j->binCount > 1)
      {
         // All of these qualities disqualify (see notes above)

         ++output;
         continue;
      }

      wxString name = wxString::FromUTF8(vp->getName().c_str());

      if (outputs.size() > 1)
      {
         // This is not the plugin's only output.
         // Use "plugin name: output name" as the effect name,
         // unless the output name is the same as the plugin name
         wxString outputName = wxString::FromUTF8(j->name.c_str());
         if (outputName != name)
         {
            name = wxString::Format(wxT("%s: %s"),
                                    name, outputName);
         }
      }

      if (wxString::FromUTF8(key.c_str()) + wxT("/") + name == path)
      {
         return vp;
      }

      ++output;
   }

   return {};
}

#endif
