
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/stat.h>

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

#include "playlist_engine_factory.h"
#include "file_catalog.h"

#include "playlist_manager.h"

/* NAME_MAX, quizzically, isn't everywhere. -aleigh */
#ifndef NAME_MAX
#define NAME_MAX 255
#endif

struct s_PlaylistManager
{
    lstTlist *playlists;

    FileCatalog *fileCatalog;

    PlaylistEngineFactory *playlistFactory;
};

static void playlist_manager_destroy_engine (void *ptr);


PlaylistManager *
playlist_manager_new (void)
{
    PlaylistManager *instance = (PlaylistManager *) malloc (sizeof (PlaylistManager));

    instance->playlists = lstFlist_create (playlist_manager_destroy_engine);

    instance->fileCatalog = file_catalog_new();

    instance->playlistFactory = playlist_engine_factory_new();

    return (instance);
}

void
playlist_manager_destroy (void *ptr)
{
    PlaylistManager *instance = (PlaylistManager *) ptr;

    lstFlist_free (instance->playlists);

    playlist_engine_factory_destroy (instance->playlistFactory);

    file_catalog_destroy (instance->fileCatalog);

    free (instance);
}

static void
playlist_manager_destroy_engine (void *ptr)
{
    IPlaylist *playlist = (IPlaylist *) ptr;

    playlist->destroy (playlist);
}

static IPlaylist *
playlist_manager_get_engine (PlaylistManager *instance, const char *playlistName)
{
    lstTlist_elem *elem = lstFlist_head (instance->playlists);

    for (; elem; elem = lstFlist_next (elem))
    {
        IPlaylist *playlist = (IPlaylist *) lstFlist_data (elem);

        if (strcmp (playlistName, playlist->get_name (playlist)) == 0)
        {
            return (playlist);
        }
    }

    return (NULL);
}

bool
playlist_manager_add_playlist (PlaylistManager *instance, const char *playlistType, const char *playlistName)
{
    if (playlist_manager_get_engine (instance, playlistName) != NULL)
    {
        return (false);
    }

    IPlaylist *playlist = playlist_engine_factory_get_playlist (instance->playlistFactory,
            playlistType, playlistName);

    if (playlist == NULL)
    {
        return (false);
    }

    lstFlist_add_next (instance->playlists, lstFlist_tail (instance->playlists), playlist);

    return (true);
}

bool
playlist_manager_add_file (PlaylistManager *instance, const char *playlistName, const char *fileName)
{
    IPlaylist *playlist;

    playlist = playlist_manager_get_engine (instance, playlistName);

    if (playlist == NULL)
    {
        return (false);
    }

    int fileId = file_catalog_get_fileid (instance->fileCatalog, fileName);

    return (playlist->add_file (playlist, fileId));
}

bool
playlist_manager_add_filelist (PlaylistManager *instance, const char *playlistName, const char *fileName)
{
    IPlaylist *playlist;

    playlist = playlist_manager_get_engine (instance, playlistName);

    if (playlist == NULL)
    {
        return (false);
    }

    struct stat sbuf;

    if (stat (fileName, &sbuf) != 0)
    {
        logFmsg (CONT_LOG_ERROR, "mod/m3: Couldn't open file: %s", fileName);

        return (false);
    }

    FILE *fp = fopen (fileName, "r");

    if (fp == NULL)
    {
        logFmsg (CONT_LOG_ERROR, "mod/m3: Couldn't open file: %s", fileName);

        return (false);
    }

    char strbuf[NAME_MAX+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;

            /*
             * BUGBUG: We shouldn't be doing this check once we allow for
             * transports other than local disk for reading mp3's.
             */
            if (stat (strbuf, &sbuf) != 0)
            {
                logFmsg (CONT_LOG_ERROR, "mod/m3: No such file or directory: %s", strbuf);
                continue;
            }

            int fileId = file_catalog_get_fileid (instance->fileCatalog, strbuf);

            playlist->add_file (playlist, fileId);
        }
    }

    fclose (fp);

    return (true);
}

FileEntry *
playlist_manager_get_next_file_entry (PlaylistManager *instance)
{
    lstTlist_elem *elem = lstFlist_head (instance->playlists);

    for (; elem; elem = lstFlist_next (elem))
    {
        IPlaylist *playlist = (IPlaylist *) lstFlist_data (elem);

        int fileId = playlist->get_next (playlist);

        if (fileId != -1)
        {
            return (file_catalog_get_file_entry (instance->fileCatalog, fileId));
        }
    }

    return (NULL);
}


