/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 Daniel Stenberg
 *
 * 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 <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include "applimits.h"
#include "dir.h"
#include "file.h"
#include "lcd.h"
#include "backlight.h"
#include "button.h"
#include "kernel.h"
#include "usb.h"
#include "tree.h"
#include "main_menu.h"
#include "sprintf.h"
#include "mpeg.h"
#include "playlist.h"
#include "menu.h"
#include "wps.h"
#include "wps-display.h"
#include "settings.h"
#include "status.h"
#include "debug.h"
#include "ata.h"
#include "rolo.h"
#include "icons.h"

#ifdef HAVE_LCD_BITMAP
#include "widgets.h"
#endif

#ifdef LOADABLE_FONTS
#include "ajf.h"
#endif

#define NAME_BUFFER_SIZE (AVERAGE_FILENAME_LENGTH * MAX_FILES_IN_DIR)

char name_buffer[NAME_BUFFER_SIZE];
int name_buffer_length;
struct entry {
    short attr; /* FAT attributes + file type flags */
    char *name;
};

static struct entry dircache[MAX_FILES_IN_DIR];
static int dircursor;
static int dirstart;
static int dirlevel;
static int filesindir;
static int dirpos[MAX_DIR_LEVELS];
static int cursorpos[MAX_DIR_LEVELS];
static char lastdir[MAX_PATH];
static char lastfile[MAX_PATH];
static char currdir[MAX_PATH];

void browse_root(void)
{
    dirbrowse("/");
}


#ifdef HAVE_LCD_BITMAP

#define TREE_MAX_ON_SCREEN   ((LCD_HEIGHT-MARGIN_Y)/LINE_HEIGTH)
#define TREE_MAX_LEN_DISPLAY 16 /* max length that fits on screen */
 
#define MARGIN_X      (global_settings.scrollbar ? SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + ICON_WIDTH /* X pixel margin */
#define MARGIN_Y      (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)  /* Y pixel margin */

#define LINE_X      0 /* X position the entry-list starts at */
#define LINE_Y      (global_settings.statusbar ? 1 : 0) /* Y position the entry-list starts at */
#define LINE_HEIGTH 8 /* pixels for each text line */

#define CURSOR_X    (global_settings.scrollbar ? 1 : 0)
#define CURSOR_Y    0 /* the cursor is not positioned in regard to
                         the margins, so this is the amount of lines
                         we add to the cursor Y position to position
                         it on a line */
#define CURSOR_WIDTH  4

#define ICON_WIDTH    6

#define SCROLLBAR_X      0
#define SCROLLBAR_Y      lcd_getymargin()
#define SCROLLBAR_WIDTH  6

extern unsigned char bitmap_icons_6x8[LastIcon][6];

#else /* HAVE_LCD_BITMAP */

#define TREE_MAX_ON_SCREEN   2
#define TREE_MAX_LEN_DISPLAY 11 /* max length that fits on screen */
#define LINE_X      2 /* X position the entry-list starts at */
#define LINE_Y      0 /* Y position the entry-list starts at */

#define CURSOR_X    0
#define CURSOR_Y    0 /* not really used for players */

#endif /* HAVE_LCD_BITMAP */

#ifdef HAVE_RECORDER_KEYPAD
#define TREE_NEXT  BUTTON_DOWN
#define TREE_PREV  BUTTON_UP
#define TREE_EXIT  BUTTON_LEFT
#define TREE_ENTER BUTTON_RIGHT
#define TREE_MENU  BUTTON_F1
#define RELEASE_MASK (BUTTON_OFF | BUTTON_F2 | BUTTON_F3)
#else
#define TREE_NEXT  BUTTON_RIGHT
#define TREE_PREV  BUTTON_LEFT
#define TREE_EXIT  BUTTON_STOP
#define TREE_ENTER BUTTON_PLAY
#define TREE_MENU  BUTTON_MENU
#define RELEASE_MASK (BUTTON_STOP)
#endif /* HAVE_RECORDER_KEYPAD */

/* using attribute not used by FAT */
#define TREE_ATTR_MPA 0x40 /* mpeg audio file */
#define TREE_ATTR_M3U 0x80 /* playlist */
#define TREE_ATTR_WPS 0x100 /* wps config file */
#define TREE_ATTR_MOD 0x200 /* firmware file */
#define TREE_ATTR_EQ  0x300 /* EQ config file */
#define TREE_ATTR_MASK 0xffd0 /* which bits tree.c uses (above + DIR) */

static int build_playlist(int start_index)
{
    int i;
    int start=start_index;

    playlist_clear();

    for(i = 0;i < filesindir;i++)
    {
        if(dircache[i].attr & TREE_ATTR_MPA)
        {
            DEBUGF("Adding %s\n", dircache[i].name);
            playlist_add(dircache[i].name);
        }
        else
        {
            /* Adjust the start index when se skip non-MP3 entries */
            if(i < start)
                start_index--;
        }
    }

    return start_index;
}

static int compare(const void* p1, const void* p2)
{
    struct entry* e1 = (struct entry*)p1;
    struct entry* e2 = (struct entry*)p2;
    
    if (( e1->attr & ATTR_DIRECTORY ) == ( e2->attr & ATTR_DIRECTORY ))
        if (global_settings.sort_case)
            return strncmp(e1->name, e2->name, MAX_PATH);
        else
            return strncasecmp(e1->name, e2->name, MAX_PATH);
    else 
        return ( e2->attr & ATTR_DIRECTORY ) - ( e1->attr & ATTR_DIRECTORY );
}

static int showdir(char *path, int start)
{
    int icon_type = 0;
#ifdef HAVE_LCD_BITMAP
    int line_height = LINE_HEIGTH;
#endif
    int i;
    int tree_max_on_screen;
    bool dir_buffer_full;
#ifdef LOADABLE_FONTS
    int fh;
    unsigned char *font = lcd_getcurrentldfont();
    fh  = ajf_get_fontheight(font);
    tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
    line_height = fh;
#else
    tree_max_on_screen = TREE_MAX_ON_SCREEN;
#endif


    /* new dir? cache it */
    if (strncmp(path,lastdir,sizeof(lastdir))) {
        DIR *dir = opendir(path);
        if(!dir)
            return -1; /* not a directory */

        name_buffer_length = 0;
        dir_buffer_full = false;
        
        for ( i=0; i<MAX_FILES_IN_DIR; i++ ) {
            int len;
            struct dirent *entry = readdir(dir);
            struct entry* dptr = &dircache[i];
            if (!entry)
                break;

            len = strlen(entry->d_name);

            /* skip directories . and .. */
            if ((entry->attribute & ATTR_DIRECTORY) &&
                (((len == 1) && 
                  (!strncmp(entry->d_name, ".", 1))) ||
                 ((len == 2) && 
                  (!strncmp(entry->d_name, "..", 2))))) {
                i--;
                continue;
            }

            /* Skip FAT volume ID */
            if (entry->attribute & ATTR_VOLUME_ID) {
                i--;
                continue;
            }

            /* Skip dotfiles if set to skip them */
            if (!global_settings.show_hidden_files &&
                ((entry->d_name[0]=='.') ||
                 (entry->attribute & ATTR_HIDDEN))) {
                i--;
                continue;
            }

            dptr->attr = entry->attribute;

            /* mark mp? and m3u files as such */
            if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) {
                if (!strcasecmp(&entry->d_name[len-4], ".mp3") ||
                   (!strcasecmp(&entry->d_name[len-4], ".mp2")) ||
		    (!strcasecmp(&entry->d_name[len-4], ".mpa")))
                    dptr->attr |= TREE_ATTR_MPA;
                else if (!strcasecmp(&entry->d_name[len-4], ".m3u"))
                    dptr->attr |= TREE_ATTR_M3U;
                else if (!strcasecmp(&entry->d_name[len-3], ".eq"))
                    dptr->attr |= TREE_ATTR_EQ;
                else if (!strcasecmp(&entry->d_name[len-4], ".wps"))
                    dptr->attr |= TREE_ATTR_WPS;
#ifdef HAVE_RECORDER_KEYPAD
                else if (!strcasecmp(&entry->d_name[len-4], ".ajz"))
#else
                else if (!strcasecmp(&entry->d_name[len-4], ".mod"))
#endif
                    dptr->attr |= TREE_ATTR_MOD;
            }

            /* filter non-mp3 or m3u files */
            if ( global_settings.mp3filter &&
                 (!(dptr->attr &
                    (ATTR_DIRECTORY|TREE_ATTR_MPA|TREE_ATTR_M3U))) ) {
                i--;
                continue;
            }

            if(len > NAME_BUFFER_SIZE - name_buffer_length - 1) {
                /* Tell the world that we ran out of buffer space */
                dir_buffer_full = true;
                break;
            }
            dptr->name = &name_buffer[name_buffer_length];
            strcpy(dptr->name,entry->d_name);
            name_buffer_length += len + 1;
        }
        filesindir = i;
        closedir(dir);
        strncpy(lastdir,path,sizeof(lastdir));
        lastdir[sizeof(lastdir)-1] = 0;
        qsort(dircache,filesindir,sizeof(struct entry),compare);

        if ( dir_buffer_full || filesindir == MAX_FILES_IN_DIR ) {
#ifdef HAVE_NEW_CHARCELL_LCD
            lcd_double_height(false);
#endif
            lcd_clear_display();
            lcd_puts(0,0,"Dir buffer");
            lcd_puts(0,1,"is full!");
            lcd_update();
            sleep(HZ*2);
            lcd_clear_display();
        }
    }

    if (start == -1)
    {
        int diff_files;

        /* use lastfile to determine start (default=0) */
        start = 0;

        for (i=0; i<filesindir; i++)
        {
            if (!strcasecmp(dircache[i].name, lastfile))
            {
                start = i;
                break;
            }
        }

        diff_files = filesindir - start;
        if (diff_files < tree_max_on_screen)
        {
            int oldstart = start;

            start -= (tree_max_on_screen - diff_files);
            if (start < 0)
                start = 0;

            dircursor = oldstart - start;
        }

        dirstart = start;
    }

    lcd_stop_scroll();
#ifdef HAVE_NEW_CHARCELL_LCD
    lcd_double_height(false);
#endif
    lcd_clear_display();
#ifdef HAVE_LCD_BITMAP
    lcd_setmargins(MARGIN_X,MARGIN_Y); /* leave room for cursor and icon */
    lcd_setfont(0);
#endif

    for ( i=start; i < start+tree_max_on_screen; i++ ) {
        int len;

        if ( i >= filesindir )
            break;

        len = strlen(dircache[i].name);

        switch ( dircache[i].attr & TREE_ATTR_MASK ) {
            case ATTR_DIRECTORY:
                icon_type = Folder;
                break;

            case TREE_ATTR_M3U:
                icon_type = Playlist;
                break;

            case TREE_ATTR_MPA:
                icon_type = File;
                break;

            case TREE_ATTR_WPS:
                icon_type = Wps;
                break;

            case TREE_ATTR_EQ:
                icon_type = Wps;
                break;

            case TREE_ATTR_MOD:
                icon_type = Mod_Ajz;
                break;

            default:
                icon_type = 0;
        }

        if (icon_type) {
#ifdef HAVE_LCD_BITMAP
            lcd_bitmap(bitmap_icons_6x8[icon_type], 
                       CURSOR_X * 6 + CURSOR_WIDTH, 
                       MARGIN_Y+(i-start)*line_height, 6, 8, true);
#else
            lcd_define_pattern((i-start)*8,tree_icons_5x7[icon_type],8);
            lcd_putc(LINE_X-1, i-start, i-start);
#endif
        }

        /* if MP3 filter is on, cut off the extension */
        if (global_settings.mp3filter && 
            (dircache[i].attr & (TREE_ATTR_M3U|TREE_ATTR_MPA)))
        {
            char temp = dircache[i].name[len-4];
            dircache[i].name[len-4] = 0;
            lcd_puts(LINE_X, i-start, dircache[i].name);
            dircache[i].name[len-4] = temp;
        }
        else
            lcd_puts(LINE_X, i-start, dircache[i].name);
    }

#ifdef HAVE_LCD_BITMAP
    if (global_settings.scrollbar) 
        scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
                  LCD_HEIGHT - SCROLLBAR_Y, filesindir, start,
                  start + tree_max_on_screen, VERTICAL);
#endif
    status_draw();
    return filesindir;
}

bool ask_resume(void)
{
#ifdef HAVE_NEW_CHARCELL_LCD
    lcd_double_height(false);
#endif

    /* always resume? */
    if ( global_settings.resume == RESUME_ON )
        return true;

    lcd_clear_display();
    lcd_puts(0,0,"Resume?");
#ifdef HAVE_LCD_CHARCELLS
    lcd_puts(0,1,"(Play/Stop)");
#else
    lcd_puts(0,1,"Play = Yes");
    lcd_puts(0,2,"Any other = No");
#endif
    lcd_update();
    if (button_get(true) == BUTTON_PLAY)
        return true;
    return false;
}

void start_resume(void)
{
    if ( global_settings.resume &&
         global_settings.resume_index != -1 ) {
        int len = strlen(global_settings.resume_file);

        DEBUGF("Resume file %s\n",global_settings.resume_file);
        DEBUGF("Resume index %X offset %X\n",
               global_settings.resume_index,
               global_settings.resume_offset);
        DEBUGF("Resume shuffle %s seed %X\n",
               global_settings.playlist_shuffle?"on":"off",
               global_settings.resume_seed);

        /* playlist? */
        if (!strcasecmp(&global_settings.resume_file[len-4], ".m3u")) {
            char* slash;

            /* check that the file exists */
            int fd = open(global_settings.resume_file, O_RDONLY);
            if(fd<0)
                return;
            close(fd);

            if (!ask_resume())
                return;
            
            slash = strrchr(global_settings.resume_file,'/');
            if (slash) {
                *slash=0;
                play_list(global_settings.resume_file,
                          slash+1,
                          global_settings.resume_index,
                          true, /* the index is AFTER shuffle */
                          global_settings.resume_offset,
                          global_settings.resume_seed );
                *slash='/';
            }
            else {
                /* check that the dir exists */
                DIR* dir = opendir(global_settings.resume_file);
                if(!dir)
                    return;
                closedir(dir);

                if (!ask_resume())
                    return;

                play_list("/",
                          global_settings.resume_file,
                          global_settings.resume_index,
                          true,
                          global_settings.resume_offset,
                          global_settings.resume_seed );
            }
        }
        else {
            if (!ask_resume())
                return;

            if (showdir(global_settings.resume_file, 0) < 0 )
                return;

            lastdir[0] = '\0';

            build_playlist(global_settings.resume_index);
            play_list(global_settings.resume_file,
                      NULL, 
                      global_settings.resume_index,
                      true,
                      global_settings.resume_offset,
                      global_settings.resume_seed);
        }

        status_set_playmode(STATUS_PLAY);
        status_draw();
        wps_show();
    }
}

void set_current_file(char *path)
{
    char *name;
    unsigned int i;

    /* separate directory from filename */
    name = strrchr(path,'/');
    if (name)
    {
        *name = 0;
        strcpy(currdir, path);
        *name = '/';
        name++;
    }
    else
    {
        strcpy(currdir, "/");
        name = path+1;
    }

    strcpy(lastfile, name);

    dircursor    =  0;
    dirstart     = -1;

    if (strncmp(currdir,lastdir,sizeof(lastdir)))
    {
        dirlevel            =  0;
        dirpos[dirlevel]    = -1;
        cursorpos[dirlevel] =  0;
        
        /* use '/' to calculate dirlevel */
        for (i=1; i<strlen(path)+1; i++)
        {
            if (path[i] == '/')
            {
                dirlevel++;
                dirpos[dirlevel]    = -1;
                cursorpos[dirlevel] =  0;
            }
        }
    }
}

bool dirbrowse(char *root)
{
    int numentries=0;
    char buf[MAX_PATH];
    int i;
    int lasti=-1;
    int button;
    int tree_max_on_screen;
    bool reload_root = false;
    bool lastfilter = global_settings.mp3filter;
    bool lastsortcase = global_settings.sort_case;
    bool lastshowhidden = global_settings.show_hidden_files;
#ifdef LOADABLE_FONTS
    int fh;
    unsigned char *font = lcd_getcurrentldfont();
    fh  = ajf_get_fontheight(font);
    tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
#else
    tree_max_on_screen = TREE_MAX_ON_SCREEN;
#endif

    dircursor=0;
    dirstart=0;
    dirlevel=0;

    memcpy(currdir,root,sizeof(currdir));

    start_resume();
    button_set_release(RELEASE_MASK);

    numentries = showdir(currdir, dirstart);
    if (numentries == -1) 
        return -1;  /* currdir is not a directory */

    put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);

    while(1) {
        struct entry* file = &dircache[dircursor+dirstart];

        bool restore = false;

        button = button_get_w_tmo(HZ/5);
        switch ( button ) {
            case TREE_EXIT:
            case TREE_EXIT | BUTTON_REPEAT:
                i=strlen(currdir);
                if (i>1) {
                    while (currdir[i-1]!='/')
                        i--;
                    strcpy(buf,&currdir[i]);
                    if (i==1)
                        currdir[i]=0;
                    else
                        currdir[i-1]=0;

                    dirlevel--;
                    if ( dirlevel < MAX_DIR_LEVELS ) {
                        dirstart = dirpos[dirlevel];
                        dircursor = cursorpos[dirlevel];
                    }
                    else
                        dirstart = dircursor = 0;

                    if (dirstart == -1)
                        strcpy(lastfile, buf);

                    restore = true;
                }
                break;

#ifdef HAVE_RECORDER_KEYPAD
            case BUTTON_OFF:
                mpeg_stop();
                status_set_playmode(STATUS_STOP);
                status_draw();
                restore = true;
                break;

            case BUTTON_OFF | BUTTON_REL:
#else
            case BUTTON_STOP | BUTTON_REL:
#endif
                global_settings.resume_index = -1;
                settings_save();
                break;
                

            case TREE_ENTER:
            case TREE_ENTER | BUTTON_REPEAT:
#ifdef HAVE_RECORDER_KEYPAD
            case BUTTON_PLAY:
            case BUTTON_PLAY | BUTTON_REPEAT: 
#endif
                if ( !numentries )
                    break;
                if ((currdir[0]=='/') && (currdir[1]==0)) {
                    snprintf(buf,sizeof(buf),"%s%s",currdir, file->name);
                } else {
                    snprintf(buf,sizeof(buf),"%s/%s",currdir, file->name);
                }

                if (file->attr & ATTR_DIRECTORY) {
                    memcpy(currdir,buf,sizeof(currdir));
                    if ( dirlevel < MAX_DIR_LEVELS ) {
                        dirpos[dirlevel] = dirstart;
                        cursorpos[dirlevel] = dircursor;
                    }
                    dirlevel++;
                    dircursor=0;
                    dirstart=0;
                } else {
                    int seed = current_tick;
                    bool play = false;
                    int start_index=0;
                    lcd_stop_scroll();
                    switch ( file->attr & TREE_ATTR_MASK ) {
                        case TREE_ATTR_M3U:
                            if ( global_settings.resume )
                                snprintf(global_settings.resume_file,
                                         MAX_PATH, "%s/%s",
                                         currdir, file->name);
                            play_list(currdir, file->name, 0, false, 0, seed );
                            start_index = 0;
                            play = true;
                            break;

                        case TREE_ATTR_MPA:
                            if ( global_settings.resume )
                                strncpy(global_settings.resume_file,
                                        currdir, MAX_PATH);
                            start_index = build_playlist(dircursor+dirstart);

                            /* it is important that we get back the index in
                               the (shuffled) list and stor that */
                            start_index = play_list(currdir, NULL,
                                                    start_index, false,
                                                    0, seed);
                            play = true;
                            break;

                            /* wps config file */
                        case TREE_ATTR_WPS:
                            snprintf(buf, sizeof buf, "%s/%s", 
                                     currdir, file->name);
                            wps_load_custom(buf);
                            restore = true;
                            break;

                        case TREE_ATTR_EQ:
                            snprintf(buf, sizeof buf, "%s/%s",
                                     currdir, file->name);
                            settings_load_eq(buf);
                            restore = true;
                            break;

#ifndef SIMULATOR
                            /* firmware file */
                        case TREE_ATTR_MOD:
                            snprintf(buf, sizeof buf, "%s/%s", 
                                     currdir, file->name);
                            rolo_load(buf);
                            break;
#endif
                    }

                    if ( play ) {
                        if ( global_settings.resume ) {
                            /* the resume_index must always be the index in the
                               shuffled list in case shuffle is enabled */
                            global_settings.resume_index = start_index;
                            global_settings.resume_offset = 0;
                            global_settings.resume_seed = seed;
                            settings_save();
                        }

                        status_set_playmode(STATUS_PLAY);
                        status_draw();
                        lcd_stop_scroll();
                        if ( wps_show() == SYS_USB_CONNECTED ) {
                            reload_root = true;
                            global_settings.resume_index = -1;
                        }
#ifdef LOADABLE_FONTS
                        tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
#else
                        tree_max_on_screen = TREE_MAX_ON_SCREEN;
#endif
                    }
                }
                restore = true;
                break;

            case TREE_PREV:
            case TREE_PREV | BUTTON_REPEAT:
            case BUTTON_VOL_UP:
                if(filesindir) {
                    if(dircursor) {
                        put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false);
                        dircursor--;
                        put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
                    }
                    else {
                        if (dirstart) {
                            dirstart--;
                            numentries = showdir(currdir, dirstart);
                            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
                        }
                        else {
                            if (numentries < tree_max_on_screen) {
                                put_cursorxy(CURSOR_X, CURSOR_Y + dircursor,
                                             false);
                                dircursor = numentries - 1;
                                put_cursorxy(CURSOR_X, CURSOR_Y + dircursor,
                                             true);
                            }
                            else {
                                dirstart = numentries - tree_max_on_screen;
                                dircursor = tree_max_on_screen - 1;
                                numentries = showdir(currdir, dirstart);
                                put_cursorxy(CURSOR_X, CURSOR_Y +
                                             tree_max_on_screen - 1, true);
                            }
                        }
                    }
                    lcd_update();
                }
                break;

            case TREE_NEXT:
            case TREE_NEXT | BUTTON_REPEAT:
            case BUTTON_VOL_DOWN:
                if(filesindir)
                {
                    if (dircursor + dirstart + 1 < numentries ) {
                        if(dircursor+1 < tree_max_on_screen) {
                            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor,
                                         false);
                            dircursor++;
                            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
                        } 
                        else {
                            dirstart++;
                            numentries = showdir(currdir, dirstart);
                            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
                        }
                    }
                    else {
                        if(numentries < tree_max_on_screen) {
                            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor,
                                         false);
                            dirstart = dircursor = 0;
                            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
                        } 
                        else {
                            dirstart = dircursor = 0;
                            numentries = showdir(currdir, dirstart);
                            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
                        }
                    }
                    lcd_update();
                }
                break;

            case TREE_MENU:
                lcd_stop_scroll();
                main_menu();
                restore = true;
                break;

            case BUTTON_ON:
                if (mpeg_is_playing())
                {
                    lcd_stop_scroll();
                    if (wps_show() == SYS_USB_CONNECTED)
                        reload_root = true;
#ifdef LOADABLE_FONTS
                    tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
#else
                    tree_max_on_screen = TREE_MAX_ON_SCREEN;
#endif
                    restore = true;
                }
                break;

#ifdef HAVE_RECORDER_KEYPAD
            case BUTTON_F2:
                if (f2_screen())
                    reload_root = true;
                restore = true;
                break;

            case BUTTON_F3:
                if (f3_screen())
                    reload_root = true;
#ifdef LOADABLE_FONTS
                tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
#else
                tree_max_on_screen = TREE_MAX_ON_SCREEN;
#endif
                restore = true;
                break;
#endif

#ifndef SIMULATOR
            case SYS_USB_CONNECTED: {
                backlight_time(4);

                /* Tell the USB thread that we are safe */
                DEBUGF("dirbrowse got SYS_USB_CONNECTED\n");
                usb_acknowledge(SYS_USB_CONNECTED_ACK);
                
                /* Wait until the USB cable is extracted again */
                usb_wait_for_disconnect(&button_queue);

                backlight_time(global_settings.backlight);
                
                /* Force a re-read of the root directory */
                reload_root = true;
            }
            break;
#endif
        }

        if ( button )
            ata_spin();
        
        /* do we need to rescan dir? */
        if (reload_root ||
            lastfilter != global_settings.mp3filter ||
            lastsortcase != global_settings.sort_case ||
            lastshowhidden != global_settings.show_hidden_files)
        {
            if ( reload_root ) {
                strcpy(currdir, "/");
                dirlevel = 0;
                reload_root = false;
            }
            dircursor = 0;
            dirstart = 0;
            lastdir[0] = 0;
            lastfilter = global_settings.mp3filter;
            lastsortcase = global_settings.sort_case;
            lastshowhidden = global_settings.show_hidden_files;
            restore = true;
        }

        if ( restore ) {
            /* restore display */
            /* We need to adjust if the number of lines on screen have
               changed because of a status bar change */
            if(CURSOR_Y+LINE_Y+dircursor>tree_max_on_screen) {
                dirstart++;
                dircursor--;
            }
            numentries = showdir(currdir, dirstart);
            put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
        }

        if ( numentries ) {
            i = dirstart+dircursor;

            /* if MP3 filter is on, cut off the extension */
            if(lasti!=i || restore) {
                lasti=i;
                lcd_stop_scroll();
                if (global_settings.mp3filter && 
                    (dircache[i].attr &
                     (TREE_ATTR_M3U|TREE_ATTR_MPA)))
                {
                    int len = strlen(dircache[i].name);
                    char temp = dircache[i].name[len-4];
                    dircache[i].name[len-4] = 0;
                    lcd_puts_scroll(LINE_X, dircursor, 
                                    dircache[i].name);
                    dircache[i].name[len-4] = temp;
                }
                else
                    lcd_puts_scroll(LINE_X, dircursor,
                                    dircache[i].name);
            }
        }
        status_draw();
        lcd_update();

    }

    return false;
}
