
/*
 *   MediaMVP Server
 *
 *   (C) 2003 Dominic Morris
 *
 *   $Id: menuapp-vdr.c,v 1.17 2005/04/01 11:39:19 dom Exp $
 *   $Date: 2005/04/01 11:39:19 $
 *
 *
 *   Contains vdr specific functionality for the menuapp
 */


#include "mediamvp.h"

#include "misc.h"
#if 0
#include <vdr/i18n.h>
#include <vdr/eit.h>
#include <vdr/recording.h>
#endif

static void        recordings_submenu(menu_t *menu, menuapp_t *menuapp, char *base, int level);
static int         channels_colour_keys(menu_t *menu, void *imenuapp, int key, int sel);
static int         schedule_colour_keys(menu_t *menu, void *imenuapp, int key, int sel);
static int         vdrrecs_colour_keys(menu_t *menu, void *imenuapp, int key, int sel);

static filelist_t *recordings_scan(char *base, int level, int *num);

/***********************
 *
 *   Handling of the Channel Menus
 *
 ***********************/
static int menuapp_channels(menu_t *menu,void *imenuapp, int sel)
{
    char           buf[512]; 
    char           buf2[512]; 
    char           buf3[20]; 
    menuapp_t     *menuapp = (menuapp_t *)imenuapp;
    dirlist_t     *dirlist;
    filelist_t    *list = NULL;
    int            list_num = 0;
    menu_t        *child;
    cChannel      *chan;

    /* Now, create a new menu for us */
    dirlist = new_dirlist(menuapp);
    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("Channels")); 

    for (chan = Channels.First(); chan != NULL; chan = Channels.Next(chan) ) {
        if ( chan->GroupSep() && !*chan->Name())
            continue;

        if ( chan->GroupSep() ) {

        } else {
            snprintf(buf,sizeof(buf),"% 3d %s",chan->Number(),chan->Name());
#if VDRVERSNUM >= 10318
            snprintf(buf2,sizeof(buf2),"%s%s",VDRURL,*chan->GetChannelID().ToString());
#else
            snprintf(buf2,sizeof(buf2),"%s%s",VDRURL,chan->GetChannelID().ToString());
#endif
            /* Make the dial code the channel number */
            snprintf(buf3,sizeof(buf3),"%d",chan->Number());
            list = filelist_add(list,&list_num,buf,buf2,strdup(buf3),FILE_URL);
            menu_add(child,buf,process_media);
        }
    }

    /* Now we'll set up dirlist */
    dirlist->title = NULL;    /* We don't need it since there's no subdirs */
    dirlist->path  = NULL;    /* We don't need it sinec there's no subdirs */
    dirlist->filelist_num = list_num;
    dirlist->filelist = list;

    menu_set_colour_actions(child,"Back",NULL,NULL,tr("Switch"),channels_colour_keys);

    menu_display(child);
    return 1;
}



static int channels_colour_keys(menu_t *menu, void *imenuapp, int key, int sel)
{
    dirlist_t   *dir = (dirlist_t *)imenuapp;
    filelist_t  *entry = &dir->filelist[sel];
    menuapp_t   *menuapp = dir->menuapp;

    switch ( key ) {
    case keyRed:
        return 0 ;
    case keyBlue:
        if ( entry == NULL ) {   /* Nothing there.. */
            return 1;
        }
        if ( menuapp->current_media == NULL ) {        
            menuapp->current_media = strdup(entry->url);
            menuapp->paused = FALSE;
            dongle_send_play(menuapp->dongle,entry->url);
        } else {
            next_media_clear(menuapp);
            next_media_push(menuapp,entry->url);       
            dongle_send_message(menuapp->dongle,RDC_STOP);
        }
        program_register_keys(menuapp->program,REGISTER_MEDIA,media_keys,menuapp);
        program_register_ack(menuapp->program,REGISTER_MEDIA,media_ack,menuapp);
        return 1;   
    default:
        break;
    }

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

/***********************
 *
 *   Handling of the Schedule Menus
 *
 ***********************/
#if VDRVERSNUM >= 10305
static int schedule_menuapp(menu_t *menu, void *imenuapp, int sel)
{
    menuapp_t     *menuapp = (menuapp_t *)imenuapp;
    dirlist_t     *dirlist;
    filelist_t    *list = NULL;
    int            list_num = 0;
    menu_t        *child;
    cSchedulesLock MutexLock;
    const cSchedules *Schedules = cSchedules::Schedules(MutexLock);

    /* Now, create a new menu for us */
    dirlist = new_dirlist(menuapp);
    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,menuapp->Now ? tr("What's on now?") : tr("What's on next?"));
   

    for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
        if (!Channel->GroupSep()) {
            const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID());
            if (Schedule) {
                const cEvent *Event = menuapp->Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
                if (Event) {
                    char       buf[1024];
                    char       buf2[1024];
                    char       buf3[20];
#if VDRVERSNUM >= 10318
                    snprintf(buf,sizeof(buf),"%d\t%.*s\t%.*s\t%s", Channel->Number(), 6, Channel->Name(), 5, *Event->GetTimeString(), Event->Title());
                    snprintf(buf2,sizeof(buf2),"%s%s",VDRURL, *Channel->GetChannelID().ToString());
#else
                    snprintf(buf,sizeof(buf),"%d\t%.*s\t%.*s\t%s", Channel->Number(), 6, Channel->Name(), 5, Event->GetTimeString(), Event->Title());
                    snprintf(buf2,sizeof(buf2),"%s%s",VDRURL, Channel->GetChannelID().ToString());
#endif
    

                    snprintf(buf3,sizeof(buf3),"%d",Channel->Number());


                    list = filelist_add(list,&list_num,buf,buf2,strdup(buf3),FILE_URL);
                    menu_add(child,buf,process_media);       
  
                }
            }
        }
    }
    /* Now we'll set up dirlist */
    dirlist->title = NULL;    /* We don't need it since there's no subdirs */
    dirlist->path  = NULL;    /* We don't need it sinec there's no subdirs */
    dirlist->filelist_num = list_num;
    dirlist->filelist = list;


    menu_set_colour_actions(child,tr("Back") /* tr("Record") */, menuapp->Now ? tr("Next") : tr("Now"), NULL /* tr("Schedule") */, tr("Switch"),schedule_colour_keys);

    menu_set_entry(child,menuapp->current_position);

    menu_display(child);

    return 1;
}

#else
static int CompareEventChannel(const void *p1, const void *p2)
{
#if VDRVERSNUM < 10300
  return (int)( (*(const cEventInfo **)p1)->GetChannelNumber() - (*(const cEventInfo **)p2)->GetChannelNumber());
#else
  return (int)( (*(const cEvent **)p1)->ChannelNumber() - (*(const cEvent **)p2)->ChannelNumber());
#endif
}

static int schedule_menuapp(menu_t *menu, void *imenuapp, int sel)
{
#if VDRVERSNUM < 10300
    cMutexLock MutexLock;
    const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
    const cEventInfo **pArray = NULL;
#else
    cSchedulesLock MutexLock;
    const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
    const cEvent **pArray = NULL;
#endif
    menuapp_t     *menuapp = (menuapp_t *)imenuapp;
    dirlist_t     *dirlist;
    filelist_t    *list = NULL;
    int            list_num = 0;
    menu_t        *child;
    const cSchedule *Schedule;
    int num = 0;

    if ( Schedules != NULL ) {
        Schedule = Schedules->First();

        /* If it ain't broke, why fix it? - Stolen from vdr */
        while ( Schedule ) {
#if VDRVERSNUM < 10300
            pArray = (const cEventInfo **)realloc(pArray, (num + 1) * sizeof(cEventInfo *));
#else
            pArray = (const cEvent **)realloc(pArray, (num + 1) * sizeof(cEvent *));
#endif
            
            pArray[num] = menuapp->Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
            
            if (pArray[num]) {
#if VDRVERSNUM < 10300
                cChannel *channel = Channels.GetByChannelID(pArray[num]->GetChannelID(), true);
#else
                cChannel *channel = Channels.GetByChannelID(pArray[num]->ChannelID(), true);
#endif
                if (channel) {
                    pArray[num]->SetChannelNumber(channel->Number());
                    num++;
                }
            }
            Schedule = (const cSchedule *)Schedules->Next(Schedule);
        }
#if VDRVERSNUM < 10300
        qsort(pArray, num, sizeof(cEventInfo *), CompareEventChannel);
#else
        qsort(pArray, num, sizeof(cEvent *), CompareEventChannel);
#endif
    }
        
    /* Now, create a new menu for us */
    dirlist = new_dirlist(menuapp);
    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,menuapp->Now ? tr("What's on now?") : tr("What's on next?"));
   
    for ( int i = 0; i < num; i++ ) {
        char       buf[1024];
        char       buf2[1024];
        char       buf3[20];
#if VDRVERSNUM < 10300
        const cEventInfo *eventInfo = pArray[i];
        cChannel *channel = Channels.GetByNumber(eventInfo->GetChannelNumber());
#else
        const cEvent *eventInfo = pArray[i];
        cChannel *channel = Channels.GetByNumber(eventInfo->ChannelNumber());
#endif


#if VDRVERSNUM < 10300
        snprintf(buf,sizeof(buf),"%d\t%.*s\t%.*s\t%s", 
                 pArray[i]->GetChannelNumber(), 6,  channel ? channel->Name() : "???",
                 5, eventInfo->GetTimeString(), eventInfo->GetTitle());
#else
        snprintf(buf,sizeof(buf),"%d\t%.*s\t%.*s\t%s", 
                 pArray[i]->ChannelNumber(), 6,  channel ? channel->Name() : "???",
                 5, eventInfo->GetTimeString(), eventInfo->Title());
#endif
        snprintf(buf2,sizeof(buf2),"%s%s",VDRURL, channel ? channel->GetChannelID().ToString() : "???");

        snprintf(buf3,sizeof(buf3),"%d",channel ? channel->Number() : 0);


        list = filelist_add(list,&list_num,buf,buf2,channel ? strdup(buf3) : NULL,FILE_URL);
        menu_add(child,buf,process_media);       
    }
    if ( pArray != NULL ) {
        free(pArray);
    }

    /* Now we'll set up dirlist */
    dirlist->title = NULL;    /* We don't need it since there's no subdirs */
    dirlist->path  = NULL;    /* We don't need it sinec there's no subdirs */
    dirlist->filelist_num = list_num;
    dirlist->filelist = list;


    menu_set_colour_actions(child,tr("Back") /* tr("Record") */, menuapp->Now ? tr("Next") : tr("Now"), NULL /* tr("Schedule") */, tr("Switch"),schedule_colour_keys);

    menu_set_entry(menuapp->current_position);
    menu_display(child);

    return 1;
}
#endif


static int schedule_colour_keys(menu_t *menu, void *imenuapp, int key, int sel)
{
    dirlist_t   *dir = (dirlist_t *)imenuapp;
    filelist_t  *entry = &dir->filelist[sel];
    menuapp_t   *menuapp = dir->menuapp;
    menu_t      *parent;

    switch ( key ) {
    case keyRed:
        menuapp->current_position = 0;
        return 0 ;
    case keyGreen:
        menuapp->Now ^= 1;
        parent = menu_parent(menu);
        menuapp->current_position = menu_get_entry(menu);
        schedule_menuapp(parent,menuapp,0);
        return 0;
    case keyYellow:
        /* Display the schedule for a channel */   
        break;   
    case keyBlue:     /* Switch to channel */
        if ( entry == NULL ) {   /* Nothing there.. */
            return 1;
        }
        if ( menuapp->current_media == NULL ) {        
            menuapp->current_media = strdup(entry->url);
            menuapp->paused = FALSE;
            dongle_send_play(menuapp->dongle,entry->url);
        } else {
            next_media_clear(menuapp);
            next_media_push(menuapp,entry->url);       
            dongle_send_message(menuapp->dongle,RDC_STOP);
        }
        program_register_keys(menuapp->program,REGISTER_MEDIA,media_keys,menuapp);
        program_register_ack(menuapp->program,REGISTER_MEDIA,media_ack,menuapp);
        break;
    }   
    return 1;
}

/***********************
 *
 *   Handling of the vdrrecs Menus
 *
 ***********************/
static int menuapp_vdrrecs(menu_t *menu,void *imenuapp, int sel)
{
    recordings_submenu(menu,(menuapp_t *)imenuapp,NULL,-1);

    return 1;

}


static void recordings_submenu(menu_t *menu, menuapp_t *menuapp, char *base, int level)
{
    menu_t         *child;
    dirlist_t      *dirlist;
    filelist_t     *list = NULL;
    char           *dirname = NULL;
    int             i;
  
    /* Define a menu */
    dirlist = new_dirlist(menuapp);

    if ( base == NULL ) {
        dirlist->level = -1;
    } else {
        if ( level == -1 ) {
	    level = 0;
        }
        dirlist->level = ++level;
    }

    child = new_menu(menuapp->dongle,menuapp->render,menuapp->program,menu,dirlist,0);
    menu_set_param_clearup(child,delete_dirlist);
    
    if ( base ) {
        if ( ( dirname = strrchr(base,'~') ) == NULL ) {
            dirname = base;
        } else {
            dirname++;
        }
    }


    menu_set_title(child,dirname ? dirname : tr("Video Recordings"));   
    menu_set_dialcode_callback(child, dial_entry);

    list = recordings_scan(base,dirlist->level,&dirlist->filelist_num);

    /* Now we'll set up dirlist */
    dirlist->title = NULL;    
    dirlist->path  = NULL;    
    dirlist->filelist = list;

    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"),NULL,NULL,NULL,vdrrecs_colour_keys);

    menu_display(child);
    return;  
}

/** \brief Scan the VDR recordings list 
 *
 *  \param base The subdirectory that we are browsing
 *  \param level The subdirectory level that we're browsing at
 *  \param num Pointer to keep the number of files added
 *
 * base should be NULL and level -1 when browsing the root directory
 *
 * This code is dirty to say the least...
 */
static filelist_t *recordings_scan(char *base, int level, int *num)
{
    char            buf[1024];
    char            buf2[1024];
    char           *lastitem = NULL;
    int             lastcount = 0;
    int             lastnew = 0;
    char           *dirname;
    char           *ptr;
    filelist_t     *list = NULL;
    int             lev,tlevel;
    cRecordings     Recordings;

    tlevel = level < 0 ? 0 : level;

    bool recordings = Recordings.Load();

    if ( recordings ) {
        cRecording *recording = Recordings.First();
        while ( recording ) {
            if ( base == NULL || (strstr(recording->Name(),base) == recording->Name() && recording->Name()[strlen(base)] == '~') ) {
                                
                snprintf(buf,sizeof(buf),"%s",recording->Name());                
                lev = 0;
                ptr = buf;
                while ( *ptr ) {
                    if ( *ptr == '~' ) {
                        if ( lev == tlevel ) {
                            *ptr = 0;
                        }
                        lev++;
                    }  
                    ++ptr;
                }

                /* Recording at the current directory level */
                if ( tlevel == lev  ) {
                    /* Add any pending subdirectory */
                    if ( lastitem ) {
                        if ( ( dirname = strrchr(lastitem,'~') ) == NULL ) {
                            dirname = lastitem;
                        } else {
                            dirname++;
                        }
                        snprintf(buf2,sizeof(buf2),"%d\t%d\t%s",lastcount,lastnew,dirname);
                        list = filelist_add(list,num,buf2,lastitem,NULL,FILE_URLDIR);
                        free(lastitem);
                        lastitem = NULL;
                    }
                    /* Add in this new recording */
                    snprintf(buf,sizeof(buf),"%s",recording->Title(' ',true,level));
                    snprintf(buf2,sizeof(buf2),"%s%s",VDRRECURL,recording->Title(' ',true));
                    list = filelist_add(list,num,buf,buf2,NULL,FILE_URL);
                } else if ( lev >= tlevel + 1 ) {  /* At one level down from where we're browsing */
                    /* Same path as last time, add it into the list */
                    if ( lastitem && strcmp(buf,lastitem) == 0 ) {
                        lastcount++;
                        if ( recording->IsNew() ) {
                            lastnew++;
                        }
                    } else {
                        /* A different item, so add in the previous subdirectory and start a new
                           one
                        */
                        if ( lastitem ) {
                            if ( ( dirname = strrchr(lastitem,'~') ) == NULL ) {
                                dirname = lastitem;
                            } else {
                                dirname++;
                            }
                            snprintf(buf2,sizeof(buf2),"%d\t%d\t%s",lastcount,lastnew,dirname);
                            list = filelist_add(list,num,buf2,lastitem,NULL,FILE_URLDIR);
                            free(lastitem);
                        }
                        lastitem = strdup(buf);
                        lastcount = 1;
                        lastnew = recording->IsNew() ? 1 : 0;
                    }
                    /* Contained within a subdirectory of a subdirectory, increment the counters */
                } else {
                    lastcount++;
                    if ( recording->IsNew() ) {
                        lastnew++;
                    }
                }
            }
            recording = Recordings.Next(recording);
        }
    }


    if ( lastitem ) {
        if ( ( dirname = strrchr(lastitem,'~') ) == NULL ) {
            dirname = lastitem;
        } else {
            dirname++;
        }
        snprintf(buf2,sizeof(buf2),"%d\t%d\t%s",lastcount,lastnew,dirname);
        list = filelist_add(list,num,buf2,lastitem,NULL,FILE_URLDIR);
        free(lastitem);
    }

 

    return list;
}




static int vdrrecs_colour_keys(menu_t *menu, void *imenuapp, int key, int sel)
{
    switch ( key ) {
    case keyRed:
        return 0;   
    default:
        break;
    }

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


/* Deal with Chan+/Chan- */
int  media_keys_vdr(menuapp_t *menuapp,int code)
{
    int            streamtype = dongle_get_type(menuapp->dongle);
    
    switch ( code ) {
    case keyChanUp:
    case keyChanDn:
        if (  ( (streamtype & (MEDIA_LIVE | MEDIA_MPEG) ) == (MEDIA_LIVE | MEDIA_MPEG) ) ) {
            if ( menuapp->current_media && strncmp(menuapp->current_media,VDRURL,strlen(VDRURL)) == 0  ) {
                char           buf[2048];
                char    *newchannel = ChannelSkip(menuapp->current_media + strlen(VDRURL),
                                                        code == keyChanUp ? 1 : -1 );
                if ( newchannel ) {
                    if ( menuapp->current_media ) {        
                        free(menuapp->current_media);
		    }
                    snprintf(buf,sizeof(buf),"%s%s",VDRURL,newchannel);
                    menuapp->current_media = strdup(buf);
                    free(newchannel);
                    menuapp->paused = FALSE;
#ifdef CAM_CHANGE_HACK
		    dongle_send_message(menuapp->dongle,RDC_STOP);
#endif
                    dongle_send_play(menuapp->dongle,buf);
                }
            }
            return 1;  /* We've done all we can.. */
        } else {
            return 0;   /* Carry on with menu */
        }
    }

    return -1;         /* Carry on with normal key functions */
}


/* Scan a file in the format used by the mp3 plugin */
static filelist_t *plugin_file_scan(char *filename, int *scanned)
{
    char         buf[FILENAME_MAX+1];
    filelist_t  *list = NULL;
    int          count = 0;
    char        *ptr,*endptr;
    struct stat  sb;
    FILE        *fp;
    int          flags;

    *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++;

        if ( (endptr = strchr(ptr,';') ) == NULL && 
             (endptr = strchr(ptr,'\n') ) == NULL &&
             (endptr = strchr(ptr,'\r') ) == NULL ) {
            continue;
        }
        *endptr = 0;

        if ( stat(buf,&sb) < 0 ) {
            continue;
        }

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

        list = filelist_add(list,&count,ptr,buf,NULL,flags);       
    }

    *scanned = count;
    return list;    
}

static bool_t find_recording(const char *dirname) {
    char          dirpath[PATH_MAX];
    char          recpath[PATH_MAX];

#if VDRVERSNUM < 10312
    cRecordings   Recordings;
    Recordings.Load();
#endif

    if (realpath(dirname,dirpath)) { /* recording does exist */
        
        for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
            /* search for recordings comparing real paths */
            if (realpath( recording->FileName(), recpath ) && strcmp(dirpath, recpath) == 0) {
                return TRUE;
            }
        }
    }

    return FALSE;
}

static void delete_recording(const char *dirname) {
    char          dirpath[PATH_MAX];
    char          recpath[PATH_MAX];
    
#if VDRVERSNUM < 10312
    cRecordings   Recordings;    
    Recordings.Load();
#endif

    if (realpath(dirname,dirpath)) { /* recording does exist */
        
        for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
            /* search for recordings comparing real paths */
            if (realpath( recording->FileName(), recpath ) && strcmp(dirpath, recpath) == 0) {
            	recording->Delete();
            }
        }
    }
}



static int is_vdr_recording(const char *dirname) {
    char    *ptr;
    if ( ( ptr = strrchr(dirname,'.') ) != NULL ) {
        if ( strcasecmp(ptr,".rec") == 0 && find_recording(dirname) ) {
            return 1;
        }
    }
    return 0;  	
}


static int recdel_colour_keys(menu_t *menu, void *imenuapp, int key, int sel)
{
    dirlist_t     *dir = (dirlist_t *)imenuapp;
    menu_t        *p_menu;
    menu_t        *pp_menu;
    menu_t        *ppp_menu;

    switch ( key ) {
    case keyRed:
        return 0;
    case keyGreen:       /* Really delete the recording */
	delete_recording(dir->path);

            /* Delete this menu, it's parent, and it's parent's parent */
            p_menu = menu_parent(menu);
            pp_menu = menu_parent(p_menu);
            ppp_menu = menu_parent(pp_menu);
            delete_menu(menu);
            delete_menu(p_menu);
            delete_menu(pp_menu);

            menu_display(ppp_menu);
            return 1; /* important, menu is invalid now */
    default:
        break;
    }

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



