// SPDX-License-Identifier: Unlicense

#include "api.h"

#ifdef SHR3D_SFX_CORE_EXTENSION_V2

#include "../../nuklear.h"
#include "../../settings.h"
#include <stdio.h>

struct LooperInstance final : SfxCoreExtensionV2Instance
{
  enum struct LooperState
  {
    idle,
    record,
    playback
  } looperState;
  std::vector<f32> recordedAudio;
  u64 playbackPos = 0;

  ProcessBlockResult processBlock(f32** inBlock, f32** /*outBlock*/, const i32 blockSize) override
  {
    switch (looperState)
    {
    case LooperState::idle:
      break;
    case LooperState::record:
      for (i32 i = 0; i < blockSize; ++i)
      {
        recordedAudio.push_back(inBlock[0][i]);
      }
      break;
    case LooperState::playback:
      if (recordedAudio.size() != 0)
      {
        for (i32 i = 0; i < blockSize; ++i)
        {
          inBlock[0][i] += recordedAudio[playbackPos];
          inBlock[1][i] += recordedAudio[playbackPos];
          ++playbackPos;
        }
        if (playbackPos >= recordedAudio.size())
          playbackPos = 0;
      }
      break;
    }
    return ProcessBlockResult::ProcessedInInBlock;
  }

  f32 getParameter(const i32 /*index*/) const override
  {
    unreachable();
  }

  void setParameter(const i32 /*index*/, const f32 /*value*/) override
  {
    unreachable();
  }

  void getParameterDisplay(const i32 /*index*/, char* /*text*/) override
  {
    unreachable();
  }

  void resetParameter(const i32 /*index*/) override
  {
    unreachable();
  }
};

static bool Looper_uiCallback(nk_context* ctx, SfxCoreExtensionV2Base* extension, i32 instance)
{
  const bool showWindow = nk_begin(ctx, "Looper", { 20, 20, 120, 110 }, NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_TITLE | NK_WINDOW_CLOSABLE | NK_WINDOW_SCALABLE);
  if (showWindow)
  {
    nk_layout_row_template_begin(ctx, 22.0f * Settings::uiScale);
    nk_layout_row_template_push_static(ctx, 25.0f * Settings::uiScale);
    nk_layout_row_template_push_static(ctx, 25.0f * Settings::uiScale);
    nk_layout_row_template_push_static(ctx, 25.0f * Settings::uiScale);
    nk_layout_row_template_push_dynamic(ctx);
    nk_layout_row_template_end(ctx);

    LooperInstance* looperInstance = static_cast<LooperInstance*>(extension->getInstance(instance));

    if (nk_button_symbol(ctx, NK_SYMBOL_CIRCLE_SOLID))
    {
      looperInstance->recordedAudio.clear();
      looperInstance->playbackPos = 0;
      looperInstance->looperState = LooperInstance::LooperState::record;
    }
    if (nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_RIGHT))
    {
      looperInstance->playbackPos = 0;
      looperInstance->looperState = LooperInstance::LooperState::playback;
    }
    if (nk_button_symbol(ctx, NK_SYMBOL_RECT_SOLID))
    {
      looperInstance->playbackPos = 0;
      looperInstance->looperState = LooperInstance::LooperState::idle;
    }

    nk_layout_row_dynamic(ctx, 22.0f * Settings::uiScale, 1);

#ifndef PLATFORM_EMSCRIPTEN
    nk_progress(ctx, &looperInstance->playbackPos, looperInstance->recordedAudio.size(), false);
#else // PLATFORM_EMSCRIPTEN
    nk_size playbackPos = looperInstance->playbackPos;
    nk_progress(ctx, &playbackPos, looperInstance->recordedAudio.size(), false);
    looperInstance->playbackPos = playbackPos;
#endif // PLATFORM_EMSCRIPTEN
  }
  nk_end(ctx);

  return showWindow;
}

static struct Looper final : SfxCoreExtensionV2Base
{
  Looper()
  {
    uiCallback = Looper_uiCallback;
    RegisterSfxCoreExtensionV2(u8"Looper", this);
  }

  i32 getParameterCount() const override
  {
    return 0;
  }

  const char8_t* getParameterLabel(const i32 index) override
  {
    switch (index)
    {
    case 0:
      return u8"";
    default:
      unreachable();
    }
  }

  const char8_t* getParameterName(const i32 index) override
  {
    switch (index)
    {
    case 0:
      return u8"Looper";
    default:
      unreachable();
    }
  }

  SfxCoreExtensionV2Instance* getInstance(const i32 instance) override
  {
    static SfxCoreExtensionV2Instance* instances[ExtensionV2MaxInstanceCount];
    assert(instance < ARRAY_SIZE(instances));

    if (instances[instance] == nullptr)
      instances[instance] = new LooperInstance;

    return instances[instance];
  }
} looper;

#endif // SHR3D_SFX_CORE_EXTENSION_V2
