
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <glob.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "../mecha/mecha.h"

#include "playlist.h"
#include "queue_playlist_engine.h"
#include "order_playlist_engine.h"
#include "randomlist_playlist_engine.h"

static lstTlist *g_playlists;
static lstTlist *g_engines;

static playlist * get_playlist (const char *name);

void
playlist_init()
{
   g_playlists = lstFlist_create (playlist_destroy);
   g_engines = lstFlist_create (playlist_engine_destroy);

#if 0
   lstFlist_add_next (g_engines, lstFlist_tail (g_engines), 
           randomlist_playlist_engine_init());

   lstFlist_add_next (g_engines, lstFlist_tail (g_engines), 
           queue_playlist_engine_init());

   lstFlist_add_next (g_engines, lstFlist_tail (g_engines), 
           order_playlist_engine_init());
#endif
}

void
playlist_destroy (void *ptr)
{
   playlist *p = ptr;

   free (p->name);
   if (p->len)
      free (p->list);

   lstFlist_free (p->filelist);

   free (p);
}

void
playlist_engine_destroy (void *ptr)
{
   playlist_engine *p = ptr;

   free (p->name);

   free (p);
}

static playlist_engine *
get_playlist_engine_by_name (const char *name)
{
   lstTlist_elem *el;

   for (el = lstFlist_head (g_engines); el; el = lstFlist_next (el))
   {
      playlist_engine *e = (playlist_engine *) lstFlist_data (el);

      if (!strcmp (name, e->name))
	 return (e);
   }
   
   return (0);
}

static playlist_engine *
get_playlist_engine_by_type (int type)
{
   lstTlist_elem *el;

   for (el = lstFlist_head (g_engines); el; el = lstFlist_next (el))
   {
      playlist_engine *e = (playlist_engine *) lstFlist_data (el);

      if (e->type == type)
	 return (e);
   }
   
   return (0);
}

int
playlist_create (const char *name, const char *type)
{
   playlist *p;
   playlist_engine *e;
   
   if (get_playlist (name))
      return (1);

   if (!(e = get_playlist_engine_by_name (type)))
      return (2);

   p = (playlist *) malloc (sizeof (playlist));

   p->name = strFcopy (name);

   p->len = 0;

   p->type = e->type;
   p->filelist = lstFlist_create (file_entry_destroy);

   lstFlist_add_next (g_playlists, lstFlist_tail (g_playlists), p);

   return (0);
}

int
playlist_delete (const char *name)
{
   lstTlist_elem *el;

   for (el = lstFlist_head (g_playlists); el; el = lstFlist_next (el))
   {
      playlist *p = lstFlist_data (el);
      if (strcmp (name, p->name) == 0)
      {
         lstFlist_remove (g_playlists, el, NULL);
         playlist_destroy (p);
         return (0);
      }
   }

   return (1);
}

playlist *
get_playlist (const char *name)
{
   lstTlist_elem *el;

   for (el = lstFlist_head (g_playlists); el; el = lstFlist_next (el))
   {
      playlist *p = lstFlist_data (el);
      if (strcmp (name, p->name) == 0)
         return (p);
   }

   return (0);
}

int
playlist_add_file (const char *playlistname, const char *file)
{
   playlist *p;
   playlist_engine *pe;
   struct stat sbuf;

   if (!(p = get_playlist (playlistname)))
      return (1);

   if (!(pe = get_playlist_engine_by_type (p->type)))
      return (2);

   if (!pe->add_file)
      return (3);

   if (stat (file, &sbuf) != 0)
   {
       return (-errno);
   }

   return (pe->add_file (p, file_entry_new (file)));
}

int
playlist_add_fileglob (const char *playlistname, const char *fileglob)
{
   playlist *p;
   playlist_engine *pe;
   glob_t globbuf;
   int e;
   unsigned int i;

   if (!(p = get_playlist (playlistname)))
      return (1);

   if (!(pe = get_playlist_engine_by_type (p->type)))
      return (2);

   if (!pe->add_file)
      return (3);

   if (glob (fileglob, 0, NULL, &globbuf) != 0)
   {
      e = errno;
      globfree (&globbuf);
      return (-e);
   }

   for (i = 0; i < globbuf.gl_pathc; i++)
      pe->add_file (p, file_entry_new (globbuf.gl_pathv[i]));

   globfree (&globbuf);

   return (0);
}

int
playlist_add_filelist (const char *playlistname, const char *file)
{
   playlist_engine *pe;
   playlist *p;

   if (!(p = get_playlist (playlistname)))
      return (1);

   if (!(pe = get_playlist_engine_by_type (p->type)))
      return (2);

   if (!pe->add_file)
      return (3);

   {
       struct stat sbuf;
       FILE *fp;
       char strbuf[NAME_MAX+1];

       if (stat (file, &sbuf) != 0)
           return (-errno);

       if (!(fp = fopen (file, "r")))
       {
           logFmsg (CONT_LOG_ERROR, "couldn't open file %s", file);
           fclose (fp);
           return (1);
       }

       while (fgets (strbuf, sizeof (strbuf), fp) != NULL)
       {
           char *nl;

           if (strlen (strbuf) > 0 && (nl = strpbrk (strbuf, "\n\r")))
           {
               *nl = 0;

               if (strbuf[0] == '#')
                   continue;

               if (stat (strbuf, &sbuf) != 0)
               {
                   logFmsg (CONT_LOG_ERROR, "%s: no such file or directory", strbuf);
                   continue;
               }

               pe->add_file (p, file_entry_new (strbuf));
           }
       }

       fclose (fp);
   }

   return (0);
}

int
playlist_reload_filelist (const char *playlistname, const char *file)
{
   playlist_engine *pe;
   playlist *p;

   if (!(p = get_playlist (playlistname)))
      return (1);

   if (!(pe = get_playlist_engine_by_type (p->type)))
      return (2);

   if (!pe->set_to_filelist)
      return (3);

   return (pe->set_to_filelist (p, file));
}

FileEntry *
playlist_get_next ()
{
   FileEntry *fileEntry;
   playlist *p;
   playlist_engine *pe;

   if ((p = get_playlist ("preempt")))
      if ((pe = get_playlist_engine_by_type (p->type)))
	 if (pe->get_next)
	    if ((fileEntry = pe->get_next (p)))
	       return (fileEntry);

   if ((p = get_playlist ("general")))
      if ((pe = get_playlist_engine_by_type (p->type)))
	 if (pe->get_next)
	    if ((fileEntry = pe->get_next (p)))
	       return (fileEntry);

   return (0);
}

