// Copyright (c) 2021 Giorgos Vougioukas
//
// The license can be found in the LICENSE file.

#include "RSI/data_bank.h"
#include "RSI/definitions.h"
#include "RSI/preferences.h"

#include <math.h>

#include "WDL/fpcmp.h"
#include "WDL/pcmfmtcvt.h"
#include "WDL/db2val.h"

#define RSI_DATABANK_SLOTS 12

RSI_DataBankSlot::RSI_DataBankSlot()
  : m_data(NULL)
  , m_size(WDL_INT64_CONST(0))
  , m_capacity(WDL_INT64_CONST(0))
  , m_index(WDL_INT64_CONST(0))
  , m_indexrev(WDL_INT64_CONST(0))
  , m_samplerate(44100.0)
  , m_antidotefmt(0)
  , m_length(0.0)
  , m_channels(2)
  , m_bitspersample(0)
  , m_stt(NULL)
  , m_ebur128(false)
  , m_ebur128ref(0.0)
  , m_ebur128mode(0)
  , m_ebur128momentary(-HUGE_VAL)
  , m_ebur128shortterm(-HUGE_VAL)
  , m_ebur128momentarywindow(400)
  , m_ebur128shorttermwindow(3000)
  , m_ebur128integrated(-HUGE_VAL)
  , m_ebur128range(-HUGE_VAL)
  , m_ebur128samplepeak(-HUGE_VAL)
  , m_ebur128truepeak(-HUGE_VAL)
  , m_ebur128gain(-HUGE_VAL)
  , m_ebur128downwardonly(false)
  , m_momentarywindow(0)
  , m_shorttermwindow(0)
  , m_prepared(false)
  , m_pps(400)
  , m_pt(0)
  , m_peak(NULL)
  , m_peakpoints(100)
  , m_peaksize(WDL_INT64_CONST(0))
  , m_peakcapacity(WDL_INT64_CONST(0))
  , m_peakstep(WDL_INT64_CONST(0))
  , m_avgsam(0.0f)
  , m_samni(0)
  , m_waveforma(NULL)
  , m_waveformb(NULL)
{
  m_samplerate = g_preferences->GetAudioDeviceSamplerate();
  m_antidotefmt = g_preferences->GetAntidoteBitDepth();
  m_ebur128 = g_preferences->WantEBUR128();
  m_ebur128ref = g_preferences->GetEBUR128LUFS();
  m_ebur128mode = g_preferences->GetEBUR128Mode();
  m_ebur128downwardonly = g_preferences->WantDownwardOnly();

  switch (m_antidotefmt)
  {
    case 0: m_bitspersample = 16; break;
    case 1: m_bitspersample = 24; break;
    case 2: m_bitspersample = 32; break;
    case 3: m_bitspersample = 32; break;
    case 4: m_bitspersample = 64; break;
  }

  m_waveforma = new LICE_SysBitmap;
  m_waveformb = new LICE_SysBitmap;
}

RSI_DataBankSlot::~RSI_DataBankSlot()
{
  if (m_data) free(m_data);
  if (m_peak) free(m_peak);
  if (m_stt) ebur128_destroy(&m_stt);
  if (m_waveforma) delete m_waveforma;
  if (m_waveformb) delete m_waveformb;
}

void RSI_DataBankSlot::Reset()
{
  WDL_MutexLock lock(&m_mutex);
  m_size = WDL_INT64_CONST(0);
  m_index = WDL_INT64_CONST(0);
  m_indexrev = WDL_INT64_CONST(0);
  m_length = 0.0;
  m_prepared = false;

  m_peaksize = WDL_INT64_CONST(0);
  m_peakstep = WDL_INT64_CONST(0);
  float m_avgsam = 0.0f;
  int m_samni = 0;

  if (m_stt) { ebur128_destroy(&m_stt); m_stt = NULL; }
}

void RSI_DataBankSlot::PrepareIndexes()
{
  WDL_MutexLock lock(&m_mutex);
  m_index = WDL_INT64_CONST(0);
  m_indexrev = m_size;

  if (m_ebur128 && (m_ebur128mode & EBUR128_MODE_M) == EBUR128_MODE_M)
  {
    m_ebur128gain = m_ebur128ref - m_ebur128momentary;
  }

  if (m_ebur128 && (m_ebur128mode & EBUR128_MODE_S) == EBUR128_MODE_S)
  {
    m_ebur128gain = m_ebur128ref - m_ebur128shortterm;
  }

  if (m_ebur128 && (m_ebur128mode & EBUR128_MODE_I) == EBUR128_MODE_I)
  {
    int res = ebur128_loudness_global(m_stt, &m_ebur128integrated);
    if (res == EBUR128_SUCCESS)
    {
      m_ebur128gain = m_ebur128ref - m_ebur128integrated;
    }
    else
    {
      m_ebur128 = false;
    }
  }

  if (m_ebur128 && (m_ebur128mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA)
  {
    int res = ebur128_loudness_range(m_stt, &m_ebur128range);
    if (res != EBUR128_SUCCESS)
    {
      m_ebur128 = false;
    }
  }

  m_prepared = true;

#if 0
  WDL_FastString ss;
  ss.SetFormatted(512,
    "Reference: %.0f LUFS\n"
    "Momentary: %.2f LUFS\n"
    "Shortterm: %.2f LUFS\n"
    "Integrated: %.2f LUFS\n"
    "Range: %.2f LU\n"
    "Peak: %.6f (%.2f dBFS)\n"
    "Peak: %.6f (%.2f dBTP)\n"
    "Gain: %.2f dB",
    m_ebur128ref,
    m_ebur128momentary,
    m_ebur128shortterm,
    m_ebur128integrated,
    m_ebur128range,
    m_ebur128samplepeak, VAL2DB(m_ebur128samplepeak),
    m_ebur128truepeak, VAL2DB(m_ebur128truepeak),
    m_ebur128gain);
  MessageBox(g_mainwnd, ss.Get(), "", MB_OK);
#endif
}

bool RSI_DataBankSlot::IsPrepared() const
{
  return m_prepared;
}

void RSI_DataBankSlot::Seek(double time)
{
  WDL_MutexLock lock(&m_mutex);
  WDL_INT64 timems = (WDL_INT64)(time * 1000);
  int csrate = (int)m_samplerate / 1000;
  WDL_INT64 pos = (WDL_INT64)(timems * (csrate * m_channels * (m_bitspersample / 8)));
  if (WDL_EssentiallyEqual(time, 0.0)) pos = 0;
  if (WDL_DefinitelyLessThan(time, 0.0)) pos = 0;
  if (pos > m_size) pos = m_size;
  m_index = m_indexrev = pos;
}

double RSI_DataBankSlot::GetLength() const
{
  return m_length;
}

double RSI_DataBankSlot::GetPosition() const
{
  return (m_index) / (m_samplerate * m_channels * (m_bitspersample / 8));
}

void RSI_DataBankSlot::Allocate(double length)
{
  WDL_MutexLock lock(&m_mutex);

  m_length = length;
  WDL_INT64 request = 0;
  double len = floor(m_length) + 1.0;

  switch (m_antidotefmt)
  {
  case 0:
    {
      request = (WDL_INT64)(len * m_samplerate * m_channels * sizeof(short));
    } break;
  case 1:
    {
      request = (WDL_INT64)(len * m_samplerate * m_channels * sizeof(char) * 3);
    } break;
  case 2:
    {
      request = (WDL_INT64)(len * m_samplerate * m_channels * sizeof(int));
    } break;
  case 3:
    {
      request = (WDL_INT64)(len * m_samplerate * m_channels * sizeof(float));
    } break;
  case 4:
    {
      request = (WDL_INT64)(len * m_samplerate * m_channels * sizeof(double));
    } break;
  }

  if (!m_data)
  {
    m_data = (unsigned char *)malloc((size_t)request);
    m_capacity = request;
  }
  else if (request > m_capacity)
  {
    m_data = (unsigned char *)realloc(m_data, (size_t)request);
    m_capacity = request;
  }

  request = (WDL_INT64)(len * m_samplerate / m_peakpoints * sizeof(float));

  if (!m_peak)
  {
    m_peak = (float *)malloc((size_t)request);
    m_peakcapacity = request;
  }
  else if (request > m_peakcapacity)
  {
    m_peak = (float *)realloc(m_peak, (size_t)request);
    m_peakcapacity = request;
  }

  if (m_ebur128)
  {
    m_stt = ebur128_init((unsigned int)m_channels,
      (unsigned long)m_samplerate, m_ebur128mode);
    ebur128_set_channel(m_stt, 0, EBUR128_LEFT);
    ebur128_set_channel(m_stt, 1, EBUR128_RIGHT);
  }
}

void RSI_DataBankSlot::AddSamples(SAM *buffer, int buffer_size)
{
  WDL_MutexLock lock(&m_mutex);

  if (m_ebur128)
  {
    size_t frames = (size_t)(buffer_size / m_channels);
    ebur128_add_frames_double(m_stt, buffer, frames);

    m_momentarywindow += (int)(frames / m_samplerate * 1000);
    m_shorttermwindow += (int)(frames / m_samplerate * 1000);

    if ((m_ebur128mode & EBUR128_MODE_M) == EBUR128_MODE_M &&
      m_momentarywindow >= m_ebur128momentary)
    {
      double tmp;
      int res = ebur128_loudness_momentary(m_stt, &tmp);
      if (res != EBUR128_SUCCESS) m_ebur128 = false;
      m_ebur128momentary = wdl_max(m_ebur128momentary, tmp);
      m_momentarywindow = 0;
    }

    if ((m_ebur128mode & EBUR128_MODE_S) == EBUR128_MODE_S &&
      m_shorttermwindow >= m_ebur128shorttermwindow)
    {
      double tmp;
      int res = ebur128_loudness_shortterm(m_stt, &tmp);
      if (res != EBUR128_SUCCESS) m_ebur128 = false;
      m_ebur128shortterm = wdl_max(m_ebur128shortterm, tmp);
      m_shorttermwindow = 0;
    }

    if ((m_ebur128mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK)
    {
      for (int i = 0; i < m_channels; i++)
      {
        double tmp;
        int res = ebur128_sample_peak(m_stt, (unsigned int)i, &tmp);
        if (res != EBUR128_SUCCESS) m_ebur128 = false;
        m_ebur128samplepeak = wdl_max(m_ebur128samplepeak, tmp);
      }
    }

    if ((m_ebur128mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK)
    {
      for (int i = 0; i < m_channels; i++)
      {
        double tmp;
        int res = ebur128_true_peak(m_stt, (unsigned int)i, &tmp);
        if (res != EBUR128_SUCCESS) m_ebur128 = false;
        m_ebur128truepeak = wdl_max(m_ebur128truepeak, tmp);
      }
    }
  }

  switch (m_antidotefmt)
  {
  case 0:
    {
      m_rawbuf.Resize(buffer_size * sizeof(short));
      doublesToPcm(buffer, 1, buffer_size, m_rawbuf.Get(), 16, 1);
    } break;
  case 1:
    {
      m_rawbuf.Resize(buffer_size * sizeof(char) * 3);
      doublesToPcm(buffer, 1, buffer_size, m_rawbuf.Get(), 24, 1);
    } break;
  case 2:
    {
      m_rawbuf.Resize(buffer_size * sizeof(int));
      doublesToPcm(buffer, 1, buffer_size, m_rawbuf.Get(), 32, 1);
    } break;
  case 3:
    {
      m_rawbuf.Resize(buffer_size * sizeof(float));
      float *flt = (float *)m_rawbuf.Get();
      for (int i = 0; i < buffer_size; i++)
      {
        flt[i] = (float)buffer[i];
      }
    } break;
  case 4:
    {
      m_rawbuf.Resize(buffer_size * sizeof(double));
      double *dbl = (double *)m_rawbuf.Get();
      for (int i = 0; i < buffer_size; i++)
      {
        dbl[i] = (double)buffer[i];
      }
    } break;
  }

  memcpy(&m_data[m_index], m_rawbuf.Get(), m_rawbuf.GetSize());
  m_index += m_rawbuf.GetSize();
  m_size = m_index;

  WDL_ASSERT(m_size <= m_capacity);

  float sam;
  const WDL_INT64 peakstop = (WDL_INT64)(m_samplerate / m_peakpoints);

  for (int i = 0; i < buffer_size; i += m_channels)
  {
    sam = (float)buffer[i];
    sam = wdl_abs(sam);
    sam = wdl_clamp(sam, 0.0f, 1.0f);
    m_avgsam += sam;

    if (m_peakstep >= peakstop)
    {
      m_avgsam /= m_samni;

      WDL_ASSERT(m_avgsam <= 1.0f);
      WDL_ASSERT(m_avgsam >= 0.0f);

      m_peak[m_peaksize++] = m_avgsam;

      WDL_ASSERT(m_peaksize <= m_peakcapacity);

      m_samni = 0;
      m_peakstep = 0;
    }
    else
    {
      m_samni++;
      m_peakstep++;
    }
  }
}

int RSI_DataBankSlot::GetSamples(SAM *buffer, int buffer_size)
{
  WDL_MutexLock lock(&m_mutex);
  WDL_INT64 copied = 0;

  switch (m_antidotefmt)
  {
  case 0:
    {
      copied = (m_size / sizeof(short)) - (m_index / sizeof(short));
      if (buffer_size < copied) copied = buffer_size;
      pcmToDoubles(&m_data[m_index], (int)copied, 16, 1, buffer, 1);
      m_index += copied * sizeof(short);
    } break;
  case 1:
    {
      copied = (m_size / sizeof(char) * 3) - (m_index / sizeof(char) * 3);
      if (buffer_size < copied) copied = buffer_size;
      pcmToDoubles(&m_data[m_index], (int)copied, 24, 1, buffer, 1);
      m_index +=  copied * sizeof(char) * 3;
    } break;
  case 2:
    {
      copied = (m_size / sizeof(int)) - (m_index / sizeof(int));
      if (buffer_size < copied) copied = buffer_size;
      pcmToDoubles(&m_data[m_index], (int)copied, 32, 1, buffer, 1);
      m_index += copied * sizeof(int);
    } break;
  case 3:
    {
      copied = (m_size / sizeof(float)) - (m_index / sizeof(float));
      if (buffer_size < copied) copied = buffer_size;
      float *flt = (float *)&m_data[m_index];
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] = (SAM)flt[i];
      }
      m_index += copied * sizeof(float); 
    } break;
  case 4:
    {
      copied = (m_size / sizeof(double)) - (m_index / sizeof(double));
      if (buffer_size < copied) copied = buffer_size;
      double *dbl = (double *)&m_data[m_index];
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] = (SAM)dbl[i];
      }
      m_index += copied * sizeof(double);
    } break;
  }

  if (m_ebur128)
  {
    //if ((m_ebur128mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128ref) / DB2VAL(m_ebur128truepeak);
    //  }
    //}
    //else if ((m_ebur128mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128ref) / DB2VAL(m_ebur128samplepeak);
    //  }
    //}
    //else if ((m_ebur128mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128ref) / DB2VAL(m_ebur128range);
    //  }
    //}
    //else if ((m_ebur128mode & EBUR128_MODE_I) == EBUR128_MODE_I)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128gain);
    //  }
    //}

    if (!m_ebur128downwardonly)
    {
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] *= DB2VAL(m_ebur128gain);
      }
    }
    else if (m_ebur128downwardonly && WDL_DefinitelyLessThan(m_ebur128gain, 0.0))
    {
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] *= DB2VAL(m_ebur128gain);
      }
    }
  }

  m_indexrev = m_index;

  WDL_ASSERT(m_index <= m_size);
  WDL_ASSERT(m_indexrev >= WDL_INT64_CONST(0));

  return (int)copied;
}

int RSI_DataBankSlot::GetSamplesReverse(SAM *buffer, int buffer_size)
{
  WDL_MutexLock lock(&m_mutex);
  WDL_INT64 copied = 0;

  switch (m_antidotefmt)
  {
  case 0:
    {
      copied = m_indexrev / sizeof(short);
      if (buffer_size < copied) copied = buffer_size;
      m_indexrev -= copied * sizeof(short);
      pcmToDoubles(&m_data[m_indexrev], (int)copied, 16, 1, buffer, 1);
    } break;
  case 1:
    {
      copied = m_indexrev / sizeof(char) * 3;
      if (buffer_size < copied) copied = buffer_size;
      m_indexrev -=  copied * sizeof(char) * 3;
      pcmToDoubles(&m_data[m_indexrev], (int)copied, 24, 1, buffer, 1);
    } break;
  case 2:
    {
      copied = m_indexrev / sizeof(int);
      if (buffer_size < copied) copied = buffer_size;
      m_indexrev -= copied * sizeof(int);
      pcmToDoubles(&m_data[m_indexrev], (int)copied, 32, 1, buffer, 1);
    } break;
  case 3:
    {
      copied = m_indexrev / sizeof(float);
      if (buffer_size < copied) copied = buffer_size;
      m_indexrev -= copied * sizeof(float); 
      float *flt = (float *)&m_data[m_indexrev];
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] = (SAM)flt[i];
      }
    } break;
  case 4:
    {
      copied = m_indexrev / sizeof(double);
      if (buffer_size < copied) copied = buffer_size;
      m_indexrev -= copied * sizeof(double);
      double *dbl = (double *)&m_data[m_indexrev];
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] = (SAM)dbl[i];
      }
    } break;
  }

  const int nch = m_channels;
  const int sz = (int)copied;

  for (int ch = 0; ch < nch; ch++)
  {
    for (int lo = ch, hi = sz - nch + ch; lo < hi; lo += nch, hi -= nch)
    {
      SAM tmp = buffer[lo];
      buffer[lo] = buffer[hi];
      buffer[hi] = tmp;
    }
  }

  if (m_ebur128)
  {
    //if ((m_ebur128mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128ref) / DB2VAL(m_ebur128truepeak);
    //  }
    //}
    //else if ((m_ebur128mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128ref) / DB2VAL(m_ebur128samplepeak);
    //  }
    //}
    //else if ((m_ebur128mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128ref) / DB2VAL(m_ebur128range);
    //  }
    //}
    //else if ((m_ebur128mode & EBUR128_MODE_I) == EBUR128_MODE_I)
    //{
    //  for (int i = 0; i < (int)copied; i++)
    //  {
    //    buffer[i] *= DB2VAL(m_ebur128gain);
    //  }
    //}

    if (!m_ebur128downwardonly)
    {
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] *= DB2VAL(m_ebur128gain);
      }
    }
    else if (m_ebur128downwardonly && WDL_DefinitelyLessThan(m_ebur128gain, 0.0))
    {
      for (int i = 0; i < (int)copied; i++)
      {
        buffer[i] *= DB2VAL(m_ebur128gain);
      }
    }
  }

  m_index = m_indexrev;

  WDL_ASSERT(m_indexrev >= WDL_INT64_CONST(0));
  WDL_ASSERT(m_index <= m_size);

  return (int)copied;
}

bool RSI_DataBankSlot::IsEBUR128Enabled() const { return m_ebur128; }
int RSI_DataBankSlot::GetEBUR128Mode() const { return m_ebur128mode; }
double RSI_DataBankSlot::GetEBUR128Reference() const { return m_ebur128ref; }
double RSI_DataBankSlot::GetEBUR128Momentary() const { return m_ebur128momentary; }
double RSI_DataBankSlot::GetEBUR128Shortterm() const { return m_ebur128shortterm; }
double RSI_DataBankSlot::GetEBUR128Integrated() const { return m_ebur128integrated; }
double RSI_DataBankSlot::GetEBUR128Range() const { return m_ebur128range; }
double RSI_DataBankSlot::GetEBUR128SamplePeak() const { return m_ebur128samplepeak; }
double RSI_DataBankSlot::GetEBUR128TruePeak() const { return m_ebur128truepeak; }
double RSI_DataBankSlot::GetEBUR128Gain() const { return m_ebur128gain; }
bool RSI_DataBankSlot::IsEBUR128DowndwardOnly() const { return m_ebur128downwardonly; }

void RSI_DataBankSlot::GetWaveformA(LICE_IBitmap **bitmap, int *peak_points) const
{
  //WDL_MutexLock lock(&m_mutex);
  if (!IsPrepared())
  {
    *bitmap = NULL; *peak_points = 0; return;
  }

  *bitmap = m_waveforma;
  *peak_points = m_peakpoints;
}

void RSI_DataBankSlot::GetWaveformB(LICE_IBitmap **bitmap, int *peak_points) const
{
  //WDL_MutexLock lock(&m_mutex);
  if (!IsPrepared())
  {
    *bitmap = NULL; *peak_points = 0; return;
  }

  *bitmap = m_waveformb;
  *peak_points = m_peakpoints;
}

void RSI_DataBankSlot::SetWaveformA(int width, int height)
{
  WDL_MutexLock lock(&m_mutex);

  if (!IsPrepared()) return;

  // zoom level: 100% 50% 33% 25% 20% 16% 14% 12% 11% 10%
  //float zoom = 1.0f;

  int waveformwidth = (int)((floor(m_length) + 1.0) * m_peakpoints);

  if (waveformwidth != m_waveforma->getWidth())
  {
    m_waveforma->resize(waveformwidth, height);
    LICE_Clear(m_waveforma, LICE_RGBA(51, 51, 51, 255));

    int lineindex = 0;
    for (WDL_INT64 i = 0; i < m_peaksize; i++)
    {
      WDL_ASSERT(lineindex <= waveformwidth);
      int lineheight = (int)(m_peak[i] * height);
      int y1 = (height - lineheight) / 2;
      int y2 = y1 + lineheight;

      LICE_Line(m_waveforma, lineindex, y1, lineindex, y2, LICE_RGBA(255, 255, 128, 255));
      lineindex++;
    }
  }
}

void RSI_DataBankSlot::SetWaveformB(int width, int height)
{
  WDL_MutexLock lock(&m_mutex);

  if (!IsPrepared()) return;

  if (width != m_waveforma->getWidth())
  {
    m_waveformb->resize(width, height);
    LICE_Clear(m_waveformb, LICE_RGBA(51, 51, 51, 255));

    int lineindex = 0;
    float avgsam = 0.0f;
    const int peakstep = (int)(m_peaksize / width);

    for (WDL_INT64 i = 0; i < m_peaksize; i += peakstep)
    {
      WDL_ASSERT(m_peak[i] >= 0.0f);

      int lineheight = (int)(m_peak[i] * height);
      int y1 = (height - lineheight) / 2;
      int y2 = y1 + lineheight;

      LICE_Line(m_waveformb, lineindex, y1, lineindex, y2, LICE_RGBA(255, 255, 128, 255));
      lineindex++;
    }
  }
}

RSI_DataBank::RSI_DataBank()
{
  int slot = 0;
  double prealloc = g_preferences->GetAntidotePreAllocate();

  while (slot < RSI_DATABANK_SLOTS)
  {
    RSI_DataBankSlot *s = new WDL_NEW RSI_DataBankSlot;
    if (!s) { delete s; Sleep(50); continue; }

    if (WDL_DefinitelyGreaterThan(prealloc, 0.0))
    {
      s->Allocate(prealloc);
    }

    m_slots.Add(s); slot++;
  }

  WDL_ASSERT(m_slots.GetSize() == RSI_DATABANK_SLOTS);
}

RSI_DataBank::~RSI_DataBank()
{
  m_slots.Empty(true);
}

RSI_DataBankSlot *RSI_DataBank::GetSlot(int index) const
{
  return m_slots.Get(index);
}

int RSI_DataBank::GetSize() const
{
  return m_slots.GetSize();
}
