/*
*  XmNap  A Motif napster client
*
*  Copyright (C) 2000 Mats Peterson
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; see the file COPYING.  If not, write to
*  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
*  Boston, MA 02111-1307, USA.
*
*  Please send any comments/bug reports to
*  [email protected]  (Mats Peterson)
*/

#include <Xm/Xm.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "mp3info.h"
#include "msgbox.h"
#include "util.h"

static int bitrateTbl[2][3][16] = {
{
 /* MPEG 2 & 2.5 */
 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0}, /* Layer III */
 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0}, /* Layer II */
 {0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0} /* Layer I  */
},
{
 /* MPEG 1 */
 {0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,0}, /* Layer III */
 {0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,0}, /* Layer II */
 {0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,0}  /* Layer I */
}
};

static int freqTbl[4][3] = {
 {32000, 16000,  8000},  /* MPEG 2.5 */
 {    0,     0,     0},  /* reserved */
 {22050, 24000, 16000},  /* MPEG 2   */
 {44100, 48000, 32000}   /* MPEG 1   */
};


MP3INFO* MP3Info(String fileName)
{
   static MP3INFO *info = NULL;
   FILE *fd;
   char tmp[128], hdrSize[4];
   unsigned long bitHeader;
   int tryCount = 0, skip, fileSize, hdrSkip;
   float frameSize;

   if (! (fd = fopen(fileName, "r"))) {
       ErrMsg(strerror(errno));
       return NULL;
   }

   if (info)
       XtFree((char*)info);
   info = XtNew(MP3INFO);

   fseek(fd, 0, SEEK_END);
   fileSize = ftell(fd);
   fseek(fd, 0, SEEK_SET);

   fread(&tmp[1], 1, 3, fd);

   do {
       tmp[0]=tmp[1];
       tmp[1]=tmp[2];
       tmp[2]=tmp[3];

       fread(&tmp[3], 1, 1, fd);

       /* check for ID3v2 tag */
       if ((tryCount == 0) && (! strncmp(tmp, "ID3", 3))) {
           /* skip id3v2 version and flags */
           fread(tmp, 1, 2, fd);

           /* read id3v2 header size */
           fread(hdrSize, 1, 4, fd);

           /* compute bytes to skip */

           hdrSkip = ((int)hdrSize[3] |
                   ((int)hdrSize[2] << (8 - 1)) |
                   ((int)hdrSize[1] << (16 - 2)) |
                   ((int)hdrSize[0] << (24 - 3))) + 10;

           /* skip */
           fseek(fd, hdrSkip, SEEK_SET);

           /* and get (hopefully) the first frame header */
           fread(tmp, 1, 4, fd);
       }

       bitHeader = (unsigned long)(
           ( (tmp[0] & 255) << 24) | ( (tmp[1] & 255) << 16) |
           ( (tmp[2] & 255) <<  8) | ( (tmp[3] & 255)      )
           );

       info->sync = (bitHeader >> 21)&0x7ff;
       info->version = ((bitHeader >> 19)&0x3);
       info->layer = ((bitHeader >> 17)&0x3);
       info->bitrate = ((bitHeader >> 12)&0xf);
       info->freq = ((bitHeader >> 10)&0x3);

       tryCount++;
       if (tryCount >= 500)
           return NULL;
   } while (info->sync!=0x7ff || info->version==1 ||
       info->layer==0 || info->bitrate==0xf ||
       info->freq==3);

   info->protect = ((bitHeader >> 16)&0x1);

   /*  get index & calc brate from it */
   info->bitrate =
       bitrateTbl[info->version & 1][info->layer - 1][info->bitrate];

   info->freq = freqTbl[info->version][info->freq];

   info->padding = ((bitHeader >> 9)&0x1);
   info->channels = ((bitHeader >> 6)&0x3);
   info->ext = ((bitHeader >> 4)&0x3);
   info->copyright = ((bitHeader >> 3)&0x1);
   info->original = ((bitHeader >> 2)&0x1);
   info->emphasis = (bitHeader & 0x3);

   skip=0;

   /* is there a variable bit rate bit */
   if (info->version == 3 ) {    /* mpeg version 1 */
       if (info->channels == 3)
           skip = 17;              /* Single Channel */
       else
           skip = 32;
   } else {                        /* mpeg version 2 or 2.5 */
       if (info->channels==3 )
           skip = 9;               /* Single Channel */
       else
           skip = 17;
   }

   /* read next twelve bits in */
   fread(tmp, 1, skip, fd);
   fread(tmp, 1, 12, fd);

   if (! strncmp("Xing", tmp, 4)) {
       /* Got a varible bitrate */
       bitHeader = (unsigned long) (
           (tmp[4] << 24) |
           (tmp[5] << 16) |
           (tmp[6] <<  8) |
           (tmp[7]      )
           );
       if (bitHeader & 1) {     /* there is frame data */
           /* get the num of frames */
           bitHeader = (unsigned long)(
               (tmp[8] << 24) |
               (tmp[9] << 16) |
               (tmp[10] <<  8) |
               (tmp[11]      )
               );

               frameSize = (float)fileSize / (float)bitHeader;
               info->bitrate =
                   (int)(( frameSize * (float)info->freq) /
                   ( 1000.0 * ( (info->layer==3) ? 12.0 : 144.0)) );
       }
   }

   info->seconds = (8 * fileSize) / 1000;
   if (info->bitrate)
       info->seconds = info->seconds/info->bitrate;
   else
       info->seconds=0;

   fclose(fd);
   return info;
}