/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2002 by Dave Chapman
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

/*

Cue file example 1 - From a CD ripped using Exact Audio Copy 

PERFORMER "Blur"
TITLE "Parklife"
FILE "D:\Range.wav" WAVE
  TRACK 01 AUDIO
    TITLE "Girls & Boys"
    PERFORMER "Blur"
    INDEX 00 00:00:00
    INDEX 01 00:00:33
  TRACK 02 AUDIO
    TITLE "Tracy Jacks"
    PERFORMER "Blur"
    INDEX 00 04:49:01
    INDEX 01 04:51:23

Example 2 - from "xmcd2cue" documentation, a program to convert cddb
entries to cue files.

TITLE "Pulse CD2"
PERFORMER "Pink Floyd"
REM Total length: 01:11:47
REM ID: d510d10d
FILE "d510d10d.mp3" MP3
  TRACK 01 AUDIO
    TITLE "The Dark Side of the Moon: Speak To Me"
    PREGAP 00:02:00
    INDEX 01 00:00:00
  TRACK 02 AUDIO
    TITLE "Breathe"
    INDEX 01 02:27:64
  TRACK 03 AUDIO
    TITLE "On the Run"
    INDEX 01 05:01:16

Strategy:

1) The cue file is completely read into memory (maximum size of buffer is
defined as MAX_CUEFILE_SIZE in applimits.h)

2) The memory buffer is parsed to extract TITLE/PERFORMER for the
entire file, and TITLE/PERFORMER/INDEX for each of the tracks.  The
TITLE/PERFORMER strings are stored as offsets into this buffer, and
the closing " character is replaced with zero in the buffer (to create
proper C strings).

With a MAX_CUEFILE_SIZE of 8192 bytes, and a MAX_TRACKS_IN_CUEFILE of
99 (the limit for an audio CD), the cuefile structure takes 9386 bytes
of RAM.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef DEBUG_STANDALONE
#include <fcntl.h>
#else
#include <file.h>
#endif

#include "cuefile.h"

/* Read a cue file from disk to memory and then parse it */

#ifdef DEBUG_STANDALONE
int lines;
#endif

int read_cuefile(struct cuefile_t* cuefile, char* mp3filename) {
  int fd;
  int nread;
  int i;
  int m,s,f;

  memcpy(cuefile->filename,mp3filename,MAX_PATH);
  i=strlen(cuefile->filename);
  if (i > 4) {
    cuefile->filename[i-3]='c';
    cuefile->filename[i-2]='u';
    cuefile->filename[i-1]='e';
  } else {
    return -1;
  }

  fd=open(cuefile->filename,O_RDONLY);

  if(fd < 0) {
    return -1;
  } else {
    nread=read(fd,cuefile->buf,8192);
    close(fd);

    /* Make sure the buffer ends with a zero - it makes parsing simpler */    
    if (nread==8192) { nread--; }
    cuefile->buf[nread]=0;

    cuefile->n=0;
    cuefile->tracks[0].title_ofs=0;
    cuefile->tracks[0].performer_ofs=0;
    cuefile->filesize=nread;

#ifdef DEBUG_STANDALONE
    lines=1;
#endif
    i=0;
    while(i<nread) {
      /* Parse a line of the cuefile->buffer in each iteration */
      while (cuefile->buf[i]==' ') i++;

      if (strncasecmp(&cuefile->buf[i],"TRACK ",6)==0) {
        cuefile->n++;
        cuefile->tracks[cuefile->n].title_ofs=0;
        cuefile->tracks[cuefile->n].performer_ofs=0;
        cuefile->tracks[cuefile->n].track_index_ofs=0;
        cuefile->tracks[cuefile->n].track_index=-1;
      } else if (strncasecmp(&cuefile->buf[i],"TITLE \"",7)==0) {
        i+=7;
        cuefile->tracks[cuefile->n].title_ofs=i;
        while((cuefile->buf[i]!='"') && (cuefile->buf[i]>=' ')) i++;
        if (cuefile->buf[i]!='"') return -1;
        cuefile->buf[i++]=0;
      } else if(strncasecmp(&cuefile->buf[i],"PERFORMER \"",11)==0) {
        i+=11;
        cuefile->tracks[cuefile->n].performer_ofs=i;
        while((cuefile->buf[i]!='"') && (cuefile->buf[i]>=' ')) i++;
        if (cuefile->buf[i]!='"') return -1;
        cuefile->buf[i++]=0;
      } else if(strncasecmp(&cuefile->buf[i],"INDEX ",6)==0) {
        /* We only read the first INDEX for a track */
        if (cuefile->tracks[cuefile->n].track_index_ofs==0) {
          i+=6;
          while (cuefile->buf[i]==' ') i++;
          while ((cuefile->buf[i] >= '0') && (cuefile->buf[i]<='9')) i++;
          while (cuefile->buf[i]==' ') i++;
          cuefile->tracks[cuefile->n].track_index_ofs=i;

          m=0; s=0; f=0;
          while ((cuefile->buf[i] >= '0') && (cuefile->buf[i]<='9')) {
            m=(m*10)+(cuefile->buf[i]-'0');
            i++;
          }
          if (cuefile->buf[i]==':') { i++; }
          else { return -1; }
          while ((cuefile->buf[i] >= '0') && (cuefile->buf[i]<='9')) {
            s=(s*10)+(cuefile->buf[i]-'0');
            i++;
          }

          if (cuefile->buf[i]==':') { 
            i++; 
            while ((cuefile->buf[i] >= '0') && (cuefile->buf[i]<='9')) {
              f=(f*10)+(cuefile->buf[i]-'0');
              i++;
            }
          }

          /* m is minutes, s is seconds, and f is frame number (0-75) */
          /* track_index is in milliseconds */
          cuefile->tracks[cuefile->n].track_index=
            ((f*1000)/76)+(s*1000)+(m*60000);

          /* Sanity check - index n must be greater than index (n-1) */
          if (cuefile->n > 1) {
            if (cuefile->tracks[cuefile->n].track_index <=
                cuefile->tracks[cuefile->n-1].track_index) {
              return -1;
            }
          }
        }
      }
      /* Skip to end of line */
      while ((i<nread) && (cuefile->buf[i]!='\n') && (cuefile->buf[i]!='\r')) i++;
      if ((cuefile->buf[i]=='\n') && (cuefile->buf[i+1]=='\r')) i+=2;
      else i++;
#ifdef DEBUG_STANDALONE
      lines++;
#endif
    }
    cuefile->buf[0]=0;
  }
  return 0;
}

void get_next_cue(int elapsed,struct cuefile_t* cuefile, int* next_subtrack) {
  int i=1;

  while ((i<=cuefile->n) && (cuefile->tracks[i].track_index < elapsed))
    i++;

  if (i > cuefile->n) {
    *next_subtrack=-1;
  } else {
    *next_subtrack=cuefile->tracks[i].track_index;
  }
}

void get_prev_cue(int elapsed,struct cuefile_t* cuefile, int* prev_subtrack) {
  int i=1;

  while ((i<=cuefile->n) && (cuefile->tracks[i].track_index < elapsed-3000))
    i++;

  if (i > 1) {
    *prev_subtrack=cuefile->tracks[i-1].track_index;
  } else {
    *prev_subtrack=cuefile->tracks[1].track_index;
    if (elapsed < *prev_subtrack)
      *prev_subtrack=0;
  }
}

#ifdef DEBUG_STANDALONE

int main(int argc, char** argv) {
  int i,j;
  struct cuefile_t cuefile;

  printf("RAM used by cuefile structure: %d\n",sizeof(struct cuefile_t));

  printf("argc: %d\n",argc);
  for (i=1;i<argc;i++) {
    if (read_cuefile(&cuefile,argv[i])==0) {
      printf("Read cuefile\n");
      printf("Album Title: \"%s\"\n",&cuefile.buf[cuefile.tracks[0].title_ofs]);
      printf("Album Performer: \"%s\"\n",&cuefile.buf[cuefile.tracks[0].performer_ofs]);
      for (j=1;j<=cuefile.n;j++) {
         printf("%02d: title=\"%s\"\n",j,&cuefile.buf[cuefile.tracks[j].title_ofs]);
         printf("%02d: performer=\"%s\"\n",j,&cuefile.buf[cuefile.tracks[j].performer_ofs]);
         printf("%02d: index=%d\n",j,cuefile.tracks[j].track_index);
      }
      printf("END\n");
    } else {
      printf("Can not read cue file %s - error on line %d\n",argv[i],lines);
    }
  }
}
  
#endif

