/*
 * ID3 format tag decoder for MP3 files
 */
#include <t_services.h>
#include <string.h>
#include "ff.h"
#include "player.h"
#include "mempool.h"

#define	ID_NULL	0x0
#define	ID_MCDI	0x4d434449
#define	ID_TALB	0x54414C42
#define	ID_TCOM	0x54434F4D
#define	ID_TCON	0x54434F4E
#define	ID_TIT2	0x54495432
#define	ID_TLEN	0x544C454E
#define	ID_TPE1	0x54504531
#define	ID_TRCK	0x5452434B
#define	ID_TYER	0x54594552

#define	WBSIZE	1600

struct t_v2header {
  char magic[3];
  unsigned char version[2];
  unsigned char flag;
  unsigned char tagsize[4];
} __attribute__((packed));

struct t_frheader {
  char frame_id[4];
  unsigned char frsize[4];
  unsigned short flags;
} __attribute__((packed));

static int get_size(char *cp)
{
    int len = 0;
    int i;

    for (i = 0; i < 4; i++) {
        len <<= 7;
        len += *cp++ & 0x7f;
    }
    return len;
}

static int get_fsize(char *cp)
{
    int len = 0;
    int i;

    for (i = 0; i < 4; i++) {
        len <<= 8;
        len += *cp++ & 0xff;
    }
    return len;
}

static int typeval(char *cp)
{
    int val, i;

    val = 0;
    for (i = 0; i < 4; i++) {
        val <<= 8;
        val += *cp++;
    }
    return val;
}

FRESULT get_id3_info(Player_Status *pst)
{
    struct t_v2header v2header;
    struct t_frheader frheader;
    char *cp;
    int b_read, len, type, fs;
    int nb, left;
    FRESULT result;
    UB *work_buffer;
    FIL *file = &pst->music_file;

    work_buffer = jsp_malloc(WBSIZE);
    pst->title_length = pst->play_start = 0;
    strcpy(pst->song_name, "???");
    strcpy(pst->performer_name, "???");
    strcpy(pst->album_name, "???");

    result = f_read(file, &v2header, sizeof(v2header), &b_read);
    if ((result != FR_OK) || (b_read != sizeof(v2header))) {
        syslog(LOG_NOTICE, "failed to read header. (%d)", result);
        return result;
    }
    if (strncmp(v2header.magic, "ID3", 3)) {
        syslog(LOG_NOTICE, "BAD Magic.");
        return result;
    }

    len = get_size(v2header.tagsize);
#ifdef ID_DEBUG
syslog(LOG_NOTICE, "total len = %d", len);
#endif
    while (len > 0) {
        result = f_read(file, &frheader, sizeof(frheader), &b_read);
        if ((result != FR_OK) || (b_read != sizeof(frheader))) {
            syslog(LOG_NOTICE, "failed while reading frame header.");
            return result;
        }
        cp = frheader.frame_id;
        type = typeval(cp);
        fs = get_fsize(frheader.frsize);
#ifdef ID_DEBUG
syslog(LOG_NOTICE, "type: %x, fs: %d", type, fs);
#endif
        if (fs > len)
            return result;

        if (fs > WBSIZE)
            nb = WBSIZE;
        else
            nb = fs;
        left = fs - nb;

        switch (type) {
        case ID_TALB:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb] = 0;
            if (nb > MAX_ALBUM_NAME) nb = MAX_ALBUM_NAME;
            memcpy(pst->album_name, work_buffer + 1, nb);
            pst->album_name[nb] = 0;
            syslog(LOG_NOTICE, "TALB = %s", pst->album_name);
            break;
        case ID_TCOM:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb-1] = 0;
#ifdef ID_DEBUG
            syslog(LOG_NOTICE, "TCOM = %s", work_buffer + 1);
#endif
            break;
        case ID_TCON:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb-1] = 0;
#ifdef ID_DEBUG
            syslog(LOG_NOTICE, "TCON = %s", work_buffer + 1);
#endif
            break;
        case ID_TIT2:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb] = 0;
            if (nb > MAX_SONG_NAME) nb = MAX_SONG_NAME;
            memcpy(pst->song_name, work_buffer + 1, nb);
            pst->song_name[nb] = 0;
            syslog(LOG_NOTICE, "TIT2 = %s", pst->song_name);
            break;
        case ID_TLEN:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb-1] = 0;
            syslog(LOG_NOTICE, "TLEN = %s", work_buffer + 1);
            pst->title_length = atoi(work_buffer+1);
            break;
        case ID_TPE1:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb] = 0;
            if (nb > MAX_PERFORMER_NAME) nb = MAX_PERFORMER_NAME;
            memcpy(pst->performer_name, work_buffer + 1, nb);
            pst->performer_name[nb] = 0;
            syslog(LOG_NOTICE, "TPE1 = %s", pst->performer_name);
            break;
        case ID_TRCK:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb-1] = 0;
#ifdef ID_DEBUG
            syslog(LOG_NOTICE, "TRCK = %s", work_buffer + 1);
#endif
            break;
        case ID_TYER:
            f_read(file, work_buffer, nb, &b_read);
            work_buffer[nb-1] = 0;
#ifdef ID_DEBUG
            syslog(LOG_NOTICE, "TYER = %s", work_buffer + 1);
#endif
            break;
#ifdef USE_MCDI
        case ID_MCDI:
            syslog(LOG_NOTICE, "MCDI: len = %02x.%02x.%02x.%02x --> %d\n",
                frheader.frsize[0],
                frheader.frsize[1],
                frheader.frsize[2],
                frheader.frsize[3],
                fs);
            f_lseek(file, file->fptr + fs);
            break;
#endif
        case ID_NULL:
            break;
        default:
#ifdef XID_DEBUG
            syslog(LOG_NOTICE, "%c%c%c%c: flag = %04x, fs = %d, left = %d",
                *cp, cp[1], cp[2], cp[3], ntohs(frheader.flags), fs, len - fs - 10);
#endif
            f_read(file, work_buffer, nb, &b_read);
            break;
        }
dly_tsk(3);
        while (left > 0) {
            if (left > sizeof(work_buffer))
                nb = sizeof(work_buffer);
            else
                nb = left;
            f_read(file, work_buffer, nb, &b_read);
            left -= nb;
        }
        if (type == ID_NULL)
            break;
        len -= fs + 10;
    }
    /* Now, file has seeked to beginning of MP3 music data */
    return FR_OK;
}
