#ifndef _RSI_MP3_INDEX_H_
#define _RSI_MP3_INDEX_H_

#include "WDL/wdlstring.h"
#include "WDL/fileread.h"
#include "WDL/mutex.h"
#include "WDL/queue.h"

class RSI_Mp3Index
{
protected:
  RSI_Mp3Index(const char *fn)
  {
    m_refcnt=0;
    m_numframes=m_framestart=0;
    m_framefile=0;
    m_fn.Set(fn);
    m_start_eatsamples=0;
    m_end_eatsamples=0;
    m_encodingtag=-1;
  }

public:
  ~RSI_Mp3Index()
  {
    delete m_framefile;
    m_framefile=0;
  }

  bool has_file_open() const // only signifies whether a file is open (file could be closed but index is valid)
  {
    return m_framefile != NULL;
  }

  int GetFrameCount()
  {
    return m_framefile||m_numframes==m_frameposmemcache.GetSize()?m_numframes:0;
  }

  unsigned int GetStreamStart()
  {
    return GetFramePos(0);
  }

  unsigned int GetSeekPositionForSample(WDL_INT64 splpos, int decsr, int *dumpSamples)
  {
    int framesize=(decsr < 32000 ? 576 : 1152);

    // todo: support reading/using index from tag etc
    int frame_pos = (int)(splpos / framesize) - 10; // seek ahead
    if (frame_pos < 0) frame_pos=0;
    else if (frame_pos >= GetFrameCount()) frame_pos=GetFrameCount()-1;

    *dumpSamples = (int) (splpos - (((WDL_INT64)frame_pos)*framesize));

    return GetFramePos(frame_pos);
  }

  struct mp3_metadata
  {
    double len;
    int srate, nch;
  };

  static bool quickMetadataRead(const char *fn, WDL_FileRead *fr, mp3_metadata *metadata);
  static RSI_Mp3Index *indexFromFilename(const char *fn, WDL_FileRead *fr); // fr can be NULL (causing it to open file manually)
  static void release_index(RSI_Mp3Index *idx)
  {
    WDL_MutexLock lock(&indexMutex);
    if (--idx->m_refcnt<=0)
    {
      int x;
      for (x = 0; x < g_indexes.GetSize() && g_indexes.Get(x) != idx; x ++);
      if (x < g_indexes.GetSize())
        g_indexes.Delete(x);

      delete idx;
    }
  }

private:
  unsigned int GetFramePos(int f)
  {
    WDL_MutexLock lock(&m_mutex);
    if (m_framefile && m_numframes > 0)
    {
      m_framefile->SetPosition(m_framestart + f * 4);
      unsigned char buf[4];
      if (m_framefile->Read(buf,4) == 4)
        return buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
    }
    else if (m_numframes==m_frameposmemcache.GetSize() && f>=0&&f<m_numframes) return m_frameposmemcache.Get()[f];

    return 0;
  }

  int m_refcnt;
  WDL_Mutex m_mutex;
  WDL_String m_fn;
  static WDL_Mutex indexMutex;
  static WDL_PtrList<RSI_Mp3Index> g_indexes;
  static int _sortfunc(const void *a, const void *b);

  int ReadFrameListFromCache(); // 0 if found
  void BuildFrameList(WDL_FileRead *fr, mp3_metadata *quick_length_check);
  int m_numframes;
  int m_framestart;
  WDL_FileRead *m_framefile;

  WDL_TypedQueue<unsigned int> m_frameposmemcache; // only used if !m_framefile, and then only if the mp3 is small enough

public:
  int m_start_eatsamples;
  int m_end_eatsamples;

  int m_encodingtag; // -1=unknown, 0=VBR, 1=ABR, 2=CBR
};

#endif // _RSI_MP3_INDEX_H_
