/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 by wavey@wavey.org
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include <stdio.h>
#include <malloc.h>

#include <stdlib.h>
#include <string.h>
#include "playlist.h"
#include <file.h>
#include "sprintf.h"
#include "debug.h"
#include "mpeg.h"
#include "lcd.h"
#include "kernel.h"
#include "settings.h"
#include "status.h"

#ifdef HAVE_LCD_BITMAP
#define LINE_Y      (global_settings.statusbar&&statusbar_enabled?1:0) /* Y position the entry-list starts at */
#else /* HAVE_LCD_BITMAP */
#define LINE_Y      0 /* Y position the entry-list starts at */
#endif /* HAVE_LCD_BITMAP */

playlist_info_t playlist;

#define PLAYLIST_BUFFER_SIZE (MAX_PATH*200)

unsigned char playlist_buffer[PLAYLIST_BUFFER_SIZE];
static int playlist_end_pos = 0;

char now_playing[MAX_PATH+1];

void playlist_clear(void)
{
    playlist_end_pos = 0;
    playlist_buffer[0] = 0;
}

int playlist_add(char *filename)
{
    int len = strlen(filename);

    if(len+2 > PLAYLIST_BUFFER_SIZE - playlist_end_pos)
        return -1;

    strcpy(&playlist_buffer[playlist_end_pos], filename);
    playlist_end_pos += len;
    playlist_buffer[playlist_end_pos++] = '\n';
    playlist_buffer[playlist_end_pos] = '\0';
    return 0;
}

char* playlist_next(int steps)
{
    int seek;
    int max;
    int fd;
    int i;
    char *buf;
    char dir_buf[MAX_PATH+1];
    char *dir_end;

    playlist.index = (playlist.index+steps) % playlist.amount;
    seek = playlist.indices[playlist.index];

    if(playlist.in_ram)
    {
        buf = playlist_buffer + seek;
        max = playlist_end_pos - seek;
    }
    else
    {
        fd = open(playlist.filename, O_RDONLY);
        if(-1 != fd)
        {
            buf = playlist_buffer;
            lseek(fd, seek, SEEK_SET);
            max = read(fd, buf, MAX_PATH);
            close(fd);
        }
        else
            return NULL;
    }
    

    /* Zero-terminate the file name */
    seek=0;
    while((buf[seek] != '\n') &&
          (buf[seek] != '\r') &&
          (seek < max))
        seek++;

    /* Now work back killing white space */
    while((buf[seek-1] == ' ') || 
          (buf[seek-1] == '\t'))
        seek--;

    buf[seek]=0;
      
    /* replace backslashes with forward slashes */
    for ( i=0; i<seek; i++ )
        if ( buf[i] == '\\' )
            buf[i] = '/';

    if('/' == buf[0]) {
        strcpy(now_playing, &buf[0]);
        return now_playing;
    }
    else {
        strncpy(dir_buf, playlist.filename, playlist.dirlen);
        dir_buf[playlist.dirlen] = 0;

        /* handle dos style drive letter */
        if ( ':' == buf[1] ) {
            strcpy(now_playing, &buf[2]);
            return now_playing;
        }
        else if ( '.' == buf[0] && '.' == buf[1] && '/' == buf[2] ) {
            /* handle relative paths */
            seek=3;
            while(buf[seek] == '.' &&
                  buf[seek+1] == '.' &&
                  buf[seek+2] == '/')
                seek += 3;
            for (i=0; i<seek/3; i++) {
                dir_end = strrchr(dir_buf, '/');
                if (dir_end)
                    *dir_end = '\0';
                else
                    break;
            }
            snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[seek]);
            return now_playing;
        }
        else if ( '.' == buf[0] && '/' == buf[1] ) {
            snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[2]);
            return now_playing;
        }
        else {
            snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, buf);
            return now_playing;
        }
    }
}

void play_list(char *dir, char *file, int start_index)
{
    char *sep="";
    int dirlen;

    empty_playlist();

    playlist.index = start_index;

    /* If file is NULL, the list is in RAM */
    if(file) {
        lcd_clear_display();
        lcd_puts(0,LINE_Y,"Loading...");
        status_draw();
        lcd_update();
        playlist.in_ram = false;
    } else {
        /* Assign a dummy filename */
        file = "";
        playlist.in_ram = true;
    }

    dirlen = strlen(dir);

    /* If the dir does not end in trailing slash, we use a separator.
       Otherwise we don't. */
    if('/' != dir[dirlen-1]) {
        sep="/";
        dirlen++;
    }
    
    playlist.dirlen = dirlen;

    snprintf(playlist.filename, sizeof(playlist.filename),
             "%s%s%s", 
             dir, sep, file);

    /* add track indices to playlist data structure */
    add_indices_to_playlist();
    
    if(global_settings.playlist_shuffle) {
        if(!playlist.in_ram) {
            lcd_puts(0,LINE_Y,"Shuffling...");
            status_draw();
            lcd_update();
        }
        randomise_playlist( current_tick );
    }

    if(!playlist.in_ram) {
        lcd_puts(0,LINE_Y,"Playing...  ");
        status_draw();
        lcd_update();
    }
    /* also make the first song get playing */
    mpeg_play(playlist_next(0));
}

/*
 * remove any filename and indices associated with the playlist
 */
void empty_playlist(void)
{
    playlist.filename[0] = '\0';
    playlist.index = 0;
    playlist.amount = 0;
}

/*
 * calculate track offsets within a playlist file
 */
void add_indices_to_playlist(void)
{
    int nread;
    int fd = -1;
    int i = 0;
    int store_index = 0;
    int count = 0;
    int next_tick = current_tick + HZ;

    unsigned char *p = playlist_buffer;
    char line[16];

    if(!playlist.in_ram) {
        fd = open(playlist.filename, O_RDONLY);
        if(-1 == fd)
            return; /* failure */
    }

    store_index = 1;

    while(1)
    {
        if(playlist.in_ram) {
            nread = playlist_end_pos;
        } else {
            nread = read(fd, playlist_buffer, PLAYLIST_BUFFER_SIZE);
            /* Terminate on EOF */
            if(nread <= 0)
                break;
        }
        
        p = playlist_buffer;

        for(count=0; count < nread; count++,p++) {

            /* Are we on a new line? */
            if((*p == '\n') || (*p == '\r'))
            {
                store_index = 1;
            } 
            else if((*p == '#') && store_index)
            {
                /* If the first character on a new line is a hash
                   sign, we treat it as a comment. So called winamp
                   style playlist. */
                store_index = 0;
            }
            else if(store_index) 
            {
                
                /* Store a new entry */
                playlist.indices[ playlist.amount ] = i+count;
                playlist.amount++;
                if ( playlist.amount >= MAX_PLAYLIST_SIZE ) {
                    if(!playlist.in_ram)
                        close(fd);
                    return;
                }

                store_index = 0;
                /* Update the screen if it takes very long */
                if(!playlist.in_ram) {
                    if ( current_tick >= next_tick ) {
                        next_tick = current_tick + HZ;
                        snprintf(line, sizeof line, "%d files",
                                 playlist.amount);
                        lcd_puts(0,LINE_Y+1,line);
                        status_draw();
                        lcd_update();
                    }
                }
            }
        }

        i+= count;

        if(playlist.in_ram)
            break;
    }
    if(!playlist.in_ram) {
        snprintf(line, sizeof line, "%d files", playlist.amount);
        lcd_puts(0,LINE_Y+1,line);
        status_draw();
        lcd_update();
        close(fd);
    }
}

/*
 * randomly rearrange the array of indices for the playlist
 */
void randomise_playlist( unsigned int seed )
{
    int count;
    int candidate;
    int store;
    
    /* seed with the given seed */
    srand( seed );

    /* randomise entire indices list */
    for(count = playlist.amount - 1; count >= 0; count--)
    {
        /* the rand is from 0 to RAND_MAX, so adjust to our value range */
        candidate = rand() % (count + 1);

        /* now swap the values at the 'count' and 'candidate' positions */
        store = playlist.indices[candidate];
        playlist.indices[candidate] = playlist.indices[count];
        playlist.indices[count] = store;
    }
}

static int compare(const void* p1, const void* p2)
{
    int* e1 = (int*) p1;
    int* e2 = (int*) p2;

    return *e1 - *e2;
}

/*
 * sort the array of indices for the playlist
 */
void sort_playlist(void)
{
    if (playlist.amount > 0)
    {
        qsort(&playlist.indices, playlist.amount, sizeof(playlist.indices[0]), compare);
    }
}

/* -----------------------------------------------------------------
 * local variables:
 * eval: (load-file "../firmware/rockbox-mode.el")
 * end:
 */
