/*
 *   MediaMVP Server
 *
 *   (C) 2003 Dominic Morris
 *
 *   $Id: menuapp.c,v 1.54 2005/04/01 11:39:20 dom Exp $
 *   $Date: 2005/04/01 11:39:20 $
 *
 *
 *   Implements the application
 */



#include "program.h"


#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>

#ifdef HAVE_LIBXML2
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#endif

#if HAVE_LIBJPEG | HAVE_MAGICK
#include  "libmvpimage.h"
#endif


extern mvpinit_t init;

typedef struct {
    char   *display_name;
    char   *url;
    int     flags;
    /* For predictive input */
    char   *dialcode;
} filelist_t;

#define FILE_DIR    1
#define FILE_URL    2
#define FILE_URLDIR 4


struct _menuapp {
    dongle_t  *dongle;
    render_t  *render;
    program_t *program;
    menu_t    *menu;
    surface_t *old_sfc;
#ifdef VDR_PLUGIN
    int        Now;
    int        current_position;
#endif
    char      *current_media;
    int        num_next_media;
    char     **next_media;
    char       paused;
};


typedef struct {
    menuapp_t  *menuapp;
    char       *path;
    char       *title;
#ifdef VDR_PLUGIN
    int        level;
#endif
    /* For handling scanning of directories */
    int         filelist_num;
    filelist_t *filelist;
    int         currentsel;  /* Currently viewing file */
} dirlist_t;



#ifndef VDR_PLUGIN
static char         *c_livetv_file = NULL;
static char         *c_liveradio_file   = NULL;
static char        **c_mp3_dir          = NULL;
static int           c_mp3_dir_num      = 0;
static char        **c_mpeg_dir         = NULL;
static int           c_mpeg_dir_num     = 0;
static char        **c_pictures_dir     = NULL;
static int           c_pictures_dir_num = 0;
static char         *c_rss_file         = NULL;
static char         *c_font_file        = NULL;
static char          c_standard_switch  = 0;
#endif



static int           menuapp_main(menu_t *menu,void *imenuapp, int sel);
#ifndef VDR_PLUGIN
static int           menuapp_live_tv(menu_t *menu,void *imenuapp, int sel);
#endif
static int           menuapp_live_radio(menu_t *menu,void *imenuapp, int sel);
static int           menuapp_mpeg(menu_t *menu,void *imenuapp, int sel);
static int           menuapp_music(menu_t *menu,void *imenuapp, int sel);
#if HAVE_LIBJPEG | HAVE_MAGICK
static int           menuapp_pictures(menu_t *menu,void *imenuapp, int sel);
static int           menuapp_display_image(dirlist_t *dirlist, int index, int rotate);
#endif

#ifdef HAVE_LIBXML2
static int           menuapp_rss(menu_t *menu,void *imenuapp, int sel);
static int           process_rss(menu_t *menu,void *imenuapp, int sel);
static int           display_rss(menu_t *menu,void *imenuapp, int sel);
static filelist_t   *rss_scan(xmlDocPtr doc, xmlNodePtr cur, int *scanned, char **title);
#endif

static int           process_media(menu_t *menu,void *imenuapp, int sel);
static int           media_colour_keys(menu_t *menu, void *imenuapp, int key, int sel);

static int           media_keys(void *ptr, int code);
static int           media_ack(int acktype, void *param, unsigned char *buf, int len);


static int           menuapp_settings(menu_t *menu,void *imenuapp, int sel);
static int           process_settings(menu_t *menu,void *imenuapp, int sel);


static filelist_t   *channel_file_scan(char *filename,int *scanned);
#ifndef VDR_PLUGIN
static filelist_t   *arguments_scan(int argc, char *argv[],int *scanned);
#endif
static filelist_t   *directory_scan(char *path, int *scanned);
static void          directory_sort(int num,filelist_t *list);

static filelist_t   *filelist_add(filelist_t *list,int *num,char *display_name,char *url,char *dialcode,int flags);

static dirlist_t    *new_dirlist(menuapp_t *menuapp);
static void          delete_dirlist(void *dir);    /* Ahem */


static void          next_media_clear(menuapp_t *menuapp);
static void          next_media_pop(menuapp_t *menuapp);
static void          next_media_push(menuapp_t *menuapp, char *url);

static int           playlist_parse(menuapp_t *menuapp, char *path, char *name);

static char         *generate_dialcode(char *name);
static int           dial_entry(menu_t *menu, void *imenuapp, int key, int sel);

/* Include any VDR specific menu generation etc here */
#ifdef VDR_PLUGIN
#include "menuapp-vdr.c"
#endif

void menuapp_config()
{
#ifndef VDR_PLUGIN
    iniparse_add(0,"media:livetv","Name of the livetv file",OPT_STR,&c_livetv_file);
    iniparse_add(0,"media:liveradio","Name of the HTTP radio file",OPT_STR,&c_liveradio_file);
    iniparse_add_array(0,"media:mp3","Directories to search for music files",OPT_STR,&c_mp3_dir,&c_mp3_dir_num);
    iniparse_add_array(0,"media:mpeg","Directories to search for video files",OPT_STR,&c_mpeg_dir,&c_mpeg_dir_num);
    iniparse_add_array(0,"media:pictures","Directories to search for picture files",OPT_STR,&c_pictures_dir,&c_pictures_dir_num);
    iniparse_add(0,"media:rss","Name of the file containing RSS entries",OPT_STR,&c_rss_file);
    iniparse_add(0,"media:switchstandard","Whether to switch NTSC/PAL on framerate",OPT_BOOL,&c_standard_switch);
    iniparse_add(0,"gui:font","Name of the fontfile to use",OPT_STR,&c_font_file);
#endif
}


void menuapp_init()
{   

}

menuapp_t *new_menuapp(dongle_t *dongle, render_t *render, program_t *app)
{
    menuapp_t      *menuapp = (menuapp_t *)malloc(sizeof(*menuapp));

    menuapp->render = render;
    menuapp->program    = app;
    menuapp->dongle = dongle;
    menuapp->current_media = NULL;
    menuapp->num_next_media = 0;
    menuapp->next_media = NULL;
    menuapp->paused = FALSE;
    menuapp->old_sfc = NULL;

    if ( c_font_file != NULL ) {
        font_t  *font = font_load(filename_expand(c_font_file));
        if ( font != NULL ) {
            render_set_font(render,font);
        }
    }
  
    menuapp_main(NULL,menuapp,0);    

    return menuapp;
}


void delete_menuapp(menuapp_t *menuapp)
{

    delete_menu_tree(menuapp->menu);

    if ( menuapp->current_media ) {
        free(menuapp->current_media);
        menuapp->current_media = NULL;
    }

    if ( menuapp->next_media ) {
        next_media_clear(menuapp);
    }

    free(menuapp);
}




static int menuapp_main(menu_t *menu,void *imenuapp, int sel)
{   
    menuapp_t   *menuapp = (menuapp_t *)imenuapp;

    menuapp->menu = new_menu(menuapp->dongle,menuapp->render,menuapp->program,NULL,menuapp,MENU_NUMBERS);
    menu_set_title(menuapp->menu,tr("MediaMVP"));

#ifndef VDR_PLUGIN
    if ( c_livetv_file ) {
        menu_add(menuapp->menu,tr("Live TV"),menuapp_live_tv);
    }
    if ( c_liveradio_file ) {
        menu_add(menuapp->menu,tr("Internet Radio"),menuapp_live_radio);
    }
    if ( c_mpeg_dir_num ) {
        menu_add(menuapp->menu,tr("Videos"),menuapp_mpeg);
    }
    if ( c_mp3_dir_num ) {
        menu_add(menuapp->menu,tr("Music"),menuapp_music);
    }


#if HAVE_LIBJPEG | HAVE_MAGICK
    if ( c_pictures_dir_num ) {
        menu_add(menuapp->menu,tr("Pictures"),menuapp_pictures);
    }
#endif
#ifdef HAVE_LIBXML2
    if ( c_rss_file ) {
        menu_add(menuapp->menu,tr("News Feeds"),menuapp_rss);
    }
#endif
#else
    /* Add all things in since paths are defined in a config file */
    menuapp->Now = 1;
    menu_add(menuapp->menu,tr("Channels"),menuapp_channels);
    menu_add(menuapp->menu,tr("Schedules"),schedule_menuapp);
    menu_add(menuapp->menu,tr("Internet Radio"),menuapp_live_radio);
    menu_add(menuapp->menu,tr("VDR Recordings"),menuapp_vdrrecs);
#if HAVE_LIBJPEG | HAVE_MAGICK
    menu_add(menuapp->menu,tr("Pictures"),menuapp_pictures);
#endif
#ifdef HAVE_LIBXML2
    menu_add(menuapp->menu,tr("News Feeds"),menuapp_rss);
#endif
    menu_add(menuapp->menu,tr("Other Videos"),menuapp_mpeg);
    menu_add(menuapp->menu,tr("Music"),menuapp_music); 
#endif
    /* Settings are always required! */
    menu_add(menuapp->menu,tr("Settings"),menuapp_settings);
    menu_display(menuapp->menu);
    return 1;
}


/***********************
 *
 *   Handling of the Live TV/Radio Menus
 *
 ***********************/
#ifndef VDR_PLUGIN
static int menuapp_live_tv(menu_t *menu,void *imenuapp, int sel)
{
    menuapp_t     *menuapp = (menuapp_t *)imenuapp;
    dirlist_t     *dirlist;
    menu_t        *child;
    int            i;


    dirlist = new_dirlist(menuapp);
    dirlist->path = strdup("");
    dirlist->filelist = channel_file_scan(filename_expand(c_livetv_file),&dirlist->filelist_num);
    dirlist->title = strdup(tr("Live TV"));

    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,dirlist,0);
    menu_set_param_clearup(child,delete_dirlist);
    menu_set_dialcode_callback(child, dial_entry);

    menu_set_title(child,tr("Live TV")); 

    for ( i = 0; i < dirlist->filelist_num; i++ ) {
        menu_add(child,dirlist->filelist[i].display_name,process_media);
    }

    menu_display(child);
    return 1;
}
#endif



static int menuapp_live_radio(menu_t *menu,void *imenuapp, int sel)
{
    menuapp_t     *menuapp = (menuapp_t *)imenuapp;
    dirlist_t     *dirlist;
    menu_t        *child;
    int            i;

    dirlist = new_dirlist(menuapp);
    dirlist->path = strdup("");


#ifdef VDR_PLUGIN
    dirlist->filelist = channel_file_scan(filename_expand("httpradio.conf"),&dirlist->filelist_num);
#else
    dirlist->filelist = channel_file_scan(filename_expand(c_liveradio_file),&dirlist->filelist_num);
#endif
    dirlist->title = strdup(tr("Internet Radio"));

    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,dirlist,0);
    menu_set_param_clearup(child,delete_dirlist);
    menu_set_dialcode_callback(child, dial_entry);

    menu_set_title(child,tr("Internet Radio")); 

    for ( i = 0; i < dirlist->filelist_num; i++ ) {
        menu_add(child,dirlist->filelist[i].display_name,process_media);
    }

    menu_display(child);
    return 1;   
}



/*
 * Handle menus for mpeg files 
 */
static int menuapp_mpeg(menu_t *menu,void *imenuapp, int sel)
{
#ifdef VDR_PLUGIN
    char            buf[FILENAME_MAX+1];
#endif
    menuapp_t      *menuapp = (menuapp_t *)imenuapp;
    dirlist_t      *dirlist;
    menu_t         *child;
    int             i;

    dirlist = new_dirlist(menuapp);

    dirlist->title = strdup(tr("Video"));  

#ifdef VDR_PLUGIN
    dirlist->path = strdup("");

    /* Read in files used by the mplayer/mp3 plugin to determine directories */
    snprintf(buf,sizeof(buf),"%s",filename_expand("mplayersources.conf"));

    dirlist->filelist = plugin_file_scan(buf,&dirlist->filelist_num);
#else
    /* If we've only got one directory, behave in the traditional way, otherwise list our
       config directories - assuming that they exist
    */
    if ( c_mpeg_dir_num == 1 ) {      
        dirlist->path = strdup(c_mpeg_dir[0]);
        dirlist->filelist = directory_scan(c_mpeg_dir[0],&dirlist->filelist_num);
    } else {
        dirlist->filelist = arguments_scan(c_mpeg_dir_num,c_mpeg_dir,&dirlist->filelist_num);
        dirlist->path = strdup("");      
    }
#endif

    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,dirlist,0);
    menu_set_title(child,tr("Video"));   
    menu_set_param_clearup(child,delete_dirlist);
    menu_set_dialcode_callback(child, dial_entry);

    for ( i = 0; i < dirlist->filelist_num; i++ ) {
        menu_add(child,dirlist->filelist[i].display_name,process_media);             
    }
    menu_set_colour_actions(child,tr("Back"),tr("Play dir"),tr("Play list"), NULL,media_colour_keys);
    menu_display(child);
   


    return 1;  
}


/*
 *   Handle the root menu for Music menu
 */
static int menuapp_music(menu_t *menu,void *imenuapp, int sel)
{
#ifdef VDR_PLUGIN
    char            buf[FILENAME_MAX+1];
#endif
    menuapp_t      *menuapp = (menuapp_t *)imenuapp;
    dirlist_t  *dirlist;
    menu_t     *child;
    int         i;

    dirlist = new_dirlist(menuapp);

#ifdef VDR_PLUGIN
    dirlist->path = strdup("");

    /* Read in files used by the mplayer/mp3 plugin to determine directories */
    snprintf(buf,sizeof(buf),"%s", filename_expand("mp3sources.conf"));

    dirlist->filelist = plugin_file_scan(buf,&dirlist->filelist_num);
#else
    if ( c_mp3_dir_num == 1 ) {      
        dirlist->path = strdup(c_mp3_dir[0]);
        dirlist->filelist = directory_scan(c_mp3_dir[0],&dirlist->filelist_num);
    } else {
        dirlist->filelist = arguments_scan(c_mp3_dir_num,c_mp3_dir,&dirlist->filelist_num);
        dirlist->path = strdup("");      
    }
#endif
   
    dirlist->title = strdup(tr("Music"));
    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,dirlist,0);
    menu_set_title(child,tr("Music"));   
    menu_set_param_clearup(child,delete_dirlist);
    menu_set_dialcode_callback(child, dial_entry);
        
    for ( i = 0; i < dirlist->filelist_num; i++ ) {
        menu_add(child,dirlist->filelist[i].display_name,process_media);                    
    }
    menu_set_colour_actions(child,tr("Back"),tr("Play dir"),tr("Play list"), NULL, media_colour_keys);

    menu_display(child);
    return 1;
}


/*
 *   Handle the root menu for Pictures menu
 */
#if HAVE_LIBJPEG | HAVE_MAGICK
static int menuapp_pictures(menu_t *menu,void *imenuapp, int sel)
{
#ifdef VDR_PLUGIN
    char            buf[FILENAME_MAX+1];
#endif
    menuapp_t  *menuapp = (menuapp_t *)imenuapp;
    dirlist_t  *dirlist;
    menu_t     *child;
    int         i;

    dirlist = new_dirlist(menuapp);

#ifdef VDR_PLUGIN
    dirlist->path = strdup("");

    /* Read in files used by the mplayer/mp3 plugin to determine directories */
    snprintf(buf,sizeof(buf),"%s",filename_expand("picturesources.conf"));

    dirlist->filelist = plugin_file_scan(buf,&dirlist->filelist_num);
#else
    if ( c_pictures_dir_num == 1 ) {      
        dirlist->path = strdup(c_pictures_dir[0]);
        dirlist->filelist = directory_scan(c_pictures_dir[0],&dirlist->filelist_num);
    } else {
        dirlist->filelist = arguments_scan(c_pictures_dir_num,c_pictures_dir,&dirlist->filelist_num);
        dirlist->path = strdup("");      
    }
#endif
   
    dirlist->title = strdup(tr("Pictures"));
    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,dirlist,0);
    menu_set_title(child,tr("Pictures"));   
    menu_set_param_clearup(child,delete_dirlist);
    menu_set_dialcode_callback(child, dial_entry);
        
    for ( i = 0; i < dirlist->filelist_num; i++ ) {
        menu_add(child,dirlist->filelist[i].display_name,process_media);                    
    }

    menu_display(child);
    return 1;
}
#endif

#ifdef HAVE_LIBXML2
/*
 *   Handle the root menu for News feeds
 */
static int menuapp_rss(menu_t *menu,void *imenuapp, int sel)
{
    char        buf[FILENAME_MAX+1];
    menuapp_t  *menuapp = (menuapp_t *)imenuapp;
    dirlist_t  *dirlist;
    menu_t     *child;
    int         i;

    dirlist = new_dirlist(menuapp);

#ifdef VDR_PLUGIN
    snprintf(buf,sizeof(buf),"%s",filename_expand("mvprss.conf"));
#else
    snprintf(buf,sizeof(buf),"%s",filename_expand(c_rss_file));
#endif



    dirlist->filelist = channel_file_scan(buf,&dirlist->filelist_num);
    dirlist->title = strdup(tr("News Feeds"));
    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,dirlist,0);
    menu_set_title(child,tr("News Feeds"));   
    menu_set_param_clearup(child,delete_dirlist);
    menu_set_dialcode_callback(child, dial_entry);
        
    for ( i = 0; i < dirlist->filelist_num; i++ ) {
        menu_add(child,dirlist->filelist[i].display_name,process_rss);
    }

    menu_display(child);
    return 1;
}

static int process_rss(menu_t *menu,void *imenuapp, int sel)
{
    xmlDocPtr doc;
    xmlNodePtr cur, channel;
    dirlist_t *dirlist = (dirlist_t *)imenuapp;
    dirlist_t *dirlist2;
    filelist_t *entry;
    menu_t *child;
    int i;

    entry = &dirlist->filelist[sel];

    Dprintf(INFO, "RSS Entry: Name: %s\tURL: %s\n", entry->display_name, entry->url);

    doc = xmlParseFile(entry->url);
    if(doc == NULL ) {
        Dprintf(ERROR, "Feed [%s]\nNot parsed successfully\n", entry->url);
        return 0;
    }

    cur = xmlDocGetRootElement(doc);
    if(cur == NULL) {
        Dprintf(ERROR,"Empty feed\n");
        xmlFreeDoc(doc);
        return 0;
    }

    if(xmlStrcasecmp(cur->name, (const xmlChar *) "rss") &&
        xmlStrcasecmp(cur->name, (const xmlChar *) "rdf") ) {
        Dprintf(ERROR,"Feed not RSS\n");
        xmlFreeDoc(doc);
        return 0;
    }

    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
        if( xmlStrcasecmp(cur->name, (const xmlChar *) "channel") == 0 ) {

            dirlist2 = new_dirlist(dirlist->menuapp);
	    
            channel = cur->xmlChildrenNode;
            dirlist2->filelist = rss_scan(doc, channel, &dirlist2->filelist_num, &dirlist2->title);

            child = new_menu(dirlist2->menuapp->dongle,dirlist2->menuapp->render,dirlist2->menuapp->program,menu,dirlist2,0);
            menu_set_title(child,dirlist2->title);   
            menu_set_param_clearup(child,delete_dirlist);
            menu_set_dialcode_callback(child, dial_entry);
            
            for ( i = 0; i < dirlist2->filelist_num; i++ ) {
                menu_add(child,dirlist2->filelist[i].display_name,display_rss);             
            }

            menu_display(child);
            return 1;
        }

        cur = cur->next;
    }

    return 1;
}

static int display_rss(menu_t *menu,void *imenuapp, int sel)
{
    dirlist_t *dirlist = (dirlist_t *)imenuapp;
    filelist_t *entry = &dirlist->filelist[sel];
    size_t rowwidth;
    menu_t *child;
    char *url;

	if( ! entry->url) {
		return 1;
	}


	url = (char *)strdup(entry->url);

    Dprintf(DEBUG, "Item: %s\n", url);

    child = new_menu(dirlist->menuapp->dongle,dirlist->menuapp->render,dirlist->menuapp->program,menu,NULL,0);
    menu_set_title(child, entry->display_name);

    /* Split out text into rows */
    rowwidth = (render_get_width(dirlist->menuapp->render) - render_get_xoffset(dirlist->menuapp->render) * 2) / (render_get_font_width(dirlist->menuapp->render) + 1) ;
    Dprintf(DEBUG, "Render width: %d\n", render_get_width(dirlist->menuapp->render));
    Dprintf(DEBUG, "X Offset    : %d\n", render_get_width(dirlist->menuapp->render));
    Dprintf(DEBUG, "Font Width  : %d\n", render_get_font_width(dirlist->menuapp->render));
    Dprintf(DEBUG, "Row Width   : %d\n", rowwidth);

    if( strlen(entry->url) < rowwidth ) {
        menu_add(child, entry->url, NULL);
    } else {
        size_t remaining_len = strlen(url);
        char *start_ptr = url;
        char *end_ptr = url;
        while(remaining_len > 0 && *start_ptr)
        {
            while(! isalnum(*start_ptr)) {
                start_ptr++;
            }
            Dprintf(DEBUG, "111--->(%d) (%s)\n", remaining_len, start_ptr);
            end_ptr += (remaining_len < rowwidth ? remaining_len : rowwidth);
            if(remaining_len >= rowwidth) {
                while(*end_ptr != ' ') end_ptr--;
                *end_ptr++ = '\0';
            }
            menu_add(child, start_ptr, NULL);
            while(*end_ptr && *end_ptr == ' ') end_ptr++;
            start_ptr = end_ptr;
            remaining_len = strlen(start_ptr);
            Dprintf(DEBUG, "222--->(%d) (%s)\n", remaining_len, start_ptr);
        }
    }

    free(url);
    menu_display_article(child);
    return 1;
}
#endif

static int process_media(menu_t *menu,void *imenuapp, int sel)
{
    char         buf[FILENAME_MAX+1];
    dirlist_t    *dirlist = (dirlist_t *)imenuapp;
    dirlist_t    *dirlist2;
    filelist_t   *entry;
    menu_t       *child;
    int           i;
    const char    *blue_key;
    int           has_subdirs;

    menu_free_dialcode(menu);
    menu_set_dialcode_selection(menu,0);

    entry = &dirlist->filelist[sel];
    dirlist->currentsel = sel;

    if ( entry->flags & FILE_URL ) {        
        /* It's a channel entry */
        if ( dirlist->menuapp->current_media ) {        
            free(dirlist->menuapp->current_media);
        }
        dirlist->menuapp->current_media = strdup(entry->url);
        dirlist->menuapp->paused = FALSE;
        dongle_send_play(dirlist->menuapp->dongle,entry->url);
        program_register_keys(dirlist->menuapp->program,REGISTER_MEDIA,media_keys,dirlist->menuapp);
        program_register_ack(dirlist->menuapp->program,REGISTER_MEDIA,media_ack,dirlist->menuapp);
        return 1;
    } else if ( entry->flags & FILE_DIR ) {

        /* The selection was a directory - we need to create a new menu - hah! */
        dirlist2 = new_dirlist(dirlist->menuapp);

        /* Scan the dirlist */
        snprintf(buf,sizeof(buf),"%s/%s",dirlist->path,entry->url);
        dirlist2->path = strdup(buf);
        dirlist2->title = strdup(dirlist->title);
        dirlist2->filelist = directory_scan(buf,&dirlist2->filelist_num);

        child = new_menu(dirlist2->menuapp->dongle,dirlist2->menuapp->render,dirlist2->menuapp->program,menu,dirlist2,0);
        snprintf(buf,sizeof(buf),"%s - %s",dirlist->title,entry->url);
        menu_set_title(child,buf);   
        menu_set_param_clearup(child,delete_dirlist);
        menu_set_dialcode_callback(child, dial_entry);
            
        has_subdirs = 0;
        for ( i = 0; i < dirlist2->filelist_num; i++ ) {
            menu_add(child,dirlist2->filelist[i].display_name,process_media);             
            if (dirlist2->filelist[i].flags & FILE_DIR) {
                has_subdirs = 1;
            }
        }

        blue_key = NULL;
#ifdef VDR_PLUGIN
        if (!has_subdirs && is_vdr_recording(dirlist2->path)) {
            blue_key = tr("Delete dir");
        }
#endif

        menu_set_colour_actions(child,tr("Back"),tr("Play dir"),tr("Play list"), blue_key, media_colour_keys);

        menu_display(child);
        return 1; 
#ifdef VDR_PLUGIN
    } else if ( entry->flags & FILE_URLDIR ) {
        recordings_submenu(menu,dirlist->menuapp,entry->url,dirlist->level);
        return 1;
#endif       
    } else {
        int filetype;
        /* It was a file, quick get the file type */
        snprintf(buf,sizeof(buf),"%s/%s",dirlist->path,entry->url);
        filetype = file_get_type(buf); 

        /* If it's a file on disc we can interrogate the mpeg parameters
           and change the TV mode
        */
        if ( c_standard_switch && filetype == MEDIA_MPEG) {
            mpeginfo_t *info;
            int frame_rate_index;

            info = mpeg_get_info_file(buf);
            frame_rate_index = mpeg_get_rate_index(info);
            
            dirlist->menuapp->dongle->old_tvmode = dirlist->menuapp->dongle->tvmode;
            if(frame_rate_index == 2 || frame_rate_index == 3) {
                if(dirlist->menuapp->dongle->tvmode != 1) {
                    dirlist->menuapp->dongle->tvmode = 1;
                    Dprintf(DEBUG, "Switching to PAL\n");
                }
            } else {
                if(dirlist->menuapp->dongle->tvmode != 0) {
                    dirlist->menuapp->dongle->tvmode = 0;
                    Dprintf(DEBUG, "Switching to NTSC\n");
                }
            }
            if(dirlist->menuapp->dongle->tvmode != dirlist->menuapp->dongle->old_tvmode) {
                dongle_send_settings(dirlist->menuapp->dongle, RDC_SETTINGS_TEST);
            }
        }     
        if ( filetype == MEDIA_MPEG ||
             filetype == MEDIA_MP3 ) {
            snprintf(buf,sizeof(buf),"file://%s/%s",dirlist->path,entry->url);
            if ( dirlist->menuapp->current_media == NULL || dongle_get_type(dirlist->menuapp->dongle) == 0 ) {        
                if ( dirlist->menuapp->current_media )
                    free(dirlist->menuapp->current_media);
                dirlist->menuapp->current_media = strdup(buf);
                dirlist->menuapp->paused = FALSE;
                dongle_send_play(dirlist->menuapp->dongle,buf);
            } else {
                next_media_clear(dirlist->menuapp);
                next_media_push(dirlist->menuapp,buf);           
                dirlist->menuapp->paused = FALSE;
                dongle_send_message(dirlist->menuapp->dongle,RDC_STOP);    
            }
#if HAVE_MAGICK | HAVE_LIBJPEG
        } else if (  filetype & MEDIA_PICTURE ) {
            int         ret;

            ret = menuapp_display_image(dirlist,sel,0);

            printf("Dirlist is %p menu %p\n",dirlist,menu);

            if ( ret == 1 ) {
                program_register_keys(dirlist->menuapp->program,REGISTER_MEDIA,media_keys,dirlist->menuapp);
            }

            return ret;
#endif
        } else if ( filetype == 0 ) {   /* Try and grok a placelist */
            if ( playlist_parse(dirlist->menuapp,dirlist->path,entry->url) == 0 ) {
                Dprintf(INFO, "Can't understand file %s\n",entry->url);
                return 1;
            }
        } else if ( filetype == -1 ) {  /* Totally unknown/bad file */
            Dprintf(INFO, "Can't understand file %s\n",entry->url);           
            return 1;     /* Just carry on displaying the menu */
        }
        program_register_keys(dirlist->menuapp->program,REGISTER_MEDIA,media_keys,dirlist->menuapp);
        program_register_ack(dirlist->menuapp->program,REGISTER_MEDIA,media_ack,dirlist->menuapp);
    }

    return 1;     /* Carry on displaying this menu */
}

static int media_colour_keys(menu_t *menu, void *imenuapp, int key, int sel)
{
    char           buf[FILENAME_MAX+1];
    dirlist_t     *dir = (dirlist_t *)imenuapp;
    int            i;
    char           play = TRUE;
#ifdef VDR_PLUGIN
    menu_t	   *child;
#endif

    switch ( key ) {
    case keyRed:
        return 0;
    case keyGreen:       /* Add all contents of the directory */
        if ( dir->menuapp->current_media ) {
            play = FALSE;
        }
        next_media_clear(dir->menuapp);

        for ( i = 0; i < dir->filelist_num; i++ ) {
            if ( dir->filelist[i].flags == 0 ) {
                snprintf(buf,sizeof(buf),"file://%s/%s",dir->path,dir->filelist[i].url);
                next_media_push(dir->menuapp,buf);
            }
        }
        if ( dir->menuapp->num_next_media ) {
            if ( play ) {
                next_media_pop(dir->menuapp);
                dongle_send_play(dir->menuapp->dongle,dir->menuapp->current_media);
                program_register_keys(dir->menuapp->program,REGISTER_MEDIA,media_keys,dir->menuapp);
                program_register_ack(dir->menuapp->program,REGISTER_MEDIA,media_ack,dir->menuapp);
            } else {
                dongle_send_message(dir->menuapp->dongle,RDC_STOP);
            }
        }
        break;
    case keyYellow:      
        playlist_parse(dir->menuapp,dir->path,dir->filelist[sel].url);
        break;   

#ifdef VDR_PLUGIN
    case keyBlue:

        child = new_menu(dir->menuapp->dongle,dir->menuapp->render,dir->menuapp->program,menu,dir,0);
        menu_set_title(child,tr("Confirm delete"));
            
        menu_set_colour_actions(child,tr("Back"),tr("Delete"),NULL, NULL, recdel_colour_keys);

        menu_display(child);
        break;
#endif

    default:
        break;
    }

    return 1;     /* Carry on displaying this menu */
}





/***********************
 *
 *   Handling of the settings Menu
 *
 ***********************/
static int menuapp_settings(menu_t *menu,void *imenuapp, int sel)
{
    menuapp_t       *menuapp = (menuapp_t *)imenuapp;
    menu_t          *child;
    static char const     *tvmode[2];
    static char const    *flicker[4];
    static char const    *output[4];
    static char const    *aspect[]  = { "4:3", "16:9" };

    tvmode[0] = tr("NTSC");
    tvmode[1] = tr("PAL");

    flicker[0] = tr("None");
    flicker[1] = tr("Low");
    flicker[2] = tr("Medium");
    flicker[3] = tr("High");

    output[0] = tr("RGB Detected");
    output[1] = tr("SVideo");
    output[2] = tr("Composite");
    output[3] = tr("RGB/Composite");
   
    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,menuapp,0);
    menu_set_title(child,tr("Settings"));

 

    /* Add in all possible options - we'll validate things on the callback */
    menu_add_option(child,tr("TV Mode:"),process_settings,
                    MENU_INT,2,tvmode,&menuapp->dongle->tvmode);

    /* A little bit of fiddling - see rules in proces_settings */
    menu_add_option(child,tr("Output:"),process_settings,
                    MENU_INT,4,output,&menuapp->dongle->videooutput);

    menu_add_option(child,tr("Flicker Control:"),process_settings,
                    MENU_INT,4,flicker,&menuapp->dongle->flickermode);

    menu_add_option(child,tr("Aspect Ratio:"),process_settings,
                    MENU_INT,2,aspect,&menuapp->dongle->aspectratio);

    menu_add(child,tr("Cancel changes"),process_settings);
    menu_add(child,tr("Save settings (may take a while)"),process_settings);

    menu_display(child);
    return 1;
}

static int process_settings(menu_t *menu,void *imenuapp, int sel)
{
    menuapp_t   *menuapp = (menuapp_t *)imenuapp;
    /* Some rules preened from the configuration.html:

    1). For NTSC unit -- in response to GUI query, video output value 
    is always '3', so you show "Video Output" as "Composite/Svideo" no changes
    allowed !!

    2). For SCART Unit --
    a). '0' : then you show "Video Output" as "RGB Detected", No changes 
    allowed !!!
    (b). '1' : then you show "Video Output" as "SVIDEO", it can only be
    changed to RGB/COMPOSITE (value == 2)
    (c). '2': you show "Video Output" as "RGB/COMPOSITE", it can only be 
    changed to SVIDEO (value == 1)

    */

    /* If we come through with -1 then we should test the settings */
    switch ( sel ) {
    case -1:  /* If we come through with -1 then we should test the settings */
        dongle_send_settings(menuapp->dongle,RDC_SETTINGS_TEST);
        return 1;  /* Carry on displaying current menu */
    case 4:
        dongle_send_settings(menuapp->dongle,RDC_SETTINGS_CANCEL);
        return 0;
    case 5:
        dongle_send_settings(menuapp->dongle,RDC_SETTINGS_SAVE);
        return 0;
    default:
        return 0;   /* Carry on displaying */
    } 
}



/***********************
 *
 *   Generic media key and ack handling (at long last!)
 *
 ***********************/
static int media_keys(void *ptr, int code)
{
    menuapp_t     *menuapp = (menuapp_t *)ptr;
    surface_t     *sfc;
    int            streamtype = dongle_get_type(menuapp->dongle);
#ifdef VDR_PLUGIN
    int            ret;
#endif

    {
        char *name = dongle_get_streamname(menuapp->dongle);

        if ( name ) {
            printf("Media name is %s\n",name);
        }

    }
    /* If we've got an old_sfc then we're viewing a picture */
#if HAVE_LIBJPEG | HAVE_MAGICK
    if ( menuapp->old_sfc ) {
        /* Get the dirlist, this horribly breaks scope but if we're viewing
           a picture then we can't have changed menus
        */
        menu_t       *menu = menuapp->menu;
        menu_t       *child;
        dirlist_t    *dirlist;

        /* We have to find the bottom menu */
        while ( ( child = menu_child(menu) ) ) {
                menu = child;
        }

        dirlist = (dirlist_t *)menu_return_param(menu);

        printf("Dirlist2 is %p %p\n",dirlist,menu);


        switch ( code ) {
        case keyMenu:
        case keyBack:
            sfc = render_attach_surface(menuapp->render,menuapp->old_sfc);
            menuapp->old_sfc = NULL;
            surface_dealloc(sfc);
            if ( menuapp->current_media == NULL ) {
                program_register_keys(menuapp->program,REGISTER_MEDIA,NULL,menuapp);
            }
            break;
        case keyOk:
        case keyPlay:
        case keyChanDn:
        case keySkip:
        case keyFastFwd:
        {
            int    sel = dirlist->currentsel;
            if ( sel < ( dirlist->filelist_num-1) ) {
                dirlist->currentsel++;
                menu_set_entry(menu,menu_get_entry(menu) +1 );
            }
            menuapp_display_image(dirlist,dirlist->currentsel,0);
            break;
        }
        case keyFastRew:
        case keyChanUp:
        case keyReplay:
        {
            int    sel = dirlist->currentsel;
            if ( sel > 0  ) {
                dirlist->currentsel--;
                menu_set_entry(menu,menu_get_entry(menu) +1 );
            }
            menuapp_display_image(dirlist,dirlist->currentsel,0);
            break;
        }
        case keyGreen:
            menuapp_display_image(dirlist,dirlist->currentsel,90);
            break;
        case keyRed:
            menuapp_display_image(dirlist,dirlist->currentsel,180);
            break;
        case keyYellow:
            menuapp_display_image(dirlist,dirlist->currentsel,270);
            break;
        case keyBlue:
            menuapp_display_image(dirlist,dirlist->currentsel,0);
            break;
        }
        return 1;
    }
#endif


    /* If it's the plugin, some keys have different behaviour */
#ifdef VDR_PLUGIN
    ret = media_keys_vdr(menuapp,code);
    if ( ret != -1 ) {
        return ret;
    }
#endif


    switch ( code ) {    
    case keyMenu:
        if ( (streamtype & MEDIA_MASK) == MEDIA_MPEG ) {
            next_media_clear(menuapp);
            dongle_send_message(menuapp->dongle,RDC_STOP);
            menu_display(menuapp->menu);
            break;
        } else {
            return 0;   /* Continue with the menus */
        }
        break;
    case keyStop:
        next_media_clear(menuapp);
        dongle_send_message(menuapp->dongle,RDC_STOP);
        menu_display(menuapp->menu);
        break;
    case keyPlay:
        /* Live media can't be paused.. */
        if  ( ( streamtype & MEDIA_LIVE ) == 0 ) {
            dongle_send_play(menuapp->dongle,menuapp->current_media);
            menuapp->paused = FALSE;
        } else {
            return 0;   /* Continue with the menus */
        }
        break;
    case keyPause:
        /* Shouldn't allow pausing of live media */
        if (  ( streamtype & MEDIA_LIVE ) == 0 ) {
            if ( menuapp->paused ) {
                menuapp->paused = FALSE;
                dongle_send_play(menuapp->dongle,menuapp->current_media);
            } else {
                menuapp->paused = TRUE;
                dongle_send_message(menuapp->dongle,RDC_PAUSE);
            }
        } else {
            return 0;   /* Continue with the menus */
        }
        break;
    case keyVolUp:
        dongle_send_message(menuapp->dongle,RDC_VOLUP);
        break;
    case keyVolDn:
        dongle_send_message(menuapp->dongle,RDC_VOLDOWN);
        break;
    case keyMute:
        dongle_send_message(menuapp->dongle,RDC_MUTE);
        break;
    case keyFastFwd:
        if (  ( streamtype & MEDIA_LIVE ) == 0 && (( streamtype & MEDIA_MASK) == MEDIA_MPEG ) ) {
            dongle_send_message(menuapp->dongle,RDC_FORWARD);
        } else {
            return 0;   /* Continue with the menus */
        }
        break;
    case keyFastRew:
        if (  ( streamtype & MEDIA_LIVE ) == 0 && (( streamtype & MEDIA_MASK) == MEDIA_MPEG ) ) {
            dongle_send_message(menuapp->dongle,RDC_REWIND);
        } else {
            return 0;   /* Continue with the menus */
        }
        break;
    case keySkip:
        if (  ( streamtype & MEDIA_LIVE ) == 0 ) {
            dongle_send_message(menuapp->dongle,RDC_SKIP);
        }
        break;
    case keyReplay:
        if (  ( streamtype & MEDIA_LIVE ) == 0 ) {
            dongle_send_message(menuapp->dongle,RDC_BACK);
        }
        break;
    case keyYellow:
        if (  ( streamtype & MEDIA_LIVE ) == 0 ) {
            dongle_send_message(menuapp->dongle,RDC_SKIP);
        } else {
            return 0;   /* Continue with the menus */
        }
        break;
    case keyGreen:
        if (  ( streamtype & MEDIA_LIVE ) == 0 ) {
            dongle_send_message(menuapp->dongle,RDC_BACK);
        } else {
            return 0;   /* Continue with the menus */
        }
        break;
    case key0 ... key9:
        if (  ( streamtype & MEDIA_LIVE ) == 0 ) {
            dongle_send_percent(menuapp->dongle,RDC_SEEK_PERCENT, (code-key0)*10);
        } else {
            return 0;   /* Carry on processing menu keys */
        }
        break;
    default:
        return 0;      /* Carry on processing menu keys */
    }   
    return 1;
}

static int media_ack(int acktype, void *param, unsigned char *buf, int len)
{
    menuapp_t         *menuapp = (menuapp_t *)param;

    switch ( acktype ) {
    case RDC_PLAY:
        if ( (dongle_get_type(menuapp->dongle) & MEDIA_MASK) == MEDIA_MPEG ) {
            dongle_send_message(menuapp->dongle,RDC_DISPLAY_OFF);
        } else if ( (dongle_get_type(menuapp->dongle) & MEDIA_MASK) == MEDIA_MP3  && (dongle_get_lasttype(menuapp->dongle) & MEDIA_MASK) == MEDIA_MPEG ) {
            dongle_send_message(menuapp->dongle,RDC_DISPLAY_ON);
        } else if ( dongle_get_type(menuapp->dongle) == 0 ) {
            if ( menuapp->num_next_media ) {
                next_media_pop(menuapp);
                menuapp->paused = FALSE;
                dongle_send_play(menuapp->dongle,menuapp->current_media);
            } else {
                /* Remove registered handlers */
                if ( menuapp->current_media ) {
                    free(menuapp->current_media);
                    menuapp->current_media = NULL;
                }
                menuapp->paused = FALSE;
                program_register_keys(menuapp->program,REGISTER_MEDIA,NULL,menuapp);
                program_register_ack(menuapp->program,REGISTER_MEDIA,NULL,menuapp);
            }
        }
        break;
    case RDC_STOP:     /* The client has sent a stop message */  
        if ( ( dongle_get_lasttype(menuapp->dongle) & MEDIA_MASK ) == MEDIA_MPEG ) {
            /* Switch back to original tv mode */
			if ( c_standard_switch && 
                 menuapp->dongle->tvmode != menuapp->dongle->old_tvmode ) {
				menuapp->dongle->tvmode = menuapp->dongle->old_tvmode;
				dongle_send_settings(menuapp->dongle, RDC_SETTINGS_TEST);
			}
            dongle_send_message(menuapp->dongle,RDC_DISPLAY_ON);
        }
        if ( menuapp->num_next_media ) {
            next_media_pop(menuapp);
            menuapp->paused = FALSE;
            dongle_send_play(menuapp->dongle,menuapp->current_media);          
        } else {
            /* Remove registered handlers */
            if ( menuapp->current_media ) {
                free(menuapp->current_media);
                menuapp->current_media = NULL;
            }
            menuapp->paused = FALSE;
            program_register_keys(menuapp->program,REGISTER_MEDIA,NULL,menuapp);
            program_register_ack(menuapp->program,REGISTER_MEDIA,NULL,menuapp);
        }
        break;
    case RDC_FORWARD:
    case RDC_REWIND:
        //   dongle_send_play(menuapp->dongle,menuapp->current_media);
        break;
    default:
        return 0;    /* Kick it up a level */
    }

    return 1;        /* Don't process anymore */
}

/* Utility functions below here */


static dirlist_t    *new_dirlist(menuapp_t *menuapp)
{
    dirlist_t   *dir = (dirlist_t *)malloc(sizeof(*dir));

    dir->filelist_num = 0;
    dir->filelist = NULL;
    dir->menuapp = menuapp;
    dir->title = NULL;
    dir->path = NULL;
    dir->currentsel = 0;
    return dir;
}

static void          delete_dirlist(void *idir)
{
    dirlist_t *dir = (dirlist_t *)idir;
    int        i;

    printf("Deleting dirlist at %p\n",dir);

    for ( i = 0; i < dir->filelist_num; i++ ) {
        free(dir->filelist[i].display_name);
        free(dir->filelist[i].url);
        free(dir->filelist[i].dialcode);
    }
    if ( dir->filelist ) {
        free(dir->filelist);
    }

    if ( dir->title ) {
        free(dir->title);
    }

    if ( dir->path ) {
        free(dir->path);
    }

    free(dir);
}

static filelist_t *channel_file_scan(char *filename,int *scanned)
{
    char         buf[512];
    filelist_t  *list = NULL;
    int          num  = 0;
    FILE        *fp;
    struct stat  sb;
    char        *ptr,*endline;

    *scanned = 0;

    if ( stat(filename,&sb) < 0 || ( fp = fopen(filename,"r") ) == NULL ) {
        return NULL;
    }
  
    while ( !feof(fp) && fgets(buf,sizeof(buf),fp) ) {
        if ( (ptr = strchr(buf,';') ) == NULL ) {
            continue;
        }
        *ptr = 0;
        ptr++;

        while (  ( endline = strrchr(ptr,'\n')  ) != NULL || ( endline = strrchr(ptr,'\r') ) != NULL ) {
            *endline = 0;
        }

        list = filelist_add(list,&num,buf,ptr,NULL,FILE_URL);       
    }
    *scanned = num;

    return list;
}

#ifndef VDR_PLUGIN
static filelist_t *arguments_scan(int argc, char *argv[],int *scanned)
{
    char          filename[FILENAME_MAX+1];
    filelist_t   *list = NULL;
    struct stat   sb;
    int           i;
    int           num  = 0;
    int           flags;

    *scanned = 0;

    for ( i = 0; i < argc; i++ ) {
        /* Want to do something clever with the filename here */
        snprintf(filename,sizeof(filename),"%s",argv[i]);
        if ( stat(filename,&sb) < 0 ) {
            continue;
        }

        if ( S_ISDIR(sb.st_mode) ) {
            flags = FILE_DIR;
        } else {
            flags = 0;
        }

        list = filelist_add(list,&num,argv[i],argv[i],NULL,flags);
    }
    
    *scanned = num;

    return list;   
}
#endif

#ifdef HAVE_LIBXML2
static void rss_item_scan(xmlDocPtr doc, xmlNodePtr item, char **item_title, char **item_description)
{
    while(item != NULL) {
        Dprintf(DEBUG, "--->%s\n",item->name);
        if(! xmlStrcasecmp(item->name, (const xmlChar *) "title")) {
            xmlChar *title = xmlNodeListGetString(doc, item->xmlChildrenNode, 1);
			if(title) {
				*item_title = (char *)strdup( (char *) title);
			} else {
				*item_title = "";
			}
            xmlFree(title);
        }

        if(! xmlStrcasecmp(item->name, (const xmlChar *) "description")) {
            xmlChar *description = xmlNodeListGetString(doc, item->xmlChildrenNode, 1);
			if(description) {
				*item_description = (char *)strdup( (char *)description);
			} else {
				*item_description = "";
			}
            xmlFree(description);
        }
        item = item->next;
    }
}

static filelist_t *rss_scan(xmlDocPtr doc, xmlNodePtr cur, int *scanned, char **list_title)
{
    filelist_t *list = NULL;
    char *item_title, *item_description;
    int num = 0;

    *scanned = 0;

    while(cur != NULL) {
        Dprintf(DEBUG, "scan: %s\n",cur->name);
        if(! xmlStrcmp(cur->name, (const xmlChar *) "title")) {
            xmlChar *title = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
			if(title) {
				*list_title = (char *)strdup((char *)title);
			} else {
				*list_title = NULL;
			}
            xmlFree(title);
        }

        if(! xmlStrcmp(cur->name, (const xmlChar *) "item")) {

            rss_item_scan(doc, cur->xmlChildrenNode, &item_title, &item_description);
            list = filelist_add(list, &num, item_title, item_description, NULL, 0);
        }

        cur = cur->next;
    }

    *scanned = num;
    return list;
}
#endif

static filelist_t *directory_scan(char *path, int *scanned)
{
    DIR    *dir;
    struct dirent *dirent;
    struct stat sb;
    char   filename[FILENAME_MAX+1];
    char   dispname[FILENAME_MAX+1];
    filelist_t *list = NULL;
    int         num  = 0;
    int         flags;

    *scanned = 0;

    if ( ( dir = opendir(path) ) == NULL ) {
        perror("opendir");
        return NULL;
    }

    while ( (dirent = readdir(dir)) != NULL ) {
        if ( dirent->d_name[0] == '.' ) {
            continue;
        }

        snprintf(filename,sizeof(filename),"%s/%s",path,dirent->d_name);
        if ( stat(filename,&sb) < 0 ) {
            continue;
        }

        if ( S_ISDIR(sb.st_mode) ) {
            flags = FILE_DIR;
            snprintf(dispname,sizeof(dispname),"[%s]",dirent->d_name);
        } else {
#ifdef HAVE_LIBID3TAG
            mp3info_t *info;

            info = mp3_get_info_file(filename);

            if ( mp3_get_title(info) ) {
                snprintf(dispname, sizeof(dispname), "%s", mp3_get_title(info));
            } else {
                snprintf(dispname,sizeof(dispname),"%s",dirent->d_name);
            }
            mp3_info_delete(info);
#else
            snprintf(dispname,sizeof(dispname),"%s",dirent->d_name);
#endif
            flags = 0;
        }

        list = filelist_add(list,&num,dispname,dirent->d_name,NULL,flags);
                  
    }

    closedir(dir);

    directory_sort(num,list);

    *scanned = num;
    return list;
}


static filelist_t *filelist_add(filelist_t *list,int *num,char *display_name,char *url,char *dialcode,int flags)
{
    int       i;

    i = (*num)++;
    list = (filelist_t *)realloc(list, *num * sizeof(filelist_t));

    list[i].display_name = strdup(display_name);               
    list[i].url = strdup(url);
    list[i].flags = flags;
    if ( dialcode != NULL ) {
        list[i].dialcode = dialcode;
    } else {
        list[i].dialcode = generate_dialcode(list[i].display_name);
    }

    Dprintf(DEBUG, "Code: %s (%s)\n", list[i].dialcode, list[i].url);
        
    return list;
}


static void directory_sort(int num,filelist_t *list)
{
    int    i,j;
    int    t_flags;
    char  *t_name;
    char  *t_url;
    char  *t_dialcode;

    for ( i = (num-1) ; i >= 0; i-- ) {
        t_name = list[i].display_name;
        t_url  = list[i].url;
        t_flags = list[i].flags;
        t_dialcode = list[i].dialcode;
        j = i + 1;

        while ( j < num && strcasecmp(list[j].url,t_url) < 0 ) {
            list[j-1].display_name = list[j].display_name;
            list[j-1].flags = list[j].flags;
            list[j-1].dialcode = list[j].dialcode;
            list[j-1].url = list[j].url;
            j++;
        }

        list[j-1].display_name = t_name;
        list[j-1].flags = t_flags;
        list[j-1].dialcode = t_dialcode;
        list[j-1].url = t_url;
    }
}





static void next_media_clear(menuapp_t *menuapp)
{
    int         i;

    if ( menuapp->num_next_media ) {
        for ( i = 0; i > menuapp->num_next_media; i++ ) {
            free(menuapp->next_media[i]);
        }
        free(menuapp->next_media);
        menuapp->next_media = NULL;
        menuapp->num_next_media = 0;
    }
}

static void next_media_pop(menuapp_t *menuapp)
{
    int       i;

    if ( menuapp->num_next_media ) {     /* Which it already is... */
        if ( menuapp->current_media ) {
            free(menuapp->current_media);
        }

        menuapp->current_media = menuapp->next_media[0];

        for ( i = 1; i < menuapp->num_next_media; i++ ) {
            menuapp->next_media[i-1] = menuapp->next_media[i];
        }
        if ( --menuapp->num_next_media == 0 ) {
            free(menuapp->next_media);
            menuapp->next_media = NULL;
        }
    } else {
        menuapp->current_media = NULL;
    }
}

static void next_media_push(menuapp_t *menuapp, char *url)
{
    int       i = menuapp->num_next_media++;

    menuapp->next_media = (char **)realloc(menuapp->next_media,menuapp->num_next_media * sizeof(char *));
    menuapp->next_media[i] = strdup(url);
}


/* Try to parse a playlist here */
static int playlist_parse(menuapp_t *menuapp, char *path, char *name)
{
    char    buf[FILENAME_MAX+1];
    char    filename[FILENAME_MAX+1];
    char   *ptr;
    FILE   *fp;
    bool_t  firsttime = TRUE;
    bool_t  ispls = FALSE;
    bool_t  isfile = FALSE;
    bool_t  added  = FALSE;

    snprintf(buf,sizeof(buf),"%s/%s",path,name);

    /* Try and open our "filelist" file */
    if ( ( fp = fopen(buf,"r") ) == NULL ) {
        return 0;
    }

    while ( fgets(filename,sizeof(filename),fp) != NULL ) {
        /* Kill any EOL characters */
        while ( ( ptr = strchr(filename,'\n') ) || (ptr = strchr(filename,'\r') ) ) {
            *ptr = 0;
        }

        /* Try and do some rudimentary stupidity prevention */
        if ( firsttime ) {
            ptr = filename;
            while ( *ptr ) {
                if ( !isprint(*ptr++) ) {              
                    fclose(fp);
                    return 0;
                }
            }
        }

            
        if ( filename[0] == '[' ) {
            if ( firsttime && strncasecmp(filename+1,"playlist]",strlen("playlist]")) == 0 ) {
                ispls = TRUE;
            }
            continue;
        }
            
        if ( filename[0] == ';' ||
             filename[0] == '#' || strlen(filename) == 0 ) {
            continue;
        }
            
        /* Skip over .pls related things */
        if ( ispls ) {
            if ( strncasecmp(filename,"file",strlen("file")) ) {
                continue;
            } else {                
                int      num;
                char    *endptr;
                char    *tempptr;
                    
                num = strtol(filename+4,&endptr,10);
                    
                if ( endptr != NULL && *endptr == '=' ) {
                    endptr++; /* Now points to filename, so get it */
                    while ( isspace(*endptr) )
                        endptr++;
                    /* Shove things along - can't use strcpy because
                       the strings overlap */
                    tempptr = filename;
                    while ( (*tempptr++ = *endptr++) != 0 )
                        ;
                    /* Additional sanity check */ 
                    if ( strlen(filename) == 0 ) {
                        continue;
                    }
                } else {        /* Not a number, ignore line? */
                    continue;
                }
            }        
        }            
            
        /* Try and be almost intelligent about adding the file */
        isfile = TRUE;
        if ( filename[0] != '/' ) {
            /* Add the line in the buffer onto the playlist */
            if ( strncmp(filename,"http://",strlen("http://")) == 0 ) {
                snprintf(buf,sizeof(buf),"%s",filename);
                isfile = FALSE;
            } else {
                snprintf(buf,sizeof(buf),"file://%s/%s",path,filename);
            }
        } else {  /* We assume the complete path is there.. */
            snprintf(buf,sizeof(buf),"file://%s",filename);
        }
            
        /* Test to see if the file exists now */
        if ( isfile && file_get_type(buf + strlen("file://")) <= 0 ) {
            continue;
        }
            
        Dprintf(DEBUG,"Adding file %s to playlist\n",buf);
        if ( firsttime ) {
            next_media_clear(menuapp);
            firsttime = FALSE;
        }
        next_media_push(menuapp,buf);
        added = TRUE;
    }
    /* Close playlist file */
    fclose(fp);
    /* Now start playing files */
    if ( added ) {
        if ( menuapp->current_media == NULL ) {        
            next_media_pop(menuapp);
            menuapp->paused = FALSE;
            if ( menuapp->current_media != NULL ) {
                dongle_send_play(menuapp->dongle,menuapp->current_media);      
                program_register_keys(menuapp->program,REGISTER_MEDIA,media_keys,menuapp);
                program_register_ack(menuapp->program,REGISTER_MEDIA,media_ack,menuapp);
            }
        } else {
            menuapp->paused = FALSE;
            dongle_send_message(menuapp->dongle,RDC_STOP);
        }     
        return 1;
    }
    return 0;
}

/* Generate a code which can be "dialled" from the remote control */
static char *generate_dialcode(char *name)
{
    char *dialcode = NULL;
    int name_length, i,j;


    if(! name) {
        return dialcode;
    }

    dialcode = (char *)malloc(MAX_DIALCODE_LEN + 1);
    memset(dialcode, 0, MAX_DIALCODE_LEN + 1);

    name_length = strlen(name);

    i = 0; j = 0;
    while(i < MAX_DIALCODE_LEN && j < name_length) {
        switch(tolower(name[j++])) {
	    case '0':
            dialcode[i++]= '0';
            break;
	    case '1':
            dialcode[i++]= '1';
            break;
	    case '2':
	    case 'a' ... 'c':
            dialcode[i++]= '2';
            break;
	    case '3':
	    case 'd' ... 'f':
            dialcode[i++]= '3';
            break;
	    case '4':
	    case 'g' ... 'i':
            dialcode[i++]= '4';
	    break;
	    case '5':
	    case 'j' ... 'l':
            dialcode[i++]= '5';
            break;
	    case '6':
	    case 'm' ... 'o':
            dialcode[i++]= '6';
            break;
	    case '7':
	    case 'p' ... 's':
            dialcode[i++]= '7';
            break;
	    case '8':
	    case 't' ... 'v':
            dialcode[i++]= '8';
            break;
	    case '9':
	    case 'w' ... 'z':
            dialcode[i++]= '9';
            break;
        }
    }

    return dialcode;
}

/*
 * Handler for dialcode
 */
static int dial_entry(menu_t *menu, void *imenuapp, int key, int sel)
{
    dirlist_t *dir = (dirlist_t *)imenuapp;
    char *current_dialcode, *next_dialcode;
    int current_length = 0, next_length;
    int current_dialcode_selection;
    int i, start_pos;

    current_dialcode = menu_get_dialcode(menu);

    if (current_dialcode) {
        current_length = strlen(current_dialcode);
    }

    if(current_length < MAX_DIALCODE_LEN) {
        next_dialcode = (char *)malloc(current_length + 2);
        memset(next_dialcode, 0 , current_length + 2);
        memcpy(next_dialcode, current_dialcode, current_length);
        if(key > 0) {
            next_dialcode[current_length] = '0' + (key - key0);
        } else {
            if(current_length > 0) {         
                next_dialcode[current_length - 1] = 0;
            }
        }
    } else {
        next_dialcode = strdup(current_dialcode);
    }

    next_length = strlen(next_dialcode);
    current_dialcode_selection = menu_get_dialcode_selection(menu);
    if(key > 0) {
        start_pos = current_dialcode_selection;
    } else {
        start_pos = 0;
    }

    for( i = start_pos; i < dir->filelist_num; i++) {
        if( strncasecmp(dir->filelist[i].dialcode, next_dialcode, next_length) == 0) {
            menu_set_dialcode_selection(menu, i);
            menu_set_dialcode(menu, next_dialcode);
            free(next_dialcode);
            return i;
        }
    }
 
    return sel;
}


/** \brief Display an image
 *
 *  \param dirlist
 *  \param idx
 *  \param rotate
 *
 *  \retval 1
 *  \retval 0
 */
#if HAVE_MAGICK | HAVE_LIBJPEG
static int menuapp_display_image(dirlist_t *dirlist, int index, int rotate)
{
    char         buf[FILENAME_MAX+1];
    int          filetype = 0;
    filelist_t  *entry = &dirlist->filelist[index];

    snprintf(buf,sizeof(buf),"%s/%s",dirlist->path,entry->url);
    filetype = file_get_type(buf); 

    if ( filetype & MEDIA_PICTURE ) {
        surface_t  *surface;
        surface_t  *old_sfc;
        int         w,d;
        
        w = render_get_width(dirlist->menuapp->render);
        d = render_get_depth(dirlist->menuapp->render);
        snprintf(buf,sizeof(buf),"%s/%s",dirlist->path,entry->url);

        if ( ( surface = surface_alloc(w,d,24) ) != NULL ) {
#ifdef HAVE_MAGICK
            if ( mvp_image_display(surface,buf,-1,-1,w,d,rotate,SCALE_DOWN) == 0 )
#else
                if ( mvp_jpeg_display(surface,buf,-1,-1,w,d) == 0 )
#endif
                {
                    //save old surface if this was the menu
                    if (dirlist->menuapp->old_sfc == NULL) {
                        dirlist->menuapp->old_sfc = render_attach_surface(dirlist->menuapp->render,surface);
                    } else {
                        old_sfc = render_attach_surface(dirlist->menuapp->render,surface);
                        surface_dealloc(old_sfc);
                    }

                    return 1;
                }
            surface_dealloc(surface);
        } 
    } else {
        return 0;
    }
    return 1; 
}
#endif



/*
 * Local Variables:
 *  indent-tabs-mode:nil
 *  require-final-newline:t
 *  c-basic-offset: 4
 * End:
 */
