/*
Diplomova prace : Palubni pocitac

storage - api pro praci s SD kartou

Atmega128A 10 MHz XTAL

Autor: Ondrej Lutera

2011/2012

*/
#include "storage.h"

char storagePowered = 0;
char cardOpened = 0;

static struct partition_struct * partition;
static struct fat_fs_struct * fs;

#ifdef CAR_SHELL
static uint8_t read_line(char* buffer, uint8_t buffer_length);
static uint32_t strtolong(const char* str);
static uint8_t print_disk_info(const struct fat_fs_struct* fs);
#endif

static uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry);
static struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name);

unsigned char syncData(void)
{

    if(!sd_raw_sync())
    {
#ifdef CAR_DEBUG
        uart_puts_p(PSTR("error syncing disk\n"));
#endif
        return 0;

    }

    return 1;

}

unsigned char openCard(void)
{

    partition = partition_open(sd_raw_read,
                               sd_raw_read_interval,
#if SD_RAW_WRITE_SUPPORT
                               sd_raw_write,
                               sd_raw_write_interval,
#else
                               0,
                               0,
#endif
                               0
                              );
    if(!(partition))
    {
        /* test otevreni partisny typu floppy         */
        partition = partition_open(sd_raw_read,
                                   sd_raw_read_interval,
#ifdef SD_RAW_WRITE_SUPPORT
                                   sd_raw_write,
                                   sd_raw_write_interval,
#else
                                   0,
                                   0,
#endif
                                   -1
                                  );
        if(!(partition))
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("opening partition failed\n"));
#endif
            return 0;
        }
    }

    /* otevreni filesystemu */
    fs = fat_open(partition);
    if(!(fs))
    {
#ifdef CAR_DEBUG
        uart_puts_p(PSTR("opening filesystem failed\n"));
#endif

        partition_close(partition);
        return 0;
    }

    cardOpened=1;

    return 1;
}

void closeCard(void)
{


    fat_close(fs);


    partition_close(partition);

    cardOpened=0;

}

uint8_t delFile(char *location, char *file)
{

    struct fat_dir_entry_struct directory;
    fat_get_dir_entry_of_path(fs, location, &directory);
    struct fat_dir_struct* dd = fat_open_dir(fs, &directory);

    if(find_file_in_dir(fs, dd, file, &directory))
    {
        if(fat_delete_file(fs, &directory))
        {
            fat_close_dir(dd);
            return 1;
        }
    }
#ifdef CAR_DEBUG
    uart_puts_p(PSTR("error deleting file: "));
    uart_puts(file);
    uart_putc('\n');
#endif
    fat_close_dir(dd);
    return 0;
}


uint8_t scanDir(char * location, uint8_t attrib,uint16_t max,char * buff)
{

    uint8_t fsItems = 0;
    uint8_t offset=0;

    if(location!=NULL && cardOpened)
    {

        /* ootevreni adresare */
        struct fat_dir_entry_struct directory;
        fat_get_dir_entry_of_path(fs, location, &directory);

        struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
        if(!dd)
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("opening directory for scanning failed\n"));
#endif

            fat_close_dir(dd);
            return 0;
        }

        uint16_t Cnt=0;
        uint16_t ix = 0;
        struct fat_dir_entry_struct dir_entry;
        /* najdeme pocet adresaru a souboru ve slozce */
        while(fat_read_dir(dd, &dir_entry))
        {

            if(dir_entry.long_name[0]!='.')
            {
                if(attrib == FIND_DIRS && (dir_entry.attributes & FAT_ATTRIB_DIR))Cnt++;
                else if(attrib==FIND_FILES && !(dir_entry.attributes & FAT_ATTRIB_DIR))Cnt++;
            }

        }
        fat_reset_dir(dd); // vyresetujeme pozici handle na pocatek tj jedme od zacatku

        if(Cnt>max) Cnt = Cnt-max;
        else Cnt = 0;
        /* nacteme si obsah adresare */
        while(fat_read_dir(dd, &dir_entry))
        {

            if(dir_entry.long_name[0]!='.' &&(
                        (attrib == FIND_DIRS && (dir_entry.attributes & FAT_ATTRIB_DIR))||
                        (attrib==FIND_FILES && !(dir_entry.attributes & FAT_ATTRIB_DIR))
                    )
              )
            {

                ix++;

                if(ix>Cnt)
                {
                    fsItems++;
#ifdef CAR_DEBUG
                    uart_puts_P("Item: ");
                    uart_put_int(fsItems);
                    uart_puts_P(" ");
                    uart_puts(dir_entry.long_name);
                    uart_puts_P("\n");
#endif

                    strncpy(buff+offset,dir_entry.long_name,7);
                    offset+=strlen(dir_entry.long_name)+1;
                }
            }


        }


        fat_close_dir(dd);
        return fsItems;
    }



    return 0;
}

unsigned char getTrackMeas(char *location,char * file,data_t * data,uint8_t trackIndex,uint8_t end)
{

    if(location && file && data)
    {

        struct fat_file_struct * fd;
        uint8_t state;
        fd = openFile(location,file,&state);
        int32_t index=0;
        uint8_t endOfFile=0;

        if(state==OLD_FILE)
        {

            do
            {

                if(!readMeas(data,index,FAT_SEEK_SET,fd))
                {

                    endOfFile=1;
                    break;
                }


            }
            while(data->index!=trackIndex+end && ++index>0);

            /* ted zkontrolujeme zda opravdu nasel konec a pocatek nove trasy*/

            if(end && index>0 && !endOfFile)index-=1;


            if(!readMeas(data,index,FAT_SEEK_SET,fd))
            {
                fat_close_file(fd);
                return 0;

            }
            else if(data->index!=trackIndex)
            {
                fat_close_file(fd);
                return 0;

            }


            fat_close_file(fd);
            return 1;


        }



        fat_close_file(fd);

    }

    return 0;
}

unsigned char findMeas(char * location,data_t *data,uint8_t lastMeas)
{

    char path[50];
    char name[10];
    uint8_t found=1;

    if(location!=NULL && cardOpened)
    {
        strncpy(path,location,50);
        struct fat_dir_entry_struct directory;
        struct fat_dir_struct* dd=NULL;
        struct fat_dir_entry_struct dir_entry;
        struct fat_file_struct * fd=NULL;
        uint8_t maxRep = 20;

        while(found)
        {
            /* otevreni adresare */

            if(dd)fat_close_dir(dd);

            fat_get_dir_entry_of_path(fs, path, &directory);

            dd = fat_open_dir(fs, &directory);

            if(!dd)
            {
#ifdef CAR_DEBUG
                uart_puts_p(PSTR("opening directory for scanning failed\n"));
#endif

                fat_close_dir(dd);
                return 0;
            }


            /* najdeme prvni nejstarsi adresar ve slozce*/
            found = 0;
            if(lastMeas)maxRep = 100;
            else maxRep =20;

            while(maxRep && fat_read_dir(dd, &dir_entry))
            {
                if(dir_entry.long_name[0]!='.')
                {
                    if(dir_entry.attributes & FAT_ATTRIB_DIR)
                    {
                        if(!lastMeas)
                        {
                            /* nasel prvni to staci a konec */
                            found = 1;
                            break;

                        }
                        else
                        {

                            /* ma hledat posledni tak pokracuje */
                            strncpy(name,dir_entry.long_name,10);
                            found = 1;
                        }

                    }

                }
                maxRep--;

            }

            if(found)
            {
                /* zavreme a otevreme novy nalezly */

                strncat_P(path,PSTR("/"),50);

                if(lastMeas) strncat(path,name,50);
                else strncat(path,dir_entry.long_name,50);

            }

            /* jdeme na dalsi cyklus hledani ve slozce */


        }

        /* nyni mame v promenne path cestu do nejhlubsiho adresare
           najdeme soubor
        */


        if(dd)fat_close_dir(dd);

        fat_get_dir_entry_of_path(fs, path, &directory);

        dd = fat_open_dir(fs, &directory);

        found = 0;
        maxRep = 40;    /* max 31 souboru + neco navic v pripade adresaru*/

        while(maxRep && fat_read_dir(dd, &dir_entry))
        {

            if(dir_entry.long_name[0]!='.')
            {
                /* testujeme soubor ktery nezacina na tecku tj ukazatel na predchozi adresar a zaroven je soubor a ma nazev xx.dat*/
                if(!(dir_entry.attributes & FAT_ATTRIB_DIR) && dir_entry.long_name[2]=='.' && dir_entry.long_name[3]=='d')
                {
                    if(!lastMeas)
                    {
                        /* nasel prvni validni soubor */
                        found = 1;
                        break;
                    }
                    else
                    {
                        /* uloz nazev a hledej dalsi validni */
                        strncpy(name,dir_entry.long_name,10);
                        found = 1;
                    }
                }

            }
            maxRep--;
        }

        /* nyni mame jmeno souboru ktery chceme cist */

        if(found)
        {

            unsigned char state;

            /* otevreme soubor a naplnime data predana ukazatelem */

            if(lastMeas) fd = openFile(path,name,&state);
            else fd = openFile(path,dir_entry.long_name,&state);

            found = 0;

            if(state == OLD_FILE)
            {

                if(lastMeas)
                {
                    if(readMeas(data,-1,FAT_SEEK_END,fd)) found = 1;
                }
                else
                {
                    if(readMeas(data,0,FAT_SEEK_SET,fd)) found = 1;
                }
            }

        }

        if(dd)fat_close_dir(dd);
        if(fd)fat_close_file(fd);

        return found;
    }

    return 0;

}

unsigned char scanMeasTimes(char * location,char * file,char * buff,uint16_t buffSize,uint8_t *firstIndex)
{

    data_t data;
    int32_t index=0;

    uint32_t fnd = 0;
    char * pBuff=buff;
    struct fat_file_struct *fd;
    char format[10];
    uint8_t state;
    uint8_t lastTripIndex=0;
    uint16_t size = buffSize/6;
    uint8_t seek;
    uint8_t lastIndex=0;



    if(location!=NULL && buff!=NULL && buffSize>6 && cardOpened)
    {

        strncpy_P(format,PSTR("%02u:%02u"),10);

        fd = openFile(location,file,&state);

        if(state==OLD_FILE)
        {

            lastTripIndex = getLastIndex(fd);

            if(lastTripIndex>=size)seek=SEEK_END;
            else seek=SEEK_SET;

            if(seek==SEEK_END)
            {

                index = -1;
                lastIndex = 0;

                while(readMeas(&data,index,seek,fd))
                {

                    if(data.index==(lastTripIndex-size))
                    {

                        index++; /* skoc na dalsi za */
                        break;

                    }

                    index--;

                }

            }
            else
            {

                index = 0;
                lastIndex=255;

            }

            fnd = 0;



            while(readMeas(&data,index,seek,fd))
            {

                if(lastIndex!=data.index)
                {
                    lastIndex = data.index;
                    if(fnd==0)*firstIndex=lastIndex;

                    if(pBuff <= &buff[buffSize-6])
                    {

                        snprintf(pBuff,6,format,data.storeTime.hour,data.storeTime.min);
                        pBuff+=6;
                        fnd++;

                        if(pBuff > &buff[buffSize-6])break;


                    }
                    else break;
                }

                index++;

            }



        }
        fat_close_file(fd);


        return fnd;

    }

    return 0;

}

struct fat_file_struct * openFile(char * location, char * file, unsigned char * state)
{

    *state = ERR;

    if(location!=NULL && cardOpened)
    {

        *state=OLD_FILE;
        /* ootevreni adresare */
        struct fat_dir_entry_struct directory;
        fat_get_dir_entry_of_path(fs, location, &directory);

        struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
        if(!dd)
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("openFile: opening directory failed\n"));
#endif
            *state=NO_DIR;

            fat_close_dir(dd);
            return NULL;
        }


        if(!find_file_in_dir(fs,dd,file,&directory))
        {

#ifdef CAR_DEBUG
            uart_puts_p(PSTR("openFile: error no file "));
            uart_puts(file);
            uart_puts_P("\n");
#endif

            struct fat_dir_entry_struct file_entry;
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("openFile:creating file\n"));
#endif
            *state = NEW_FILE;

            if(!fat_create_file(dd, file, &file_entry))
            {
#ifdef CAR_DEBUG
                uart_puts_p(PSTR("openFile:error creating file: "));
                uart_puts(file);
                uart_putc('\n');

#endif
                *state=ERR_CREATE;

                fat_close_dir(dd);
                return NULL;
            }

        }

        struct fat_file_struct* fd = open_file_in_dir(fs,dd,file);


        if(!fd)
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("openFile: error opening "));
            uart_puts(file);
            uart_putc('\n');
#endif
            *state = ERR_OPEN;

            fat_close_dir(dd);
            return NULL;
        }


        fat_close_dir(dd);
        return fd;

    }

    return NULL;
}


unsigned char readData(uint8_t * data,  uint16_t data_size,int32_t index,unsigned char seek,struct fat_file_struct* fd)
{

    if(data!=NULL && cardOpened && fd)
    {


        int32_t offset = data_size;

        offset*=index;

        if(!fat_seek_file(fd, &offset, seek))
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("readData: error seeking on\n"));

#endif


            return 0;

        }


        if(data_size!=fat_read_file(fd,data,data_size))
        {

#ifdef CAR_DEBUG
            uart_puts_p(PSTR("readData: error reading file, END?\n"));

#endif

            return 0;
        }


        return 1;


    }

    return 0;

}


unsigned char storeData(const uint8_t * data,uint16_t data_size,struct fat_file_struct* fd)
{

    if(fd && data!=NULL && cardOpened)
    {


        if(!fd)
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("storeData:error opening\n"));


#endif

            return 0;
        }


        int32_t offset = 0;

        if(!fat_seek_file(fd, &offset, FAT_SEEK_END))
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("storeData:error seeking on\n"));

#endif


            return 0;

        }


        if(fat_write_file(fd,data,data_size)!=data_size)
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("error writing to file\n"));

#endif

            return 0;
        }



        return 1;

    }

    return 0;


};

unsigned char readMeas(data_t *data,int32_t index,unsigned char seek,struct fat_file_struct *fd)
{

    if(data!=NULL)
    {
        if(readData((uint8_t *)data, sizeof(data_t),index,seek,fd))
        {

            uint8_t crc = crc8( (uint8_t *)data, sizeof(data_t)-sizeof(uint8_t));

            if(crc == data->crc)
            {


                return 1;

            }
#ifdef CAR_DEBUG
            else
            {

                uart_puts_p(PSTR("readMeas: Crc doesn't equal!\n"));

            }
#endif

        }


    }

    return 0;


}

uint8_t getLastIndex(struct fat_file_struct *fd)
{

    int32_t index = -1;     /* offset posunout od konce na posledni data */

    data_t data;


    readMeas(&data,index,FAT_SEEK_END,fd);

    return data.index;

}

unsigned char findDir(char * location)
{


    if(!cardOpened)return 0;

    /* otevreni adresare */
    struct fat_dir_entry_struct directory;
    struct fat_dir_struct* dd ;

    fat_get_dir_entry_of_path(fs,location, &directory);

    dd = fat_open_dir(fs, &directory);
    if(!dd)
    {
#ifdef CAR_DEBUG
        uart_puts(location);
        uart_puts_p(PSTR(" not found\n"));
#endif

        fat_close_dir(dd);
        return 0;
    }



    fat_close_dir(dd);
    return 1;

}

unsigned char createDir(char *location,char * dir_name)
{

    uint8_t created = 0;

    if(!cardOpened)return 0;

    /* otevreni adresare */
    struct fat_dir_entry_struct directory;
    struct fat_dir_struct* dd ;

    fat_get_dir_entry_of_path(fs,location, &directory);

    dd = fat_open_dir(fs, &directory);
    if(!dd)
    {
#ifdef CAR_DEBUG
        uart_puts_p(PSTR("CreateDir: opening directory failed\n"));
#endif

        fat_close_dir(dd);
        return 0;
    }

    struct fat_dir_entry_struct dir_entry;

#ifdef CAR_WDT_EN
    wdt_disable();
#endif

    if(!fat_create_dir(dd, dir_name, &dir_entry))
    {
#ifdef CAR_DEBUG
        uart_puts_p(PSTR("CreateDir:error creating directory: "));
#endif
        created = 0;
    }
    else
    {
#ifdef CAR_DEBUG
        uart_puts_p(PSTR("CreateDir:directory created: "));
#endif
        created = 1;
    }
#ifdef CAR_DEBUG
    uart_puts(location);
    uart_putc('\n');
#endif
#ifdef CAR_WDT_EN
    wdt_enable(CAR_WDT_TIME);
    wdt_reset();
#endif

    fat_close_dir(dd);


    return created;
}

unsigned char storeMeas(data_t *data,struct fat_file_struct * fd)
{

    if(data!=NULL)
    {

        getTimePCF(&(data->storeTime));

        data->crc = crc8( (uint8_t *)data, sizeof(data_t)-sizeof(uint8_t));     // crc je na konci

        if(!storeData((const uint8_t*)data,sizeof(data_t),fd))
        {
#ifdef CAR_DEBUG
            uart_puts_p(PSTR("storeMeas: error store data!\n"));
#endif
            return 0;
        }

        return 1;        /* pokud je vse ok tak vrat crc dat */

    }

    return 0;

}

void storagePwr(char enable)
{

    configure_power_card();

    if(enable)
    {

        power_card();
        _delay_ms(50);
    }
    else shut_card();

}

unsigned char storageInit(char *dir)
{

    if(!storagePowered)storagePwr(storagePowered=1);

    /* raw init */
    if(!sd_raw_init())
    {
#ifdef CAR_DEBUG
        uart_puts_p(PSTR("MMC/SD initialization failed\n"));
#endif
        return 0;
    }

    /* otevreni partisny */
    openCard();

    if(!cardOpened)return 0;
#ifdef CAR_WDT_EN
    wdt_reset();
#endif
    /* otevreni adresare */
    struct fat_dir_entry_struct directory;
    struct fat_dir_struct* dd ;

    fat_get_dir_entry_of_path(fs,dir, &directory);

    dd = fat_open_dir(fs, &directory);

    if(!dd)
    {
#ifdef CAR_WDT_EN
        wdt_reset();
#endif
        if(dir[0]=='/')dir++;

        fat_close_dir(dd);

        createDir("/",dir);

        return 1;

    }

#ifdef CAR_WDT_EN
    wdt_enable(CAR_WDT_TIME);
    wdt_reset();
#endif

    fat_close_dir(dd);



    return 0;


}

/* ---------------------funkce shellu prevzate z puvodni knihovny --------------------------------------*/

#ifdef CAR_SHELL

uint8_t read_line(char* buffer, uint8_t buffer_length)
{
    memset(buffer, 0, buffer_length);

    uint8_t read_length = 0;
    while(read_length < buffer_length - 1)
    {
        uint16_t c;
        do
        {
            c= uart_getc();
#ifdef CAR_WDT_EN
            wdt_reset();
#endif

        }
        while(c&UART_NO_DATA);

        c = c&0x00ff;

        if(c == 0x08 || c == 0x7f)
        {
            if(read_length < 1)
                continue;

            --read_length;
            buffer[read_length] = '\0';

            uart_putc(0x08);
            uart_putc(' ');
            uart_putc(0x08);

            continue;
        }

        uart_putc(c);

        if(c == '\n')
        {
            buffer[read_length] = '\0';
            break;
        }
        else
        {
            buffer[read_length] = c;
            ++read_length;
        }
    }

    return read_length;
}

uint32_t strtolong(const char* str)
{
    uint32_t l = 0;
    while(*str >= '0' && *str <= '9')
        l = l * 10 + (*str++ - '0');

    return l;
}
#endif

uint8_t find_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name, struct fat_dir_entry_struct* dir_entry)
{
    while(fat_read_dir(dd, dir_entry))
    {
        if(strcmp(dir_entry->long_name, name) == 0)
        {
            fat_reset_dir(dd);
            return 1;
        }
    }

    return 0;
}

struct fat_file_struct* open_file_in_dir(struct fat_fs_struct* fs, struct fat_dir_struct* dd, const char* name)
{
    struct fat_dir_entry_struct file_entry;
    if(!find_file_in_dir(fs, dd, name, &file_entry))
        return 0;

    return fat_open_file(fs, &file_entry);
}
#ifdef CAR_SHELL
uint8_t print_disk_info(const struct fat_fs_struct* fs)
{
    if(!fs)
        return 0;

    struct sd_raw_info disk_info;
    if(!sd_raw_get_info(&disk_info))
        return 0;

    uart_puts_p(PSTR("manuf:  0x"));
    uart_puthex_byte(disk_info.manufacturer);
    uart_putc('\n');
    uart_puts_p(PSTR("oem:    "));
    uart_puts((char*) disk_info.oem);
    uart_putc('\n');
    uart_puts_p(PSTR("prod:   "));
    uart_puts((char*) disk_info.product);
    uart_putc('\n');
    uart_puts_p(PSTR("rev:    "));
    uart_puthex_byte(disk_info.revision);
    uart_putc('\n');
    uart_puts_p(PSTR("serial: 0x"));
    uart_puthex_long(disk_info.serial);
    uart_putc('\n');
    uart_puts_p(PSTR("date:   "));
    uart_put_int(disk_info.manufacturing_month);
    uart_putc('/');
    uart_put_int(disk_info.manufacturing_year);
    uart_putc('\n');
    uart_puts_p(PSTR("size:   "));
    uart_put_int(disk_info.capacity / 1024 / 1024);
    uart_puts_p(PSTR("MB\n"));
    uart_puts_p(PSTR("copy:   "));
    uart_put_int(disk_info.flag_copy);
    uart_putc('\n');
    uart_puts_p(PSTR("wr.pr.: "));
    uart_put_int(disk_info.flag_write_protect_temp);
    uart_putc('/');
    uart_put_int(disk_info.flag_write_protect);
    uart_putc('\n');
    uart_puts_p(PSTR("format: "));
    uart_put_int(disk_info.format);
    uart_putc('\n');
    uart_puts_p(PSTR("free:   "));
    uart_put_longint(fat_get_fs_free(fs));
    uart_putc('/');
    uart_put_longint(fat_get_fs_size(fs));
    uart_putc('\n');

    return 1;
}
#endif
#ifdef FAT_DATETIME_SUPPORT
void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec)
{
    *year = 2007;
    *month = 1;
    *day = 1;
    *hour = 0;
    *min = 0;
    *sec = 0;
}
#endif

#ifdef CAR_SHELL

void simpleShell(void)
{

    /* open directory */
    struct fat_dir_entry_struct directory;
    fat_get_dir_entry_of_path(fs, "/", &directory);

    struct fat_dir_struct* dd = fat_open_dir(fs, &directory);
    if(!dd)
    {
#ifdef CAR_DEBUG
        uart_puts_p(PSTR("opening directory failed\n"));
#endif
        return ;
    }

    char buffer[24];
    while(1)
    {
#ifdef CAR_WDT_EN
        wdt_reset();
#endif
        /* print prompt */
        uart_putc('>');
        uart_putc(' ');

        /* read command */
        char* command = buffer;
        if(read_line(command, sizeof(buffer)) < 1)
            continue;

        /* execute command */
        if(strcmp_P(command, PSTR("init")) == 0)
        {
            break;
        }
        else if(strncmp_P(command, PSTR("cd "), 3) == 0)
        {
            command += 3;
            if(command[0] == '\0')
                continue;

            /* change directory */
            struct fat_dir_entry_struct subdir_entry;
            if(find_file_in_dir(fs, dd, command, &subdir_entry))
            {
                struct fat_dir_struct* dd_new = fat_open_dir(fs, &subdir_entry);
                if(dd_new)
                {
                    fat_close_dir(dd);
                    dd = dd_new;
                    continue;
                }
            }

            uart_puts_p(PSTR("directory not found: "));
            uart_puts(command);
            uart_putc('\n');
        }
        else if(strcmp_P(command, PSTR("ls")) == 0)
        {
            /* print directory listing */
            struct fat_dir_entry_struct dir_entry;
            while(fat_read_dir(dd, &dir_entry))
            {
                uint8_t spaces = sizeof(dir_entry.long_name) - strlen(dir_entry.long_name) + 4;

                uart_puts(dir_entry.long_name);
                uart_putc(dir_entry.attributes & FAT_ATTRIB_DIR ? '/' : ' ');
                while(spaces--)
                    uart_putc(' ');
                uart_put_longint(dir_entry.file_size);
                uart_putc('\n');
            }
        }
        else if(strncmp_P(command, PSTR("cat "), 4) == 0)
        {
            command += 4;
            if(command[0] == '\0')
                continue;

            /* search file in current directory and open it */
            struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
            if(!fd)
            {
                uart_puts_p(PSTR("error opening "));
                uart_puts(command);
                uart_putc('\n');
                continue;
            }

            /* print file contents */
            uint8_t buffer[8];
            uint32_t offset = 0;
            intptr_t count;
            while((count = fat_read_file(fd, buffer, sizeof(buffer))) > 0)
            {
                uart_put_longint(offset);
                uart_putc(':');
                for(intptr_t i = 0; i < count; ++i)
                {
                    uart_putc(' ');
                    uart_puthex_byte(buffer[i]);
                }
                uart_putc('\n');
                offset += 8;
            }

            fat_close_file(fd);
        }
        else if(strcmp_P(command, PSTR("disk")) == 0)
        {
            if(!print_disk_info(fs))
                uart_puts_p(PSTR("error reading disk info\n"));
        }
#if FAT_WRITE_SUPPORT
        else if(strncmp_P(command, PSTR("rm "), 3) == 0)
        {
            command += 3;
            if(command[0] == '\0')
                continue;

            struct fat_dir_entry_struct file_entry;
            if(find_file_in_dir(fs, dd, command, &file_entry))
            {
                if(fat_delete_file(fs, &file_entry))
                    continue;
            }

            uart_puts_p(PSTR("error deleting file: "));
            uart_puts(command);
            uart_putc('\n');
        }
        else if(strncmp_P(command, PSTR("touch "), 6) == 0)
        {
            command += 6;
            if(command[0] == '\0')
                continue;

            struct fat_dir_entry_struct file_entry;
            if(!fat_create_file(dd, command, &file_entry))
            {
                uart_puts_p(PSTR("error creating file: "));
                uart_puts(command);
                uart_putc('\n');
            }
        }
        else if(strncmp_P(command, PSTR("mv "), 3) == 0)
        {
            command += 3;
            if(command[0] == '\0')
                continue;

            char* target = command;
            while(*target != ' ' && *target != '\0')
                ++target;

            if(*target == ' ')
                *target++ = '\0';
            else
                continue;

            struct fat_dir_entry_struct file_entry;
            if(find_file_in_dir(fs, dd, command, &file_entry))
            {
                if(fat_move_file(fs, &file_entry, dd, target))
                    continue;
            }

            uart_puts_p(PSTR("error moving file: "));
            uart_puts(command);
            uart_putc('\n');
        }
        else if(strncmp_P(command, PSTR("write "), 6) == 0)
        {
            command += 6;
            if(command[0] == '\0')
                continue;

            char* offset_value = command;
            while(*offset_value != ' ' && *offset_value != '\0')
                ++offset_value;

            if(*offset_value == ' ')
                *offset_value++ = '\0';
            else
                continue;

            /* search file in current directory and open it */
            struct fat_file_struct* fd = open_file_in_dir(fs, dd, command);
            if(!fd)
            {
                uart_puts_p(PSTR("error opening "));
                uart_puts(command);
                uart_putc('\n');
                continue;
            }

            int32_t offset = strtolong(offset_value);
            if(!fat_seek_file(fd, &offset, FAT_SEEK_SET))
            {
                uart_puts_p(PSTR("error seeking on "));
                uart_puts(command);
                uart_putc('\n');

                fat_close_file(fd);
                continue;
            }

            /* read text from the shell and write it to the file */
            uint8_t data_len;
            while(1)
            {
                /* give a different prompt */
                uart_putc('<');
                uart_putc(' ');

                /* read one line of text */
                data_len = read_line(buffer, sizeof(buffer));
                if(!data_len)
                    break;

                /* write text to file */
                if(fat_write_file(fd, (uint8_t*) buffer, data_len) != data_len)
                {
                    uart_puts_p(PSTR("error writing to file\n"));
                    break;
                }
            }

            fat_close_file(fd);
        }
        else if(strncmp_P(command, PSTR("mkdir "), 6) == 0)
        {
            command += 6;
            if(command[0] == '\0')
                continue;

            struct fat_dir_entry_struct dir_entry;
#ifdef CAR_WDT_EN
            wdt_disable();
#endif
            if(!fat_create_dir(dd, command, &dir_entry))
            {
                uart_puts_p(PSTR("error creating directory: "));
                uart_puts(command);
                uart_putc('\n');
            }

#ifdef CAR_WDT_EN
            wdt_enable(CAR_WDT_TIME);
            wdt_reset();
#endif
        }
        else if(strcmp_P(command, PSTR("exit")) == 0)
        {

            uart_puts_p(PSTR("exiting\n"));

            break;


        }
#endif
#if SD_RAW_WRITE_BUFFERING
        else if(strcmp_P(command, PSTR("sync")) == 0)
        {
            if(!sd_raw_sync())
                uart_puts_p(PSTR("error syncing disk\n"));
        }
#endif
        else
        {
            uart_puts_p(PSTR("unknown command"));
            // uart_puts(command);
            uart_putc('\n');
        }
    }



    /* close directory */
    fat_close_dir(dd);

}
#endif
