/*
 * libkarma/playlist.c
 *
 * Copyright (c) Frank Zschockelt <libkarma@freakysoft.de> 2005
 * Copyright (c) Keith Bennett <keith@mcs.st-and.ac.uk> 2006
 *
 * You may distribute and modify this program under the terms of 
 * the GNU GPL, version 2 or later.
 *
 */
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#include "lkarma.h"
#include "properties.h"
#include "playlist.h"
#include "util.h"

uint32_t get_playlist_fid(char * name);

char * lk_playlist_escape(char * data, unsigned int length)
{
    char * str;
    unsigned int i, count;
    
    str=malloc(length*5+1);
    for(i=0,count=0; i < length; i++, count++){
        if(data[i]=='\\'){
            str[count]='\\';
            str[++count]='\\';
        } else if(data[i]=='\n'){
            str[count]='\\';
            str[++count]='n';
        } else if(data[i]>=32 || data[i]<0) str[count]=data[i];
        else {
            str[count]='\\';
            str[++count]='x';
            if((data[i]/16)<10) str[++count]=(data[i]/16)+'0';
            else str[++count]=(data[i]/16)+55;
            if((data[i]%16)<10) str[++count]=(data[i]%16)+'0';
            else str[++count]=(data[i]%16)+55;
        }
    }
    str[count++]=0;
    str=(char *)realloc(str, count);
    return str;
}

unsigned int lk_playlist_unescape_inplace(char *str)
{
    unsigned int length;
    char *r,*w, *end;

    r=w=str;
    end=&((str)[strlen(str)]);
    
    while(r<end){
        if(*r=='\\'){
            if(*(r+1)=='n'){
                *w='\n';
                r+=2;
            }else if(*(r+1)=='\\'){
                *w='\\';
                r+=2;
            }else{
                r+=2;
                if(*r<='9') *w=(*r-'0')*16;
                else *w=(*r-55)*16;
                if(*(++r)<='9') *w+=(*r-'0');
                else *w+=(*r-55);
                r++;
            }
        }else{
            *w=*r;
            r++;
        }
        ++w;
    }
    length=w-str;
    return length;    
}

unsigned int lk_playlist_unescape(char * str, char ** data)
{
    unsigned int length;
    *data=strdup(str);
    length = lk_playlist_unescape_inplace(*data);
    *data=(char *)realloc(*data, length);
    return length;
}

uint32_t get_playlist_fid(char * name)
{
    uint32_t * tmp, ret;
    
    tmp=lk_properties_andOrSearch(EXACT|ORS, NULL, "type", "playlist");
    tmp=lk_properties_andOrSearch(EXACT|ANDS, tmp, "title", name);
    ret=*tmp;
    free(tmp);
    return ret;
}


playlist * lk_playlist_create(char * name)
{
    playlist * pl;
    uint32_t head;

    pl=malloc(sizeof(playlist));
    if(pl){
        pl->name=strdup(name);
        pl->playlist=malloc(4);
        head=lk_htorl(767);
        memcpy(pl->playlist, &head, 4);
        pl->length=4;
        pl->fid=0;
    }
    return pl;
}

playlist * lk_playlist_read(char * name)
{    
    playlist *pl;

    pl=malloc(sizeof(playlist));
    if(pl){
        pl->fid=get_playlist_fid(name);
        if(pl->fid!=0){
            if(lk_properties_get_property(pl->fid, "playlist")){
                pl->length=lk_playlist_unescape(
                    lk_properties_get_property(pl->fid, "playlist"),
                    &pl->playlist);
                pl->name=strdup(name);
            }else{
                free(pl);
                pl=lk_playlist_create(name);
                pl->fid=get_playlist_fid(name);
            }
            return pl;
        }else{
            free(pl);
            return NULL;
        }
    }else return NULL;
}

playlist * lk_playlist_fid_read(uint32_t fid)
{
    playlist *pl;
    
    if (fid==0) return NULL;
    
    pl=malloc(sizeof(playlist));
    if(pl){
        pl->fid=fid;
        if(lk_properties_get_property(pl->fid, "playlist")){
            pl->length=lk_playlist_unescape(
                lk_properties_get_property(pl->fid, "playlist"),
                &pl->playlist);
        }else{
            free(pl);
            pl=lk_playlist_create(lk_properties_get_property(fid, "title"));
            pl->fid=fid;
        }
        pl->name=strdup(lk_properties_get_property(fid, "title"));
        return pl;
    }else return NULL;
}

int lk_playlist_free(playlist ** pl)
{
    if((*pl)->name) free((*pl)->name);
    if((*pl)->playlist) free((*pl)->playlist);
    if(*pl) free(*pl);
    *pl=NULL;
    return 0;
}

int lk_playlist_set_name(playlist * pl, char * name)
{
    if(pl){
        if(pl->name)
            free(pl->name);
        pl->name=strdup(name);
        return 0;
    }else return -1;
}

char * lk_playlist_get_name(playlist * pl)
{
    return pl->name;
}

int lk_playlist_clear(playlist * pl)
{
    if(pl){
        pl->length=4;
        pl->playlist=(char *)realloc(pl->playlist, pl->length);
        return 0;
    }else return -1;
}


int lk_playlist_delete(playlist * pl, int karma)
{
    uint32_t fid;

    fid=pl->fid;
    pl->fid=0;
    return lk_karma_delete_file(karma, fid);
}

int lk_playlist_count(playlist * pl)
{
    if((pl->length-4)/8)
        return (pl->length-4)/8;
    else return 0;
}

int lk_playlist_get_item(playlist * pl, unsigned int n, uint32_t *fid,
                         uint32_t * fid_generation)
{
    *fid=*fid_generation=0;
    if(4+n*8 < pl->length){
        memcpy(fid, &pl->playlist[4+n*8], 4);
        memcpy(fid_generation, &pl->playlist[8+n*8], 4);
        *fid=lk_rtohl(*fid);
        *fid_generation=lk_rtohl(*fid_generation);
        return 0;
    }else return -1;
}

int lk_playlist_set_item(playlist * pl, unsigned int n, uint32_t fid,
                         uint32_t fid_generation)
{
    if(4+n*8 < pl->length){
        fid=lk_htorl(fid);
        memcpy(&pl->playlist[4+n*8], &fid, 4);
        fid_generation=lk_htorl(fid_generation);
        memcpy(&pl->playlist[8+n*8], &fid, 4);
        return 0;
    }else return -1;
}

int lk_playlist_append(playlist * pl, uint32_t fid, uint32_t fid_generation)
{
    if(pl){
        pl->length+=8;
        pl->playlist=(char *)realloc(pl->playlist, pl->length);
        fid=lk_htorl(fid);
        memcpy(&pl->playlist[pl->length-8], &fid, 4);
        fid_generation=lk_htorl(fid_generation);
        memcpy(&pl->playlist[pl->length-4], &fid_generation, 4);
        return 0;
    }else return -1;
}

int lk_playlist_remove(playlist * pl, unsigned int n)
{
    n=n*8;
    if(n+4<pl->length){
        memmove(&pl->playlist[4+n], &pl->playlist[12+n], pl->length-n-4);
        pl->length-=8;
        pl->playlist=(char *)realloc(pl->playlist, pl->length);
        return 0;
    }else return -1;    
}

int lk_playlist_insert(playlist * pl, unsigned int at, uint32_t fid,
                       uint32_t fid_generation)
{
    at=at*8;
    if(at+4<pl->length){
        pl->length+=8;
        pl->playlist=(char *)realloc(pl->playlist, pl->length);
        memmove(&pl->playlist[12+at], &pl->playlist[4+at], pl->length-at-12);
        return lk_playlist_set_item(pl, at/8, fid, fid_generation);
    }else return lk_playlist_append(pl, fid, fid_generation);
}

int lk_playlist_write(playlist * pl, int karma)
{
    playlist * rootpl=NULL;
    HASH * property;
    char * prop, * escaped;
    uint32_t * tmp;
    int i;
    
    if(!pl->fid)
        pl->fid=lk_properties_new_property();
    property=lk_properties_idsearch(pl->fid);
    lk_properties_set_property_hash(property, "type", "playlist");
    lk_properties_set_property_hash(property, "title", pl->name);
    lk_properties_set_property_hash(property, "length",
                                    simple_itoa(pl->length));
    escaped=lk_playlist_escape(pl->playlist, pl->length);
    lk_properties_set_property_hash(property, "playlist", escaped);
    free(escaped);
    lk_karma_write_file_chunk(karma, 0, 0, pl->fid, 0, NULL);
    prop=lk_properties_export(pl->fid);
    lk_karma_update_file_details(karma, pl->fid, prop);
    free(prop);
    
    rootpl=lk_playlist_read(lk_properties_get_property(256, "title"));
    lk_playlist_clear(rootpl);
    tmp=lk_properties_andOrSearch(EXACT|ORS, NULL, "type", "playlist");
    for(i=0;tmp[i]!=0;i++){
        if(tmp[i]!=256)
            lk_playlist_append(rootpl, tmp[i], 0);
    }
    free(tmp);
    escaped=lk_playlist_escape(rootpl->playlist, rootpl->length);
    property=lk_properties_idsearch(rootpl->fid);
    lk_properties_set_property_hash(property, "playlist", escaped);
    free(escaped);
    lk_properties_set_property_hash(property, "length",
                                    simple_itoa(rootpl->length));
    prop=lk_properties_export(rootpl->fid);
    lk_karma_update_file_details(karma, rootpl->fid, prop);
    free(prop);
    lk_playlist_free(&rootpl);
    return 0;
}
