/*
 * audio_playlist.c: The audio playlist manager implementation
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: audio_playlist.c,v 1.2 2005/12/22 10:42:20 cm Exp $
 */

#include <stdlib.h>
#include "audio_playlist.h"

#if defined(PLUGIN_DAAP)
#  include "daap_config.h" // for NewStrDup()
#elif defined(PLUGIN_CDDA)
#  include "cdda_conf.h" // for NewStrDup()
#endif

// --- cAudioTune --------------------------------------------------------------

cAudioTune::cAudioTune(cAudioPlaylist *parent, int songid) :
  m_songIndex(0), m_songId(songid), mpo_parent(parent) {
}

cAudioTune::~cAudioTune() {
}

bool cAudioTune::operator==(const cAudioTune& tune) const {
  return m_songId == tune.GetSongId();
}

int cAudioTune::SetSongId(int songid) {
  return m_songId = songid;
}

#if defined(PLUGIN_DAAP)
const cMediaDatabaseBlob *cAudioTune::GetData(void) {
  if (0 == m_songIndex)
    m_songIndex = mpo_parent->Database()->FindIndex(dfId, m_songId);

  if (-1 != m_songIndex)
    return mpo_parent->Database()->Item(m_songIndex)->GetData();

  return NULL;
}
#elif defined(PLUGIN_CDDA)
cCddaTrack *cAudioTune::GetData(void) {
  return mpo_parent->Disc()->Tracks()->Item(m_songId);
}
#endif

const char *cAudioTune::GetFormat(void) {
#if defined(PLUGIN_DAAP)
  if (0 == m_songIndex)
    m_songIndex = mpo_parent->Database()->FindIndex(dfId, m_songId);

  if (-1 != m_songIndex)
    return mpo_parent->Database()->Item(m_songIndex)->GetSongFormat();

  return NULL;
#elif defined(PLUGIN_CDDA)
  return "CDA";
#endif
}

// --- cAudioPlaylist ----------------------------------------------------------

#if defined(PLUGIN_DAAP)
cAudioPlaylist::cAudioPlaylist(cMediaDatabase *parent, const char *name, int id, bool writeable) :
#elif defined(PLUGIN_CDDA)
cAudioPlaylist::cAudioPlaylist(cCddaDisc *parent, const char *name, int id, bool writeable) :
#endif
  m_writeable(writeable), m_lock(false), mp_name(NULL), m_id(id), m_currentItem(-1),
  mpo_parent(parent), m_initialized(false) {
  if (NULL != name)
    mp_name = NewStrDup(name);
}

cAudioPlaylist::~cAudioPlaylist() {
  delete [] mp_name;
}

void cAudioPlaylist::Load() {
  m_initialized = true;
}

const char *cAudioPlaylist::SetName(const char *name) {
  if (NULL == name || ! m_writeable)
    return NULL;

  if (NULL != mp_name)
    delete [] mp_name;

  return mp_name = NewStrDup(name);
}

int cAudioPlaylist::SetId(int id) {
  if (m_writeable)
    m_id = id;

  return m_id;
}

cAudioTune *cAudioPlaylist::Current(void) {
  if (! m_initialized)
    Load();

  if (-1 != m_currentItem)
    return mo_items.Item(m_currentItem);

  return NULL;
}

// stores the current track; -1 is used to signal not initialized/
// reached the end of the playlist
int cAudioPlaylist::SetCurrent(int current) {
  if (! m_initialized)
    Load();

  if ((0 <= current && current < mo_items.Count()) || -1 == current)
    return m_currentItem = current;
  else
    return m_currentItem;
}

bool cAudioPlaylist::Lock(void) {
  return m_lock = true;
}

bool cAudioPlaylist::Unlock(void) {
  return m_lock = false;
}

int cAudioPlaylist::Count(void) {
  if (! m_initialized)
    Load();

  return mo_items.Count();
}

cAudioTune *cAudioPlaylist::Item(unsigned int index) {
  if (! m_initialized)
    Load();

  return mo_items.Item(index);
}

// returns pointer to added item
// will take overship of item
cAudioTune *cAudioPlaylist::Add(cAudioTune *item) {
  if (m_writeable && NULL != item) {
    if (! m_initialized)
      Load();

    return mo_items.Add(item);
  }

  return NULL;
}

// returns -1 when cannot delete item, otherwise new numOfItems
int cAudioPlaylist::Delete(int index) {
  int rval = 0;

  if (! m_initialized)
    Load();

  if (m_writeable && (! m_lock || (m_lock && index > m_currentItem))) {
    rval = mo_items.Delete(index);

    if (-1 != rval && m_currentItem == mo_items.Count()) // numOfItems is already decr by 1
      m_currentItem--;
  }

  return rval;
}

// returns -1 when cannot move item, otherwise new position
int cAudioPlaylist::Move(int dest, int src) {
  int rval = -1;

  if (! m_initialized)
    Load();

  if (m_writeable && (! m_lock || (m_lock && dest > m_currentItem && src > m_currentItem))) {
    rval = mo_items.Move(dest, src);

    if (-1 != rval && m_currentItem == src)
      m_currentItem = dest;
  }

  return rval;
}

bool cAudioPlaylist::Clear(void) {
  bool rval = false;

  if (m_writeable && ! m_lock) {
    rval = mo_items.Clear();
    m_currentItem = -1;
  }

  return rval;
}

int cAudioPlaylist::Contains(const cAudioTune *tune) const {
  for (int i = 0; i < mo_items.Count(); i++)
    if (*(mo_items.Item(i)) == *tune)
      return i;

  return -1;
}

int cAudioPlaylist::Contains(int songId) {
  cAudioTune o_tune(this, songId);
  return Contains(&o_tune); 
}

#ifndef WITHOUT_PRELOADER

// --- cAudioTunePreloader -----------------------------------------------------

cAudioTunePreloader::cAudioTunePreloader() : cThread("DAAP TunePreloader"),
  m_ready(false), m_numOfGets(0), mpo_tune(NULL), mpo_blob(NULL) {
  Start();
}

cAudioTunePreloader::~cAudioTunePreloader() {
  Clear();

  Cancel(2);
}

void cAudioTunePreloader::Action(void) {
#ifdef DEBUG
  printf("%.16s (%d) starting preloader\n", __FILE__, __LINE__);
#endif

  while (Running()) {
    if (NULL != mpo_tune && ! m_ready) {
      LOCK_THREAD;

      mpo_blob = mpo_tune->GetData();

      // m_ready _must_ be set true, even when we cannot recieve any data
      // otherwise the player will not skip the song
      m_ready = true;
    } else
      cCondWait::SleepMs(500);
  }

#ifdef DEBUG
  printf("%.16s (%d) stopping preloader\n", __FILE__, __LINE__);
#endif
}

void cAudioTunePreloader::Clear(void) {
  LOCK_THREAD;

  m_ready = false;
  mpo_tune = NULL;

  if (0 == m_numOfGets && NULL != mpo_blob)
    delete mpo_blob;
  else
    m_numOfGets = 0;

  mpo_blob = NULL;
}

void cAudioTunePreloader::SetItem(cAudioTune *tune) {
  Clear();

  mpo_tune = tune;
}

const cMediaDatabaseBlob *cAudioTunePreloader::GetData() {
  LOCK_THREAD;

  if (m_ready && NULL != mpo_blob) {
    m_numOfGets++;

    return mpo_blob;
  }

  return NULL;
}

#endif

#if defined(PLUGIN_DAAP)

// --- cAudioPlaylists ---------------------------------------------------------

cAudioPlaylists::cAudioPlaylists(cMediaDatabase *parent) :
  mpo_parent(parent) {
}

cAudioPlaylists::~cAudioPlaylists() {
}

int cAudioPlaylists::Contains(cAudioPlaylist *list) const {
  if (NULL != list) {
    for (int i = 0; i < mo_pls.Count(); i++)
      if (list->GetId() == mo_pls.Item(i)->GetId())
        return i;
  }

  return -1;
}

#endif
