/*
 * File: dsp.cpp
 * Created Date: March 17, 2023
 * Author: Steven Atkinson (steven@atkinson.mn)
 */

#include "dsp1.h"

#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER

#include <assert.h>


DSP1::DSP1()
: mOutputPointers(nullptr)
, mOutputPointersSize(0)
{
}

DSP1::~DSP1()
{
  this->_DeallocateOutputPointers();
}

void DSP1::_AllocateOutputPointers(const size_t numChannels)
{
  if (this->mOutputPointers != nullptr)
  {
    assert(false); // Tried to re-allocate over non-null mOutputPointers
  }
  this->mOutputPointers = new float*[numChannels];
  if (this->mOutputPointers == nullptr)
  {
    assert(false); // Failed to allocate pointer to output buffer!
  }
  this->mOutputPointersSize = numChannels;
}

void DSP1::_DeallocateOutputPointers()
{
  if (this->mOutputPointers != nullptr)
  {
    delete[] this->mOutputPointers;
    this->mOutputPointers = nullptr;
  }
  if (this->mOutputPointers != nullptr)
  {
    assert(false); // "Failed to deallocate output pointer!"
  }
  this->mOutputPointersSize = 0;
}

float** DSP1::_GetPointers()
{
  for (auto c = 0; c < this->_GetNumChannels(); c++)
    this->mOutputPointers[c] = this->mOutputs[c].data();
  return this->mOutputPointers;
}

void DSP1::_PrepareBuffers(const size_t numChannels, const size_t numFrames)
{
  const size_t oldFrames = this->_GetNumFrames();
  const size_t oldChannels = this->_GetNumChannels();

  const bool resizeChannels = oldChannels != numChannels;
  const bool resizeFrames = resizeChannels || (oldFrames != numFrames);
  if (resizeChannels)
  {
    this->mOutputs.resize(numChannels);
    this->_ResizePointers(numChannels);
  }
  if (resizeFrames)
    for (auto c = 0; c < numChannels; c++)
      this->mOutputs[c].resize(numFrames);
}

void DSP1::_ResizePointers(const size_t numChannels)
{
  if (this->mOutputPointersSize == numChannels)
    return;
  this->_DeallocateOutputPointers();
  this->_AllocateOutputPointers(numChannels);
}

History::History()
: DSP1()
, mHistoryRequired(0)
, mHistoryIndex(0)
{
}

void History::_AdvanceHistoryIndex(const size_t bufferSize)
{
  this->mHistoryIndex += bufferSize;
}

void History::_EnsureHistorySize(const size_t bufferSize)
{
  const size_t repeatSize = std::max(bufferSize, this->mHistoryRequired);
  const size_t requiredHistoryArraySize = 10 * repeatSize; // Just so we don't spend too much time copying back.
  if (this->mHistory.size() < requiredHistoryArraySize)
  {
    this->mHistory.resize(requiredHistoryArraySize);
    std::fill(this->mHistory.begin(), this->mHistory.end(), 0.0f);
    this->mHistoryIndex = this->mHistoryRequired; // Guaranteed to be less than
                                                  // requiredHistoryArraySize
  }
}

void History::_RewindHistory()
{
  // TODO memcpy?  Should be fine w/ history array being >2x the history length.
  for (size_t i = 0, j = this->mHistoryIndex - this->mHistoryRequired; i < this->mHistoryRequired; i++, j++)
    this->mHistory[i] = this->mHistory[j];
  this->mHistoryIndex = this->mHistoryRequired;
}

void History::_UpdateHistory(float** inputs, const size_t numChannels, const size_t numFrames)
{
  this->_EnsureHistorySize(numFrames);
  if (numChannels < 1)
  {
    assert(false); // "Zero channels?"
  }
  if (this->mHistoryIndex + numFrames >= this->mHistory.size())
    this->_RewindHistory();
  // Grabs channel 1, drops hannel 2.
  for (size_t i = 0, j = this->mHistoryIndex; i < numFrames; i++, j++)
    // Convert down to float here.
    this->mHistory[j] = (float)inputs[0][i];
}

#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
