//autoqueue.c:

/*
 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009-2014
 *
 *  This file is part of RoarAudio PlayList Daemon,
 *  a playlist management daemon for RoarAudio.
 *  See README for details.
 *
 *  This file is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3
 *  as published by the Free Software Foundation.
 *
 *  RoarAudio is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#include "rpld.h"

static inline int __autoqueue(pli_t queue, struct rpld_playlist * pl) {
 struct rpld_playlist_entry   * plent;
 struct rpld_playlist_entry   * copy;
 struct rpld_playlist_search    pls;
 struct rpld_playlist_pointer * plp = NULL, * plp_copy = NULL;
 int                            update_current = 0;

 if ( rpld_pointer_is_set(POINTER_CURRENT, queue, -1) == 0 ) {
  ROAR_DBG("autoqueue(void): no current pointer");

  plp = rpld_pointer_get(POINTER_DEFAULT, queue, -1);
  if ( plp != NULL ) {
   ROAR_DBG("autoqueue(void): we have a default pointer at %p", plp);

   if ( rpld_plp_copy(&plp_copy, plp) == -1 ) {
    ROAR_DBG("autoqueue(void): copy of default pointer to current pointer failed");
    return -1;
   }

   rpld_pointer_set(POINTER_CURRENT, queue, -1, plp_copy);

   rpld_plp_unref(plp);
   rpld_plp_unref(plp_copy);

#ifdef DEBUG
   if ( rpld_pointer_is_set(POINTER_CURRENT, queue, -1) == 0 ) {
    ROAR_DBG("autoqueue(void): still no current pointer");
   }
  } else {
   ROAR_DBG("autoqueue(void): no default pointer");
#endif
  }
 }

 plp = rpld_pointer_get(POINTER_CURRENT, queue, -1);
 if ( plp != NULL ) {
  ROAR_DBG("autoqueue(void): we have a current pointer at %p", plp);
  update_current = 1;
 }

 if ( plp == NULL ) {
  plp = rpld_pointer_get(POINTER_FALLBACK, queue, -1);

#ifdef DEBUG
  if ( plp != NULL ) {
   ROAR_DBG("autoqueue(void): we have a fallback pointer at %p", plp);
  }
#endif
 }

 if ( plp == NULL ) {
  ROAR_DBG("autoqueue(void): No pointers left to try. giving up.");
  ROAR_DBG("autoqueue(void) = -1");
  return -1;
 }

 if ( (plent = rpld_plp_search(plp)) == NULL ) {
  rpld_plp_unref(plp);
  return -1;
 }

 if ( (copy = rpld_ple_copy(plent)) == NULL ) {
  rpld_plp_unref(plp);
  return -1;
 }

 rpld_pl_push(pl, copy);

 if ( update_current                                 &&
      !( plp->pls.type == RPLD_PL_ST_RANDOM          ||
         plp->pls.type == RPLD_PL_ST_RANDOM_LIKENESS ) ) {
         
  if ( rpld_pointer_set(POINTER_CURRENT, queue, -1, NULL) == 0 ) {

   if ( plent->list.next != NULL ) {
    plent = plent->list.next;

    memset(&pls, 0, sizeof(pls));

    pls.type = RPLD_PL_ST_UUID;

    memcpy(pls.subject.uuid, plent->uuid, sizeof(pls.subject.uuid));

    plp_copy = rpld_plp_init(NULL, &pls);
    rpld_pointer_set(POINTER_CURRENT, queue, -1, plp_copy);
    rpld_plp_unref(plp_copy);
   }
  }
 }

 rpld_plp_unref(plp);
 return 0;
}

static inline struct rpld_playlist_entry * __get_fallback(pli_t queue) {
 struct rpld_playlist_entry   * plent;
 struct rpld_playlist_pointer * plp = NULL;
 int err;

 plp = rpld_pointer_get(POINTER_FALLBACK, queue, -1);
 if ( plp == NULL )
  return NULL;

 plent = rpld_plp_search(plp);

 err = roar_error;
 rpld_plp_unref(plp);
 roar_err_set(err);

 return plent;
}

static inline void __go_back_in_time(pli_t queue, struct rpld_playlist * pl, struct rpld_playlist_entry ** plep, time_t now) {
 struct rpld_playlist_entry * ple = *plep;

 (void)pl;

 // keep everything that is up to 5 secs too early.
 if ( (ple->wallclock_start - 5) <= now )
  return;

 // TODO: Implement a way to upmove the CURRENT Pointer.
 // This is a bit harder as we need to detect the inital point.
 // That is doable in case it was inited using the DEFAULT pointer.
 // But what about manually set pointer or other kind of pointer operations?

 // for now we ignore that case and skip to the next try.

 // if there is nothing else left we can still try the FALLBACK pointer before we keep the current title.

 ple = __get_fallback(queue);
 if ( ple != NULL ) {
  rpld_ple_free(*plep);
  *plep = ple;
  return;
 }

 // ok, nothing left. We just return and keep the current one.
}

int autoqueue(pli_t queue) {
 struct rpld_playlist       * pl = rpld_pl_get_by_id(queue);
 struct rpld_playlist_entry * ple = NULL;
 time_t now = RPLD_PLE_WALLCLOCK_INVALID;
 int ret = 0;
 int err;

 ROAR_DBG("autoqueue(void) = ?");

 if ( pl == NULL ) {
  ROAR_DBG("autoqueue(void) = -1");
  return -1;
 }

 while (ret == 0 && ple == NULL) {
  ret = __autoqueue(queue, pl);
  err = roar_error;
  if ( pl != NULL )
   ple = rpld_pl_pop(pl);
  if ( ple == NULL ) {
   err = roar_error;
   ret = -1;
  } else if ( ple->wallclock_start == RPLD_PLE_WALLCLOCK_INVALID ) {
   break;
  } else {
   if ( now == RPLD_PLE_WALLCLOCK_INVALID )
    now = time(NULL);

   if ( ((ple->wallclock_start + ple->length) < now) && !(ple->flags & RPLD_PLE_FLAG_UNSKIPABLE) ) {
    rpld_ple_free(ple);
    ple = NULL;
    continue;
   } else if ( ple->wallclock_start > now ) {
    __go_back_in_time(queue, pl, &ple, now);
   } else {
    break;
   }
  }
 }

 if ( ple != NULL )
  rpld_pl_push(pl, ple);

 rpld_pl_unref(pl);

 roar_err_set(err);
 return ret;
}

//ll
