/*******************************************************************************
 *
 *      A U D I O   R E C O R D E R    M E N U   S Y S T E M
 *
 *      Copyright A Levido 2013 - All Rights Reserved
 *
 ******************************************************************************/
#include "menu.h"
#include "widgets.h"
#include "graphics.h"
#include "cdcrtc.h"
#include "ff.h"
#include "audio.h"
#include "touchscreen.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "./USB/usb.h"

#define ALTERNATIVE_PLAY_SCREEN_CONTROLS 1

#define _ERRLOC_
#ifdef _ERRLOC_
UINT8 errbuff[30];
#endif

/* Constants */
#define LFN_SIZE            260          // max long filename size
#define DFN_SIZE            24          // max file name chars to display
#define FMT_OFFSET          20          // offset of fmat chunk in .wav file
#define BPS_OFFSET          34          // offset of bit rate in .wav file
#define FILE_SIZE_OFFSET     4          // offset of file size in .wav file
#define DATA_SIZE_OFFSET    40          // offset of data size in .wav file
#define DATA_OFFSET         44          // offset of data chunk in .wav file
#define CLT_SIZE            32          // cluster link table size
#define SHOW_COUNTER         1          // show tape counter
#define HIDE_COUNTER         0          // hide tape counter
#define ON_DELAY      10000000
#define MAX_REC_RATE         8          // 96 kbps
#define BACKLIGHT_TIMEOUT    30         // ten seconds
#define SLEEP_TIMEOUT        60         // 15 seconds

/* directory entry */
typedef struct direntry{
    UINT8 type;
    UINT32 len;
    struct direntry* next;
    struct direntry* prev;
    UINT8 fname[LFN_SIZE];
}DIRENTRY;

enum { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND };
enum { IDLE, RECORD, PLAY, USB};

/* local functions */
INT8* getFileErrorMessage(FRESULT fres);
void getDfn(void);
void rollDfn(void);
void rollDpath(void);
void incCounter(void);
void clearCounter(void);
void errorHandler(UINT32 errcode);
UINT8 bcd2dec(UINT8 bcd);
UINT8 dec2bcd(UINT8 dec);
void noSDCard(void);
void doStopPlay(void);
void sleepWake();

SCREEN* currentScreen;

FATFS fs;                               // FAT file system
FILINFO fil;                            // File information data structure
DIR dir;                                // Directory information structure
FIL file;                               // File structure
INT8 volLabel[21];                      // Volume label
INT8 lfn[LFN_SIZE];                     // Long file name working buffer
INT8 dfn[DFN_SIZE+1];                   // Display file name
INT8 path[LFN_SIZE];
UINT32 pathlen;
INT8 dpath[LFN_SIZE + 2];
UINT32 dpathlen;
INT8 pathFile[LFN_SIZE];
DWORD clusterMap[CLT_SIZE];             // Cluster link table
DIRENTRY* currentFile;                  // Pointer to current filename stucture
DIRENTRY* playStartFile;                // Pointer to current filename stucture
UINT32 cfgIndex;                        // Index of current cinfig menu
INT32 dfnScrollIndex;                   // Index of scrolling display
INT32 dpathScrollIndex;                 // Index of scrolling display
UINT32 scrolldfn;                       // Flag true if display must scroll
UINT32 scrolldpath;                     // Flag true if display must scroll
UINT32 timerEnable;                     // Flag true if timer is running
INT8 counterText[9];                    // Counter text string 00:00:00
INT8 tickCount;                         // tick count
INT8 recFile[25];                       // record file name
UINT32 recRate;                         // record bit rate
UINT8 backlight;                        // backlight level
INT8 date[11];
INT8 time[9];
INT8 battery[5];
volatile UINT32 usbMode;
UINT32 unitToSet;
UINT32 backlightTimer;
UINT32 sleepTimer;
UINT32 mode;                            // recording, playing, idle or usb
UINT32 root;
UINT32 current_file_data_start_pos;
BOOL noSDCardAfterError, doMountAfterError, doStopPlayAfterError;

extern BOOL inInterruptHandler;
extern jmp_buf errJump, intJump;
UINT32 errSource;
/* Buttons ********************************************************************/
BUTTON sourceButton = {
   SOURCE_COL, "LINE", iconLineIn, FALSE, 0, 2, doSourceButton, TRUE
};
BUTTON configButton = {
    CONFIG_COL, "CONFIG", iconConfig, FALSE, 0, 0, doConfigButton, TRUE
};
BUTTON muteButton = {
    MUTE_COL, "MUTE", iconSpkUnmute, FALSE, 0, 1, doMuteButton, TRUE
};
BUTTON recordButton = {
    RECORD_COL, "RECORD", iconRecord, FALSE, 0, 0, doRecordButton, TRUE
};
BUTTON stopButton = {
    STOP_COL, "STOP", iconStop, FALSE, 0, 0, doStopButton, TRUE
};
BUTTON playButton = {
    PLAY_COL, "PLAY", iconPlay, FALSE, 0, 0, doPlayButton, TRUE
};
BUTTON prevButton = {
    PREV_COL, "PREV", iconPrev, FALSE, 0, 0, doPlayButton, TRUE
};
BUTTON nextButton = {
    NEXT_COL, "NEXT", iconNext, FALSE, 0, 0, doPlayButton, TRUE
};
BUTTON timeButton = {
    SET_TIME_COL, "SET TIME", iconClock, FALSE, 0, 0, doSetTime, TRUE
};
BUTTON dateButton = {
    SET_DATE_COL, "SET DATE", iconCal, FALSE, 0, 0, doSetDate, TRUE
};
BUTTON doneButton = {
    DONE_COL, "OK", 0, FALSE, 0, 0, doCfgBack, TRUE
};
BUTTON errReturnButton = {
    ERR_RTN_COL, "OK", 0, FALSE, 0, 0, doErrReturn, TRUE
};
BUTTON USBButton = {
    USB_COL, "", iconUSBon, TRUE, 0, 1, doUSBButton, TRUE
};
BUTTON pausePlayButton = {
    PAUSE_PLAY_COL, "", iconPause, FALSE, 0, 1, doPausePlay, TRUE
};
BUTTON skipPlayButton = {
    SKIP_PLAY_COL, "", iconSkipFwd, FALSE, 0, 0, doSkipFwd, TRUE
};
BUTTON skipBackPlayButton = {
    SKIP_PLAY_COL, "", iconSkipBack, FALSE, 0, 0, doSkipBck, TRUE
};
BUTTON pauseRecButton = {
    PAUSE_REC_COL, "", iconPause, FALSE, 0, 1, doPauseRec, TRUE
};
BUTTON skipRecButton = {
    SKIP_REC_COL, "", iconSkipFwd, FALSE, 0, 0, doSkipRec, TRUE
};
BUTTON sortButton = {
    SORT_COL, "", iconSort, FALSE, 0, 1, doSortButton, TRUE
};
BUTTON aboutButton = {
    SKIP_REC_COL, "ABOUT", 0, FALSE, 0, 0, doAbout, TRUE
};
BUTTON updateButton = {
    SKIP_REC_COL, "UPDATE", 0, FALSE, 0, 0, doUpdate, TRUE
};
BUTTON dirUpButton = {
    FILE_COL, 0, iconUp, TRUE, 0, 0, doDirUp, TRUE
};
BUTTON dirDnButton = {
    FILE_COL, 0, iconDown, TRUE, 0, 0, doDirDown, TRUE
};
BUTTON playSingleMultiButton = {
    PLAY_SINGLE_MULTI_COL, "", iconPlaySingle, FALSE, 0, 1, doPlaySingleMultiButton, TRUE
};

/* Clickers *******************************************************************/
CLICKER recLevelClicker = {
    REC_LVL_COL, "+00.00 dB", iconLineUp, iconLineDn, FALSE, doIncRecLvl, doDecRecLvl, TRUE, FALSE, TRUE
};
CLICKER fileClicker = {
    FILE_COL, "FILE", iconPlus, iconMinus, FALSE, doIncFile, doDecFile, TRUE, FALSE, FALSE
};
CLICKER volClicker = {
    VOL_COL, "-20 dB", iconVolUp, iconVolDn, FALSE, doIncVol, doDecVol, TRUE, FALSE, TRUE
};
CLICKER recRateClicker = {
    REC_RATE_COL, "48 kbps", iconPlus, iconMinus, FALSE, doIncRate, doDecRate, TRUE, FALSE, FALSE
};
CLICKER backlightClicker = {
    SET_BACKLIGHT_COL, "BACKLT", iconPlus, iconMinus, FALSE, doIncBacklight, doDecBacklight, TRUE, FALSE, TRUE
};
CLICKER hoursClicker = {
    SET_HR_COL, "HOUR", iconPlus, iconMinus, FALSE, doIncHours, doDecHours, TRUE, FALSE, TRUE
};
CLICKER minutesClicker = {
    SET_MIN_COL, "MIN", iconPlus, iconMinus, FALSE, doIncMins, doDecMins, TRUE, FALSE, TRUE
};
CLICKER secondsClicker = {
   SET_SEC_COL, "SEC", iconPlus, iconMinus, FALSE, doIncSecs, doDecSecs, TRUE, FALSE, TRUE
};
CLICKER yearsClicker = {
    SET_YR_COL, "YEAR", iconPlus, iconMinus, FALSE, doIncYears, doDecYears, TRUE, FALSE, TRUE
};
CLICKER monthsClicker = {
    SET_MON_COL, "MONTH", iconPlus, iconMinus, FALSE, doIncMonths, doDecMonths, TRUE, FALSE, TRUE
};
CLICKER daysClicker = {
    SET_DAY_COL, "DAY", iconPlus, iconMinus, FALSE, doIncDays, doDecDays, TRUE, FALSE, TRUE
};
SLIDER playbackSlider = {
    0, 16, 240, 54, 8, doSkipForward, doSkipBack
};

/* Screens ********************************************************************/
SCREEN readyScreen = {
     "READY",
    /*---------------------------------------------*/
    "No SD Card",
    "",
   { 0             ,   &fileClicker,   &volClicker },
   {0,                  0,              0,
    &dirUpButton,       0,              0,
    &dirDnButton,       0,              0,
    &USBButton,         &configButton,  &muteButton,
    &recordButton,      &stopButton,    &playButton },
   {&playbackSlider},
    time,               battery,        date,
    FALSE, REDRAW_ALL
};

SCREEN configScreen = {
     "CONFIGURE",
    /*---------------------------------------------*/
    "",
    "",
   { &recLevelClicker,  &recRateClicker,    &backlightClicker},
   { 0,                 0,                  0,
     0,                 0,                  0,
     0,                 0,                  0,
     &sourceButton,     &sortButton,        &aboutButton,
     &timeButton,       &doneButton,        &dateButton },
   {0},
     time,              battery,            date,
    FALSE, REDRAW_ALL
};

SCREEN timeScreen = {
     "SET TIME",
    /*---------------------------------------------*/
    "",
    "",
   { &hoursClicker,     &minutesClicker,    &secondsClicker },
   { 0,                 0,                  0,
     0,                 0,                  0,
     0,                 0,                  0,
     0,                 0,                  0,
     &dateButton,       &doneButton,        &configButton },
   {0},
     time,              battery,            date,
    FALSE, REDRAW_ALL
};

SCREEN dateScreen = {
     "SET DATE",
    /*---------------------------------------------*/
    "",
    "",
   { &yearsClicker,      &monthsClicker,      &daysClicker },
   { 0,                 0,                  0,
     0,                 0,                  0,
     0,                 0,                  0,
     0,                 0,                  0,
     &configButton,     &doneButton,        &timeButton },
   {0},
     time,              battery,            date,
    FALSE, REDRAW_ALL
};

SCREEN aboutScreen = {
     "ABOUT THIS VOICE RECORDER",
    /*---------------------------------------------*/
    "Software Version 01.02.00",
    "Copyright Silicon Chip",
   { 0,                 0,                  0, },
   { 0,                 0,                  0,
     0,                 0,                  0,
     0,                 &updateButton,      0,
     0,                 0,                  0,
     0,                 &doneButton,        0 },
   {0},
     time,              battery,            date,
    FALSE, REDRAW_ALL
};

/* Initialise */
void menuInit(void)
{
    UINT32 timer;
    backlight = 20;
    grInit(WHITE);
    grSetBacklight(backlight);

    cdcInit();
    auInit();
    clkInitialise();
    currentScreen = &readyScreen;
    currentFile = 0;
    dpathScrollIndex = 0;
    dfnScrollIndex = 0;
    cfgIndex = 0;
    fil.lfname = lfn;
    fil.lfsize = LFN_SIZE;
    clearCounter();
    timerEnable = 0;
    tickCount = 0;
    time[0] = 0;
    date[0] = 0;
    battery[0] = 0;
    recRate = 6;
    usbMode == DISCONNECTED;
    unitToSet = HOUR;
    backlightTimer = 0;
    mode = IDLE;
    root = TRUE;
}

/* Event Processing ***********************************************************/
void menuProcess(EVENT event)
{
    switch(event.type){
        case START:
            doMount();
            break;
        case TOUCH:
            if(backlightTimer == BACKLIGHT_TIMEOUT){ grSetBacklight(backlight); }
            backlightTimer = 0;
            sleepTimer = 0;
            processTouch(currentScreen, event.code, event.x, event.y);
            break;
        case TOUCH_RELEASE:
            processRelease(currentScreen);
            break;
        case TICK:
            if(event.code == ONE_SEC){
                if(sleepTimer == SLEEP_TIMEOUT){ 
                    sleepWake();
                    backlightTimer = 0;
                    sleepTimer = 0;
                    break;
                }
                else if((timerEnable == 0) && usbMode != ATTACHED ) { sleepTimer++; }
                if(backlightTimer == BACKLIGHT_TIMEOUT){ grSetBacklight(1); }
                else { backlightTimer++; }
                if(timerEnable) {
                    incCounter();
                    currentScreen->redraw |= REDRAW_LINE1;
                }
            }
            watchdogReset();
            if(tchGetPgood() == 0) { doPowerFail(); }
            currentScreen->batt = tchGetBatt();
            clkGetTimeString(time);
            clkGetDateString(date);
            currentScreen->redraw |= REDRAW_TIME;
            if(scrolldpath) rollDpath();
            if(scrolldfn) rollDfn();
            checkUSB();
            break;
        case SD_CARD:
            if(event.code == SD_IN) {
               doMount();
            }
            else if(event.code == SD_OUT) {
                noSDCard();
            }
            break;
        case AUDIO:
            if(event.code == REC_STOP) {
            }
            else if (event.code == PLAY_STOP) {
                doStopPlay();
            }
            else if (event.code == PLAY_STOP_ATEND) {
                if( playSingleMultiButton.icon == iconPlayMulti && currentFile->next != playStartFile ) {
                    doSkipFwd(0);
                } else {
                    doStopPlay();
                    mode = IDLE;
                    if( playSingleMultiButton.icon == iconPlayMulti )
                        doIncFile(0);
                }
            }
            break;

        case FS_ERROR:
            break;

        case ERROR:
            if(event.code == NO_SD){
                noSDCard();
            }
            else { errorHandler(event.code); }
            break;
        default:
            break;
        }
    drawWidgets(currentScreen);

}
void noSDCard(void)
{
    if( readyScreen.button[1] == &errReturnButton ) {
        noSDCardAfterError = TRUE;
        doMountAfterError = FALSE;
    } else {
        readyScreen.clicker[1]->inactive = TRUE;
        readyScreen.clicker[1]->redraw = TRUE;

        readyScreen.button[12]->inactive = TRUE;
        readyScreen.button[12]->redraw = TRUE;
        readyScreen.button[13]->inactive = TRUE;
        readyScreen.button[13]->redraw = TRUE;
        readyScreen.button[14]->inactive = TRUE;
        readyScreen.button[14]->redraw = TRUE;
        readyScreen.titleText = "NO SD CARD";
        readyScreen.line1Text = "";
        readyScreen.line2Text = "";
        readyScreen.redraw |= REDRAW_TITLE |REDRAW_LINE1 | REDRAW_LINE2;
    }
}

/* Incremet counter*/
void incCounter(void)
{
    counterText[8] = 0;
    if(++counterText[7] > '9'){
        counterText[7] = '0';
        if(++counterText[6] > '5'){
            counterText[6] = '0';
            if(++counterText[4] > '9'){
                counterText[4] = '0';
                if(++counterText[3] > '5'){
                    counterText[3] = '0';
                    if(++counterText[1] > '9'){
                        counterText[1] = '0';
                        if(++counterText[0] > '9'){
                            counterText[0] = '0';
                        }
                    }
                }
            }
        }
    }
    counterText[5] = ':';
    counterText[2] = ':';
    counterText[8] = 0;
}
/* Clear counter */
void clearCounter(void)
{
    counterText[0] = '0';
    counterText[1] = '0';
    counterText[2] = ':';
    counterText[3] = '0';
    counterText[4] = '0';
    counterText[5] = ':';
    counterText[6] = '0';
    counterText[7] = '0';
    counterText[8] = 0;
}
void setCounter(unsigned int value) {
    counterText[8] = 0;
    counterText[7] = '0' + value%10;
    value /= 10;
    counterText[6] = '0' + value%6;
    value /= 6;
    counterText[5] = ':';
    counterText[4] = '0' + value%10;
    value /= 10;
    counterText[3] = '0' + value%6;
    value /= 6;
    counterText[2] = ':';
    counterText[1] = '0' + value%10;
    value /= 10;
    counterText[0] = '0' + value%10;
}

UINT32 menuGetUSBMode(void)
{
    return usbMode;
}

/* Button Functions ***********************************************************/

void doMount(void)
{
    if( readyScreen.button[1] == &errReturnButton ) {
        noSDCardAfterError = FALSE;
        doMountAfterError = TRUE;
    } else {
        FRESULT fres;
        if(sdCardPresent() == 0) throwError(NO_SD, 101);
        /* Mount the SD card and read the volume label to initialise */
        if(disk_status(0) & STA_NOINIT){
            f_mount(0, &fs);
            fres = f_getlabel("", volLabel, 0);
            if(fres)throwError(fres, 102);
        }
        path[0] = 0;
        pathlen = 0;
        dpath[0] = 0;
        doReadFiles();
    }
}
int compareDIRENTRYp(const void* a, const void* b) {
    const DIRENTRY* pa = *((const DIRENTRY**)a);
    const DIRENTRY* pb = *((const DIRENTRY**)b);
    return strcmp(pa->fname, pb->fname);
}
void doSortFiles(DIRENTRY* pFile, int num)
{
    DIRENTRY** ppEntries;
    if( num == -1 ) {
        DIRENTRY* pTemp = pFile->next;
        num = 1;
        while( pTemp != pFile ) {
            pTemp = pTemp->next;
            ++num;
        }
    }
    ppEntries = malloc(num * sizeof(*ppEntries));
    if( ppEntries ) {
        int i;
        for( i = 0; i < num; ++i ) {
            ppEntries[i] = pFile;
            pFile = pFile->next;
        }
        qsort(ppEntries, num, sizeof(*ppEntries), &compareDIRENTRYp);
        for( i = 0; i < num; ++i ) {
            ppEntries[i]->prev = ppEntries[i == 0 ? num-1 : i-1];
            ppEntries[i]->next = ppEntries[i == num-1 ? 0 : i+1];
        }
        free(ppEntries);
    }
}
void doUnsortFiles()
{
    UINT8 currentFile_fname[LFN_SIZE];
    UINT8 playStartFile_fname[LFN_SIZE];
    DIRENTRY* pTemp;
    strcpy(currentFile_fname, currentFile->fname);
    if( playStartFile )
        strcpy(playStartFile_fname, playStartFile->fname);
    doReadFiles();
    pTemp = currentFile;
    while( currentFile && currentFile->next && currentFile->next != pTemp && strcmp(currentFile_fname, currentFile->fname) )
        currentFile = currentFile->next;
    if( pTemp != currentFile ) {
        getDfn();
        readyScreen.line2Text = dfn;
        if( mode == PLAY || mode == RECORD )
            readyScreen.line1Text = counterText;
        else
            readyScreen.line1Text = dpath;
    } else if( mode == PLAY || mode == RECORD ) {
        readyScreen.line1Text = counterText;
    }
    if( playStartFile ) {
        playStartFile = currentFile;
        while( playStartFile->next != currentFile && playStartFile->next && playStartFile->next != playStartFile && strcmp(playStartFile_fname, playStartFile->fname) )
            playStartFile = playStartFile->next;
        if( strcmp(playStartFile_fname, playStartFile->fname) )
            playStartFile = 0;
    }
}
void doReadFiles(void)
{
    FRESULT fres;
    INT8* fn;
    DIRENTRY* entry;
    int numFiles, len;
    BOOL needs_sorting = FALSE;


    /* if there are already file, release the memory */
    if(currentFile){
        entry = currentFile;
        do{
            free(entry);
            entry = entry->next;
        }while(entry != currentFile);
    }
    currentFile = 0;

    /* Now open the current directory and read the files into a linked list */
    fres = f_opendir(&dir, path);             // curent directory
    if(fres)throwError(fres, 103);
    numFiles = 0;
    do{
        fres = f_readdir(&dir, &fil);
        if(fres)throwError(fres, 1044);
        if(fil.fname[0] == 0) break;        // end of directory
        if(fil.fname[0] == '.')continue;    // skip dot entries
        if(fil.fattrib & AM_HID)continue;   // skip  hidden

        fn = *fil.lfname ? fil.lfname : fil.fname;
        len = strlen(fn);
        entry = malloc(sizeof(DIRENTRY) + len + 1 - sizeof(entry->fname));
        if(entry){                          // enqueue
            strcpy(entry->fname, fn);
            entry->len = len;
            entry->type = fil.fattrib & AM_DIR;
            if(currentFile == 0){
                currentFile = entry;
                currentFile->next = entry;
                currentFile->prev = entry;
            }
            else{
                currentFile->prev->next = entry;
                entry->prev = currentFile->prev;
                currentFile->prev = entry;
                entry->next = currentFile;
                if( sortButton.icon == iconSort && !needs_sorting && strcmp(currentFile->fname, entry->fname) < 0 )
                    needs_sorting = TRUE;
            }
            ++numFiles;
        }
        else throwError(MALLOC, 105);
    }while(1);

    if( sortButton.icon == iconSort && currentFile && needs_sorting )
        doSortFiles(currentFile, numFiles);

    if(currentFile){
         getDfn();
         readyScreen.line2Text = dfn;
         readyScreen.line1Text = dpath;
         //readyScreen.button[14]->inactive = FALSE;
     }
     else {
         readyScreen.line2Text = "Directory Empty";
         readyScreen.button[14]->inactive = TRUE;
     }

     readyScreen.clicker[1]->inactive = FALSE;
     readyScreen.clicker[1]->redraw = TRUE;

     readyScreen.button[12]->inactive = FALSE;
     readyScreen.button[12]->redraw = TRUE;
     readyScreen.button[13]->inactive = TRUE;
     readyScreen.button[13]->redraw = TRUE;
     readyScreen.button[14]->redraw = TRUE;
     readyScreen.titleText = "READY";
     readyScreen.redraw |= (REDRAW_TITLE | REDRAW_LINE1 | REDRAW_LINE2);
}

BOOL goToFile(const char* name) {
    DIRENTRY* startFile = currentFile;
    do {
        if( !strcmp(currentFile->fname, name) )
            return TRUE;
        currentFile = currentFile->next;
    } while( currentFile != startFile );
    return FALSE;
}
void doDirUp(UINT16 code)
{
    char* ptr;

    if(root == TRUE) return;
    ptr = strrchr(path, '/');
    if(ptr == NULL || ptr == strchr(path, '/')) {
        path[0] = 0;
        pathlen = 0;
        root = TRUE;
    }
    else{
        *ptr = 0;
        pathlen = ptr - (char*)path;
    }
    doReadFiles();
    if( ptr ) {
        if( goToFile(ptr+1) ) {
            getDfn();
            readyScreen.line2Text = dfn;
        }
    }
}

void doDirDown(UINT16 code)
{
    if(pathlen + strlen(currentFile->fname) > LFN_SIZE - 2){
        throwError(PATH_LONG, 150);
    }
    strcat(path, "/");
    strcat(path, currentFile->fname);
    pathlen += currentFile->len + 1;
    root = FALSE;
    doReadFiles();
}

void updateDpath(void)
{
    UINT16 n;

    strcpy(dpath, volLabel);
    strcat(dpath, ":");
    n = strlen(dpath);
    if(pathlen + n > sizeof(dpath) - 1){
        strncpy( dpath + n, path, sizeof(dpath) - n - 1 );
        dpath[sizeof(dpath) - 1] = '\0';
        dpathlen = sizeof(dpath) - 1;
    }
    else {
        strcat(dpath, path);
        dpathlen = n + pathlen;
    }

    if( dpathlen > DFN_SIZE )
        scrolldpath = 1;
    else
        scrolldpath = 0;
}

void getDfn(void)
{
//    UINT16 n, m;

    /* File Name */
    if(currentFile->len <= DFN_SIZE){
        strcpy(dfn, currentFile->fname);
        scrolldfn = 0;
    }
    else{
        strncpy(dfn, currentFile->fname, DFN_SIZE);
        dfn[DFN_SIZE] = 0;
        scrolldfn = 1;
    }
    dfnScrollIndex = -3;
    
    /* Path Name */
    updateDpath();
    dpathScrollIndex = -3;

    /* display directory */
    if(currentFile->type & AM_DIR){
        dirDnButton.inactive = FALSE;
        readyScreen.button[14]->inactive = TRUE;
    }
    else {
        dirDnButton.inactive = TRUE;
        readyScreen.button[14]->inactive = FALSE;
    }
    if(root == TRUE){
        dirUpButton.inactive = TRUE;
    }
    else {
        dirUpButton.inactive = FALSE;
    }
    dirDnButton.redraw = TRUE;
    dirUpButton.redraw = TRUE;
    readyScreen.button[14]->redraw = TRUE;
    readyScreen.redraw |= ( REDRAW_LINE1 | REDRAW_LINE2);
}

void rollDpath(void)
{
    updateDpath();
    if( dpathScrollIndex > 0 )
        memmove(dpath, dpath + (dpathScrollIndex > dpathlen - DFN_SIZE + 3 ? dpathlen - DFN_SIZE + 3 : dpathScrollIndex), dpathlen - dpathScrollIndex + 1);
    dpath[DFN_SIZE] = '\0';
    if( dpathScrollIndex == -3 || (dpathScrollIndex > 0 && dpathScrollIndex < dpathlen - DFN_SIZE + 3) )
        currentScreen->redraw |= REDRAW_LINE1;
    if(++dpathScrollIndex > (int)dpathlen - DFN_SIZE + 6)
        dpathScrollIndex = -3;
}

void rollDfn(void)
{
    strncpy(dfn, currentFile->fname + (dfnScrollIndex > 0 ? (dfnScrollIndex > currentFile->len - DFN_SIZE + 3 ? currentFile->len - DFN_SIZE + 3 : dfnScrollIndex) : 0), DFN_SIZE);
    if( dfnScrollIndex == -3 || (dfnScrollIndex > 0 && dfnScrollIndex < currentFile->len - DFN_SIZE + 3) )
        currentScreen->redraw |= REDRAW_LINE2;
    if(++dfnScrollIndex > (int)currentFile->len - DFN_SIZE + 6)
        dfnScrollIndex = -3;
}

void doConfigButton(UINT16 code)
{
    currentScreen = &configScreen;
    currentScreen->redraw |= REDRAW_ALL;
}
void doCfgBack(UINT16 code)
{
    currentScreen = &readyScreen;
    currentScreen->redraw |= REDRAW_ALL;
}
void doIncRate(UINT16 code)
{
    recRate = recRate < MAX_REC_RATE ? recRate + 1 : MAX_REC_RATE;
    recRateClicker.text = cdcGetRateString(recRate);
    recRateClicker.redraw = TRUE;
}
void doDecRate(UINT16 code)
{
    recRate = recRate > 0 ? recRate - 1 : 0;
    recRateClicker.text = cdcGetRateString(recRate);
    recRateClicker.redraw = TRUE;
}

void doIncBacklight(UINT16 code)
{
    backlight = backlight < 31 ? backlight + 1 : 31;
    grSetBacklight(backlight);
}
void doDecBacklight(UINT16 code)
{
    backlight = backlight > 0 ? backlight - 1 : 0;
    grSetBacklight(backlight);
}

void doPlayButton(UINT16 code)
{
    FRESULT fres;
    UINT32 dword;
    UINT16 word;
    UINT n;
    UINT32 temp;

    if(currentFile == 0) return; /* No File selected */
    if(pathlen + strlen(currentFile->fname) > LFN_SIZE - 2){
        throwError(PATH_LONG, 151);
    }
    strcpy(pathFile, path);
    strcat(pathFile, "/" );
    strcat(pathFile, currentFile->fname);
    mode = PLAY;

    cdcRestart();
    fres = f_open(&file, pathFile, FA_OPEN_EXISTING | FA_READ);
    if(fres) throwError(fres, 106);
    /* RIFF */
    fres = f_read(&file, &dword, 4, &n);
    if(fres) throwError(fres, 107);
    if(dword != 0x46464952) throwError(NO_RIFF, 108);
    /* RIFF chunk size */
    fres = f_read(&file, &dword, 4, &n);
    if(fres) throwError(fres, 109);
    /* WAVE */
    fres = f_read(&file, &dword, 4, &n);
    if(fres) throwError(fres, 110);

    if(dword != 0x45564157) throwError(NO_WAVE, 111);
    /* fmt */
    fres = f_read(&file, &dword, 4, &n);
    if(fres) throwError(fres, 112);
    if(dword != 0x20746d66) throwError(NO_FMT, 113);
    /* fmt chunk size -> temp */
    fres = f_read(&file, &temp, 4, &n);
    if(fres) throwError(fres, 114);
    /* Audio format */
    fres = f_read(&file, &word, 2, &n);
    if(word != 0x0001) throwError(NOT_PCM, 115);
    /* Num Channels */
    fres = f_read(&file, &word, 2, &n);
    if(fres) throwError(fres, 116);
    if(word != 0x0002) throwError(NOT_2CH, 117);
    /* Sample Rate */
    fres = f_read(&file, &dword, 4, &n);
    if(fres) throwError(fres, 118);
    switch(dword){
        case  8000: n = 0; break;
        case 16000: n = 1; break;
        case 22050: n = 2; break;
        case 24000: n = 3; break;
        case 32000: n = 4; break;
        case 44100: n = 5; break;
        case 48000: n = 6; break;
        case 88200: n = 7; break;
        case 96000: n = 8; break;
        default: n = 99; break;
    }
    if(n == 99) { throwError(BAD_RATE, 119); }
    else cdcSetRate(n);
    /* Bits per sample (only 16 bit supported so far) */
    fres = f_lseek(&file, BPS_OFFSET);
    if(fres) throwError(fres, 121);
    fres = f_read(&file, &word, 2, &n);
    if(fres) throwError(fres, 122);
    if(word != 16) throwError(BAD_DEPTH, 123);
    cdcSetBitDepth(0);
    /* keep skipping chunks till get to data */
    temp += FMT_OFFSET;
    fres = f_lseek(&file, temp);
    if(fres) throwError(fres, 124);
    fres = f_read(&file, &dword, 4, &n);
    if(fres) throwError(fres, 125);
    if(dword != 0x61746164) {
        fres = f_read(&file, &dword, 4, &n);
        if(fres) throwError(fres, 126);
        temp += (dword + 4);
        fres = f_lseek(&file, temp);
        if(fres) throwError(fres, 126);
    }
    /* data chunk size */
    fres = f_read(&file, &dword, 4, &n);
    if(fres) throwError(fres, 127);
    /* dataSize = dword; */
    /* position file poiner to data start */
    fres = f_lseek(&file, temp + 8);
    if(fres) throwError(fres, 128);
    current_file_data_start_pos = temp+8;

    file.cltbl = clusterMap;
    clusterMap[0] = CLT_SIZE;
    fres = f_lseek(&file, CREATE_LINKMAP);
    if(fres) throwError(fres, 129);

    dirUpButton.inactive = TRUE;
    dirUpButton.redraw = TRUE;
    dirDnButton.inactive = TRUE;
    dirDnButton.redraw = TRUE;
    readyScreen.titleText = "PLAYING";
#ifndef ALTERNATIVE_PLAY_SCREEN_CONTROLS
    readyScreen.clicker[1]->inactive = TRUE;
    readyScreen.clicker[1]->redraw = TRUE;
#endif
//    readyScreen.button[9]->inactive = TRUE;
#ifndef ALTERNATIVE_PLAY_SCREEN_CONTROLS
    readyScreen.button[6] = &playSingleMultiButton;
    readyScreen.button[6]->redraw = TRUE;
    readyScreen.button[9] = &skipBackPlayButton;
    readyScreen.button[9]->redraw = TRUE;
#endif
    readyScreen.button[13]->inactive = FALSE;
    readyScreen.button[13]->redraw = TRUE;
    readyScreen.line1Text = counterText;
#ifndef ALTERNATIVE_PLAY_SCREEN_CONTROLS
    readyScreen.button[12] = &skipPlayButton;
    readyScreen.button[12]->redraw = TRUE;
#else
    readyScreen.button[12] = &playSingleMultiButton;
    readyScreen.button[12]->redraw = TRUE;
#endif
    readyScreen.button[14] = &pausePlayButton;
    readyScreen.button[14]->redraw = TRUE;
    readyScreen.redraw |= (REDRAW_TITLE | REDRAW_LINE1);
    clearCounter();
    timerEnable = 1;
    if( !code )
        playStartFile = currentFile;
    auStartPlay( );
    
}
void doStopButton(UINT16 code)
{
    auStopPlay(FALSE);
    mode = IDLE;
}
void doStopPlay(void)
{
    clearCounter();
    timerEnable = 0;
    if( readyScreen.button[1] != &errReturnButton ) {
        readyScreen.titleText = "READY";
        readyScreen.line1Text = dpath;
        readyScreen.redraw = REDRAW_LINE1;
        if(currentFile->type & AM_DIR){
            dirDnButton.inactive = FALSE;
            readyScreen.button[14]->inactive = TRUE;
            dirDnButton.redraw = TRUE;
        }
        else {
            dirDnButton.inactive = TRUE;
            readyScreen.button[14]->inactive = FALSE;
        }
        if(root == TRUE){
            dirUpButton.inactive = TRUE;
        }
        else {
            dirUpButton.inactive = FALSE;
            dirUpButton.redraw = TRUE;
        }
        readyScreen.clicker[1]->inactive = FALSE;
        readyScreen.clicker[1]->redraw = TRUE;
        if(usbMode == ATTACHED) readyScreen.button[9]->inactive = FALSE;
#ifndef ALTERNATIVE_PLAY_SCREEN_CONTROLS
        readyScreen.button[6] = &dirDnButton;
        readyScreen.button[6]->redraw = TRUE;
        readyScreen.button[9] = &USBButton;
        readyScreen.button[9]->redraw = TRUE;
#endif
        readyScreen.button[13]->inactive = TRUE;
        readyScreen.button[13]->redraw = TRUE;
        readyScreen.button[12] = &recordButton;
        readyScreen.button[12]->redraw = TRUE;
        readyScreen.button[14] = &playButton;
        readyScreen.button[14]->redraw = TRUE;
        readyScreen.redraw |= (REDRAW_TITLE | REDRAW_LINE1);
    } else {
        doStopPlayAfterError = TRUE;
    }
//    doReadFiles();
}

void doPausePlay(UINT16 code)
{
    if(code == 1 ){             // pause
        timerEnable = 0;
        readyScreen.titleText = "PLAY PAUSED";
        readyScreen.button[14]->icon = iconPlay;
    auPause();
    }
    else if(code == 0) {        // resume
        timerEnable = 1;
        readyScreen.titleText = "PLAYING";
        readyScreen.button[14]->icon = iconPause;
    auResume();
    }
    readyScreen.button[14]->redraw = TRUE;
    readyScreen.redraw |= (REDRAW_TITLE | REDRAW_LINE1);
}
void doSkipFwd(UINT16 code)
{
    stopPlay();
    doIncFile(0);
    doPlayButton(1);
}
void doSkipBck(UINT16 code)
{
    stopPlay();
    doDecFile(0);
    doPlayButton(1);
}
void doRecordButton(UINT16 code)
{
    FRESULT fres;
    UINT64 qword;
    UINT32 dword;
    UINT16 word;
    UINT n;

    if( mode == RECORD )
        return;

    if(pathlen  > LFN_SIZE - 20){
        throwError(PATH_LONG, 152);
    }
    
    strcpy(recFile, "REC");
    clkAppendTimestamp(recFile);
    strcat(recFile, ".wav");
    
    strcpy(pathFile, path);
    strcat(pathFile, "/");
    strcat(pathFile, recFile);

    mode = RECORD;
    cdcRestart();
    /* Create and open the file */
    fres = f_open(&file, pathFile, FA_CREATE_NEW | FA_WRITE);
    if(fres) throwError(fres, 130);
    /* write RIFF */
    qword = 0x0000000046464952;
    fres = f_write(&file, &qword, 8, &n);
    if(fres) throwError(fres, 131);
    if(n < 8) throwError(DISK_FULL, 132);
    /* write WAVEfmt */
    qword = 0x20746d6645564157;
    fres = f_write(&file, &qword, 8, &n);
    if(fres) throwError(fres, 133);
    if(n < 8) throwError(DISK_FULL, 134);
    /* write fmt size */
    dword = 16;
    fres = f_write(&file, &dword, 4, &n);
    if(fres) throwError(fres, 135);
    if(n < 4) throwError(DISK_FULL, 136);
    /* write PCM & Channels */
    dword = 0x00020001;
    fres = f_write(&file, &dword, 4, &n);
    if(fres) throwError(fres, 137);
    if(n < 4) throwError(DISK_FULL, 138);
    /* Write sample rate */
    switch(recRate){
        case 0: dword = 8000; break;
        case 1: dword = 16000; break;
        case 2: dword = 22050; break;
        case 3: dword = 24000; break;
        case 4: dword = 32000; break;
        case 5: dword = 44100; break;
        case 6: dword = 48000; break;
        case 7: dword = 88200; break;
        case 8: dword = 96000; break;
    }
    fres = f_write(&file, &dword, 4, &n);
    if(fres) throwError(fres, 139);
    if(n < 4) throwError(DISK_FULL, 140);
     /* Write byte rate */
    dword = dword * 4;
    if(fres) throwError(fres, 141);
    fres = f_write(&file, &dword, 4, &n);
    if(n < 4) throwError(DISK_FULL, 142);
     /* Write alignement */
    word = 2 * 16 / 8;
    fres = f_write(&file, &word, 2, &n);
    if(fres) throwError(fres, 143);
    if(n < 2) throwError(DISK_FULL, 144);
    /* Write BPS */
    word = 16;
    fres = f_write(&file, &word, 2, &n);
    if(fres) throwError(fres, 145);
    if(n < 2) throwError(DISK_FULL, 145);
    /* Write data */
    qword = 0x0000000061746164;
    fres = f_write(&file, &qword, 8, &n);
    if(fres) throwError(fres, 146);
    if(n < 8) throwError(DISK_FULL, 147);
/*
    dword = 0;
    word = (32768-44) / sizeof(dword);
    while( word-- ) {
        fres = f_write(&file, &dword, 4, &n);
        if(fres) throwError(fres, 145);
        if(n < 4) throwError(DISK_FULL, 142);
    }
*/

    cdcSetRate(recRate);
    cdcSetMonitor(1);
    dirUpButton.inactive = TRUE;
    dirUpButton.redraw = TRUE;
    dirDnButton.inactive = TRUE;
    dirDnButton.redraw = TRUE;
    readyScreen.titleText = "RECORDING";
    readyScreen.line2Text = recFile;
    readyScreen.clicker[1]->inactive = TRUE;
    readyScreen.clicker[1]->redraw = TRUE;
    readyScreen.button[9]->inactive = TRUE;
    readyScreen.button[9]->redraw = TRUE;
    readyScreen.button[13]->inactive = FALSE;
    readyScreen.button[13]->redraw = TRUE;
    readyScreen.line1Text = counterText;
    readyScreen.button[12] = &skipRecButton;
    readyScreen.button[12]->redraw = TRUE;
    readyScreen.button[14] = &pauseRecButton;
    readyScreen.button[14]->redraw = TRUE;
    readyScreen.button[13]->fun = doStopRec;
    readyScreen.redraw |= (REDRAW_TITLE | REDRAW_LINE1 | REDRAW_LINE2);

    clearCounter();
    timerEnable = 1;
    auStartRec();
}

void doStopRec(UINT16 code)
{
    auStopRec(code > 0);
    mode = IDLE;
    timerEnable = 0;
    clearCounter();
    readyScreen.titleText = "READY";
    readyScreen.line1Text = dpath;
    readyScreen.redraw = REDRAW_LINE1;
    readyScreen.clicker[1]->inactive = FALSE;
    readyScreen.clicker[1]->redraw = TRUE;
    if(usbMode == ATTACHED) readyScreen.button[9]->inactive = FALSE;
    readyScreen.button[9]->redraw = TRUE;
    readyScreen.button[13]->inactive = TRUE;
    readyScreen.button[13]->redraw = TRUE;
    readyScreen.button[12] = &recordButton;
    readyScreen.button[12]->redraw = TRUE;
    readyScreen.button[14] = &playButton;
    readyScreen.button[14]->redraw = TRUE;
    readyScreen.button[13]->fun = doStopButton;
    readyScreen.redraw |= (REDRAW_TITLE | REDRAW_LINE1);

    cdcSetMonitor(0);
    doReadFiles();
}
void doPauseRec(UINT16 code)
{
    if(code == 1) {                 // pause
        readyScreen.titleText = "RECORD PAUSED";
        readyScreen.button[14]->icon = iconPlay;
        timerEnable = 0;
        auPause();
    }
    else if(code ==0 ) {            // resume
        readyScreen.titleText = "RECORDING";
        readyScreen.button[14]->icon = iconPause;
        timerEnable = 1;
        auResume();
    }
    readyScreen.button[14]->redraw = TRUE;
    readyScreen.redraw |= (REDRAW_TITLE | REDRAW_LINE1);
}

void doSkipRec(UINT16 code)
{
    // too easy to press it immediately after pressing record so ignore that.
    if( counterText[0] == '0' && counterText[1] == '0' && counterText[3] == '0' && counterText[4] == '0' && counterText[6] == '0' && counterText[7] == '0' )
        return;

    doStopRec(0);
    doRecordButton(0);
}


void doMuteButton(UINT16 state)
{
    if(state == 0){ //No mute
        muteButton.icon = iconSpkUnmute;
    }
    else { // Mute
        muteButton.icon = iconSpkMute;
    }
    cdcSetOutputMute(state);
    cdcSetInputMute(state);
    muteButton.redraw = TRUE;
}

void doIncRecLvl(UINT16 code)
{ 
   recLevelClicker.text = cdcIncLineLevel();
   recLevelClicker.update = TRUE;
}
void doDecRecLvl(UINT16 code)
{ 
   recLevelClicker.text = cdcDecLineLevel();
   recLevelClicker.update = TRUE;
}

void doSourceButton(UINT16 code)
{ 
    switch(code){
        case 0:         // Line
            cdcSetSource(0);
            sourceButton.icon = iconLineIn;
            break;
        case 1:         // Mic no boost
            cdcSetSource(1);
            cdcSetMicBoost(0);
            sourceButton.icon = iconMic;
            break;
        case 2:
            cdcSetSource(1);
            cdcSetMicBoost(1);
            sourceButton.icon = iconMicBoost;
            break;
    }
    sourceButton.redraw = TRUE;
}

void doSortButton(UINT16 code)
{
    switch(code){
        case 0:
            sortButton.icon = iconSort;
            doSortFiles(currentFile, -1);
            break;
        case 1:
            sortButton.icon = iconNoSort;
            doUnsortFiles(currentFile);
            break;
    }
    sortButton.redraw = TRUE;
}

void doPlaySingleMultiButton(UINT16 code)
{
    switch(code){
        case 0:
            playSingleMultiButton.icon = iconPlaySingle;
            break;
        case 1:
            playSingleMultiButton.icon = iconPlayMulti;
            break;
    }
    playSingleMultiButton.redraw = TRUE;
}

void doIncVol(UINT16 code)
{ 
    volClicker.text = cdcIncVolume();
    volClicker.update = TRUE;
}
void doDecVol(UINT16 code)
{ 
    volClicker.text = cdcDecVolume();
    volClicker.update = TRUE;
}

void doIncFile(UINT16 code)
{
#ifdef ALTERNATIVE_PLAY_SCREEN_CONTROLS
    BOOL inPlayMode = (mode == PLAY);
    if( inPlayMode )
        stopPlay();
#endif

    if(currentFile == 0)
        return;
    if( currentFile->next && currentFile->next != currentFile ) {
        currentFile = currentFile->next;
    } else {
        while( currentFile->prev && currentFile->prev != currentFile )
            currentFile = currentFile->prev;
    }
    getDfn();
    dfnScrollIndex = -3;
    //fileClicker.redraw = TRUE;
    readyScreen.redraw |= ( REDRAW_LINE2 );

#ifdef ALTERNATIVE_PLAY_SCREEN_CONTROLS
    if( inPlayMode )
        doPlayButton(1);
#endif
}
void doDecFile(UINT16 code)
{
#ifdef ALTERNATIVE_PLAY_SCREEN_CONTROLS
    BOOL inPlayMode = (mode == PLAY);
    if( inPlayMode )
        stopPlay();
#endif

    if(currentFile == 0)
        return;
    if( currentFile->prev && currentFile->prev != currentFile ) {
        currentFile = currentFile->prev;
    } else {
        while( currentFile->next && currentFile->next != currentFile )
            currentFile = currentFile->next;
    }
    getDfn();
    dfnScrollIndex = -3;
    //fileClicker.redraw = TRUE;
    readyScreen.redraw |= ( REDRAW_LINE2 );

#ifdef ALTERNATIVE_PLAY_SCREEN_CONTROLS
    if( inPlayMode )
        doPlayButton(1);
#endif
}
void doSkipForward(UINT16 code)
{
    if( mode == PLAY ) {
        UINT32 intStatus = INTDisableInterrupts();
        int sampleRate = cdcGetSampleRate();
        if( sampleRate > 0 ) {
            // skip ahead 5 seconds
            f_lseek(&file, file.fptr + sampleRate * 4 * 5);
            setCounter( (file.fptr - current_file_data_start_pos) / (sampleRate * 4) );
            currentScreen->redraw |= REDRAW_LINE1;
        }
        INTRestoreInterrupts(intStatus);
    }
}
void doSkipBack(UINT16 code)
{
    if( mode == PLAY ) {
        UINT32 intStatus = INTDisableInterrupts();
        int sampleRate = cdcGetSampleRate();
        if( sampleRate > 0 ) {
            // skip back 5 seconds
            int pos = file.fptr - sampleRate * 4 * 5;
            if( pos < (int)current_file_data_start_pos )
                pos = current_file_data_start_pos;
            f_lseek(&file, pos);
            setCounter( (file.fptr - current_file_data_start_pos) / (sampleRate * 4) );
            currentScreen->redraw |= REDRAW_LINE1;
        }
        INTRestoreInterrupts(intStatus);
    }
}
void checkUSB(void)
{
    switch(usbMode){
        case DISCONNECTED:
            if(tchGetVUSB() == TRUE){   // must be plugged in
                usbMode = CONNECTED;
                USBDeviceInit();
                USBDeviceAttach();      // try attching
            }
            break;
        case CONNECTED:
        case READY:    
            if(tchGetVUSB() == FALSE) {  // unplugged
                usbMode = DISCONNECTED;
                USBButton.inactive = TRUE;
                USBButton.redraw = TRUE;
            }
            break;
        case ATTACHED:
            if(tchGetVUSB() == FALSE) {  // unplugged
                usbMode = DISCONNECTED;
                USBDeviceDetach();
                doMount();
                USBButton.icon = iconUSBon;
                USBButton.inactive = TRUE;
                USBButton.redraw = TRUE;
                mode = IDLE;
            }
            break; 
    }
}
void doUSBAttach()
{

    if(usbMode == CONNECTED) {          // successful trial attachment
        usbMode = READY;
        USBDeviceDetach();
        USBButton.inactive = FALSE;     // enable button
    }
    else if(usbMode == READY){          // button pressed
        USBMSDInit();
        usbMode = ATTACHED;
        
    }
    USBButton.redraw = TRUE;
}
void doUSBButton(UINT16 code)
{
    if(code == 1) {             //USB on

        mode = USB;
        USBDeviceInit();
        USBDeviceAttach();
        
        dirUpButton.inactive = TRUE;
        dirUpButton.redraw = TRUE;
        dirDnButton.inactive = TRUE;
        dirDnButton.redraw = TRUE;
        readyScreen.clicker[1]->inactive = TRUE;
        readyScreen.clicker[1]->redraw = TRUE;
        readyScreen.button[9]->icon = iconUSBoff;
        readyScreen.button[9]->redraw = TRUE;
        readyScreen.button[12]->inactive = TRUE;
        readyScreen.button[12]->redraw = TRUE;
        readyScreen.button[13]->inactive = TRUE;
        readyScreen.button[13]->redraw = TRUE;
        readyScreen.button[14]->inactive = TRUE;
        readyScreen.button[14]->redraw = TRUE;
        readyScreen.titleText = "USB MODE";
        readyScreen.redraw |= REDRAW_TITLE;
        
    }
    else if(code == 0) {        //USB off
        usbMode = READY;
        mode = IDLE;
        USBDeviceDetach();
        doMount();
        readyScreen.button[9]->icon = iconUSBon;
        readyScreen.button[9]->redraw = TRUE;
    }
}
void sleepWake(void)
{
    /* shut down all peripherals */
    INTDisableInterrupts();
    DmaEnable(FALSE);
    lcdShutdown();
    sdShutdown();
    clkShutdown();
    OSCConfig(OSC_FRC_DIV, 0, 0, OSC_FRC_POST_8);   // 1Mhz
    //swap to local clock
    cdcShutdown();
    cdcShutdown(); // to be sure, to be sure.
        // I/O Ports

    INTEnableInterrupts();
    watchdogEnable(FALSE);

    /* go to sleep */
    tchSleepWake();
    INTDisableInterrupts();
    /* wake up all peripherals */
    cdcWakeup();
    OSCConfig(OSC_POSC_PLL, OSC_PLL_MULT_20, OSC_PLL_POST_1, OSC_FRC_POST_8);
    clcWakeup();
    sdWakeup();
    lcdWakeup(backlight);
    currentScreen->redraw |= REDRAW_ALL;
    DmaEnable(TRUE);
    INTEnableInterrupts();
    watchdogEnable(TRUE);
}
void doSetTime(UINT16 code)
{
    currentScreen = &timeScreen;
    currentScreen->redraw |= REDRAW_ALL;
}

void doSetDate(UINT16 code)
{
    currentScreen = &dateScreen;
    currentScreen->redraw |= REDRAW_ALL;
}

void doIncHours(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetTime();
    number = bcd2dec(data.byte[3]);
    number = number < 23 ? number + 1 : 0;
    data.byte[3] = dec2bcd(number);
    RtccSetTime(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetTimeString(time);

}
void doDecHours(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetTime();
    number = bcd2dec(data.byte[3]);
    number = number > 0 ? number - 1 : 23;
    data.byte[3] = dec2bcd(number);
    RtccSetTime(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetTimeString(time);
}
void doIncMins(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetTime();
    number = bcd2dec(data.byte[2]);
    number = number < 59 ? number + 1 : 0;
    data.byte[2] = dec2bcd(number);
    RtccSetTime(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetTimeString(time);
}
void doDecMins(UINT16 code)
{
     union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetTime();
    number = bcd2dec(data.byte[2]);
    number = number > 0 ? number - 1 : 59;
    data.byte[2] = dec2bcd(number);
    RtccSetTime(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetTimeString(time);
}
void doIncSecs(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetTime();
    number = bcd2dec(data.byte[1]);
    number = number < 59 ? number + 1: 0;
    data.byte[1] = dec2bcd(number);
    RtccSetTime(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetTimeString(time);
}
void doDecSecs(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetTime();
    number = bcd2dec(data.byte[1]);
    number = number > 0 ? number - 1 : 59;
    data.byte[1] = dec2bcd(number);
    RtccSetTime(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetTimeString(time);
}
void doIncYears(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetDate();
    number = bcd2dec(data.byte[3]);
    number = number < 99 ? number + 1 : 0;
    data.byte[3] = dec2bcd(number);
    RtccSetDate(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetDateString(date);
}
void doDecYears(UINT16 code)
{
     union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetDate();
    number = bcd2dec(data.byte[3]);
    number = number > 0 ? number - 1 : 99;
    data.byte[3] = dec2bcd(number);
    RtccSetDate(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetDateString(date);

}
void doIncMonths(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetDate();
    number = bcd2dec(data.byte[2]);
    number = number < 12 ? number + 1 : 1;
    data.byte[2] = dec2bcd(number);
    RtccSetDate(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetDateString(date);

}
void doDecMonths(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetDate();
    number = bcd2dec(data.byte[2]);
    number = number > 1 ? number - 1 : 12;
    data.byte[2] = dec2bcd(number);
    RtccSetDate(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetDateString(date);

}
void doIncDays(UINT16 code)
{
     union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetDate();
    number = bcd2dec(data.byte[1]);
    number = number < 31 ? number + 1 : 1;
    data.byte[1] = dec2bcd(number);
    RtccSetDate(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetDateString(date);

}
void doDecDays(UINT16 code)
{
    union{
        UINT32 dword;
        UINT8 byte[4];
    }data;
    BYTE number;

    data.dword = RtccGetDate();
    number = bcd2dec(data.byte[1]);
    number = number > 1 ? number - 1 : 31;
    data.byte[1] = dec2bcd(number);
    RtccSetDate(data.dword);

    currentScreen->redraw |= REDRAW_TIME;
    clkGetDateString(date);
}
UINT8 bcd2dec(UINT8 bcd)
{
    return ((bcd & 0x0f) + ((bcd >> 4) * 10));
}
UINT8 dec2bcd(UINT8 dec)
{
    return (((dec / 10) << 4) | (dec % 10));
}

void doAbout(UINT16 code)
{
    currentScreen = &aboutScreen;
    currentScreen->redraw |= REDRAW_ALL;
}
void doUpdate(UINT16 code)
{
    FIL f;
    UINT32 fres;
    fres = f_open(&f, "/PIC32TSR.HEX", FA_OPEN_EXISTING | FA_READ);
    if( fres == FR_OK ) {
        SYSKEY = 0xAA996655;  // Write Key1 to SYSKEY
        SYSKEY = 0x556699AA;  // Write Key2 to SYSKEY
        RSWRSTbits.SWRST = 1;
    } else {
        throwError(NO_HEX_FILE, 0);
    }
}

void doPowerFail(void)
{
    switch(mode){
        case RECORD:
            doStopRec(0);
            mode = IDLE;
            break;
        case PLAY:
            doStopPlay();
            mode = IDLE;
            break;
        case USB:
            doUSBButton(1);
            break;
        case IDLE:
        default:
            break;
    }
    sleepWake();
}
/* Error Handling *************************************************************/
void errorHandler(UINT32 errcode)
{   
    INT8 i;

    if( mode == RECORD )
        doStopRec(errcode);
    else if( mode == PLAY )
        auStopPlay(FALSE);

    switch(errcode){
        case FR_DISK_ERR01:
        case FR_DISK_ERR02:
        case FR_DISK_ERR03:
        case FR_DISK_ERR04:
        case FR_DISK_ERR05:
        case FR_DISK_ERR06:
        case FR_DISK_ERR07:
        case FR_DISK_ERR08:
        case FR_DISK_ERR09:
        case FR_DISK_ERR10:
        case FR_DISK_ERR11:
        case FR_DISK_ERR12:
        case FR_DISK_ERR13:
        case FR_DISK_ERR14:
        case FR_DISK_ERR15:
        case FR_DISK_ERR16:
        case FR_DISK_ERR17:
        case FR_DISK_ERR18:
        case FR_DISK_ERR19:
        case FR_DISK_ERR20:
        case FR_DISK_ERR21:
        case FR_DISK_ERR22:
        case FR_DISK_ERR23:
        case FR_DISK_ERR24:
        case FR_DISK_ERR25:
        case FR_DISK_ERR26:
        case FR_DISK_ERR27:
        case FR_DISK_ERR28:
        case FR_DISK_ERR29:
        case FR_DISK_ERR30:
        case FR_DISK_ERR31:
        case FR_DISK_ERR32:
        case FR_DISK_ERR33:
        case FR_DISK_ERR34:
        case FR_DISK_ERR35:
        case FR_DISK_ERR36:
        case FR_DISK_ERR37:
        case FR_DISK_ERR38:
        case FR_DISK_ERR39:
        case FR_DISK_ERR40:
        case FR_DISK_ERR41:
        case FR_DISK_ERR42:
        case FR_DISK_ERR43:
        case FR_DISK_ERR44:
        case FR_DISK_ERR45:
        case FR_DISK_ERR46:
        case FR_DISK_ERR47:
        case FR_DISK_ERR48:
        case FR_DISK_ERR49:
        case FR_DISK_ERR50:
        case FR_DISK_ERR51:
        sprintf(readyScreen.line1Text, "I/O error [%02d]", errcode);
//        sdShutdown();
        break;
    case FR_INT_ERR:
        readyScreen.line1Text =  "File system error";
//        sdShutdown();
        break;
    case FR_NOT_READY:
        readyScreen.line1Text =  "SD card not working";
//        noSDCard();
//        sdShutdown();
        break;
    case FR_NO_FILE:
        readyScreen.line1Text =  "File not found";
        break;
    case FR_NO_PATH:
        readyScreen.line1Text =  "Path not found";
        break;
    case FR_INVALID_NAME:
        readyScreen.line1Text =  "Path not valid";
        break;
    case FR_DENIED:
        readyScreen.line1Text =  "Access denied / full";
        break;
    case FR_EXIST:
        readyScreen.line1Text =  "File already exists";
        break;
    case FR_INVALID_OBJECT:
        readyScreen.line1Text =  "Invalid file or dir";
        break;
    case FR_WRITE_PROTECTED:
        readyScreen.line1Text =  "Card write protected";
        break;
    case FR_INVALID_DRIVE:
        readyScreen.line1Text =  "Invalid drive";
        break;
    case FR_NOT_ENABLED:
        readyScreen.line1Text =  "Vol has no work area";
        break;
    case FR_NO_FILESYSTEM:
        readyScreen.line1Text =  "No valid FAT volume";
        break;
    case FR_MKFS_ABORTED:
        readyScreen.line1Text =  "Can't make file sys";
        break;
    case FR_TIMEOUT:
        readyScreen.line1Text =  "File system time out";
        break;
    case FR_LOCKED:
        readyScreen.line1Text =  "File locked";
        break;
    case FR_NOT_ENOUGH_CORE:
        readyScreen.line1Text =  "LFN buffer error";
        break;
    case FR_TOO_MANY_OPEN_FILES:
        readyScreen.line1Text =  "Too many open files";
        break;
    case FR_INVALID_PARAMETER:
        readyScreen.line1Text =  "Invalid parameter";
        break;
    case NO_SD:
        readyScreen.line1Text = "No SD Card";
        break;
    case MALLOC:
        readyScreen.line1Text = "Too many files";
        break;
    case NO_RIFF:
        readyScreen.line1Text = "Not a Wave File";
        break;
    case NO_WAVE:
        readyScreen.line1Text = "Not a Wave File";
        break;
    case NO_FMT:
        readyScreen.line1Text = "Not a Wave File";
        break;
    case NOT_PCM:
        readyScreen.line1Text = "Unsupported Coding";
        break;
    case NOT_2CH:
        readyScreen.line1Text = "Unsupported #Channels";
        break;
    case BAD_RATE:
        readyScreen.line1Text = "Unsupported Bit Rate";
        break;
    case BAD_DEPTH:
        readyScreen.line1Text = "Unsupported Bit Depth";
        break;
    case NO_DATA:
        readyScreen.line1Text = "No Sound Data";
        break;
     case DISK_FULL:
        readyScreen.line1Text = "Disk is full!";
        break;
     case PATH_LONG:
        readyScreen.line1Text = "Path Too Long!";
        break;
     case NO_HEX_FILE:
        readyScreen.line1Text = "PIC32TSR.HEX not found";
        break;
     default:
        readyScreen.line1Text =  "Undefined error";
        break;
    }

#ifdef _ERRLOC_
    
    if( errcode != NO_HEX_FILE ) {
        sprintf(errbuff, "%04i %s", errSource, readyScreen.line1Text);
        readyScreen.line1Text = errbuff;
    }
#endif

    readyScreen.error = TRUE;
    readyScreen.titleText = "ERROR!";
    currentScreen = &readyScreen;
    dirUpButton.inactive = TRUE;
    dirDnButton.inactive = TRUE;
    for(i = 9; i < 15; i++) {
        if(readyScreen.button[i]){
            readyScreen.button[i]->inactive = TRUE;
        }
    }
    for(i = 0; i < 3; i++){
       if(readyScreen.clicker[i]){
           readyScreen.clicker[i]->inactive = TRUE;
       }
    }
    readyScreen.button[1] = &errReturnButton;
    readyScreen.redraw = REDRAW_ALL;
    
}
void doErrReturn(UINT16 code)
{
    INT8 i;

      readyScreen.error = FALSE;
      readyScreen.titleText = "READY";
//    readyScreen.line1Text = "";
//    for(i = 9; i < 15; i++) {
//        if(readyScreen.button[i]){
//            readyScreen.button[i]->inactive = FALSE;
//        }
//    }
//     for(i = 0; i < 3; i++){
//        if(readyScreen.clicker[i]){
//             readyScreen.clicker[i]->inactive = FALSE;
//        }
//    }
    readyScreen.button[1] = 0;
    readyScreen.redraw = REDRAW_ALL;


    if(currentFile){
        getDfn();
        readyScreen.line2Text = dfn;
        readyScreen.line1Text = dpath;
    } else {
        readyScreen.line2Text = "Directory Empty";
        readyScreen.button[14]->inactive = TRUE;
    }

    readyScreen.titleText = "READY";
    if( doStopPlayAfterError ) {
        doStopPlay();
        doStopPlayAfterError = FALSE;
    }
    if( noSDCardAfterError ) {
        noSDCard();
        noSDCardAfterError = FALSE;
    } else if( doMountAfterError ) {
        doMount();
        doMountAfterError = FALSE;
    }

    muteButton.inactive = FALSE;
    volClicker.inactive = FALSE;
    configButton.inactive = FALSE;
    readyScreen.clicker[1]->inactive = FALSE;
    readyScreen.clicker[1]->redraw = TRUE;

    readyScreen.button[12]->inactive = FALSE;
    readyScreen.button[12]->redraw = TRUE;
    readyScreen.button[13]->inactive = TRUE;
    readyScreen.button[13]->redraw = TRUE;
    readyScreen.button[14]->redraw = TRUE;
    readyScreen.redraw = REDRAW_ALL;

    mode = IDLE;
}

