/* --------------------------------------------------------------------------

   MusicBrainz -- The Internet music metaDatabase

   Copyright (C) 2000 Robert Kaye
   Portions  (C) 1999 EMusic.com
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   
   This library 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
   Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

     $Id: vmm.c,v 1.3 2000/10/24 15:57:09 robert Exp $

----------------------------------------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h> 

#include <vorbis/vorbisfile.h>
#include <musicbrainz/mb_c.h>

char *load_metadata(char *file);
int save_metadata(char *file, char *outfile, char *rdf);

/* This metaData struct is only used for gathering metaData from the 
   comment stream and the ogg file itself */
typedef struct
{
   char         *artist;
   char         *album;
   char         *title;
   char         *genre;
   char         *comment;
   char         *file;
   char         *GUID;
   int           year;
   short         track;
   double        duration;
   size_t        size;
} MetaData;

#define PCMOUT_SIZE 4096

static int quiet = 0;
static int nosubmit = 0;

MetaData *new_metadata(void)
{
   MetaData *m;

   m = (MetaData *)malloc(sizeof(MetaData));
   memset(m, 0, sizeof(MetaData));

   return m;
}

void delete_metadata(MetaData *m)
{
   if (m->artist)
      free(m->artist);
   if (m->album)
      free(m->album);
   if (m->title)
      free(m->title);
   if (m->file)
      free(m->file);
   if (m->GUID)
      free(m->GUID);
   if (m->genre)
      free(m->genre);
   if (m->comment)
      free(m->comment);

   free(m);
}

int get_metadata(char *file, MetaData *metaData)
{
   char           *temp;
   FILE           *fpFile;
   OggVorbis_File  vf;
   vorbis_comment *comment;

   fpFile = fopen(file, "rb");
   if (fpFile == NULL)
      return 0;

   memset(&vf, 0, sizeof(vf));
   if (ov_open(fpFile, &vf, NULL, 0) < 0)
   {
      fclose(fpFile);
      return 0;
   }

   comment = ov_comment(&vf, -1);
   if (comment)
   {
       temp = vorbis_comment_query(comment, "title", 0);
       if (temp)
           metaData->title = strdup(temp);

       temp = vorbis_comment_query(comment, "artist", 0);
       if (temp)
           metaData->artist = strdup(temp);

       temp = vorbis_comment_query(comment, "genre", 0);
       if (temp)
           metaData->genre = strdup(temp);

       temp = vorbis_comment_query(comment, "track", 0);
       if (temp)
           metaData->track = atoi(temp);

       temp = vorbis_comment_query(comment, "comment", 0);
       if (temp)
           metaData->comment = strdup(temp);

       metaData->track = 0;
       metaData->file = strdup(file);
   }
   metaData->duration = ov_time_total(&vf, 0);
   ov_clear(&vf);

   return 1;
}

char *submit_metadata(MetaData *metaData)
{
   musicbrainz_t o;
   int    ret;
   char   *args[12];
   char    temp[255];
   int     i;
   char   *rdf = NULL;
   int     len;

   if (metaData == NULL)
       return NULL;

   o = mb_New();
   //mb_SetServer(o, MUSICBRAINZ_SERVER, MUSICBRAINZ_PORT);

   args[0] = metaData->title ? strdup(metaData->title) : "";
   args[1] = metaData->GUID ? strdup(metaData->GUID) : "";
   args[2] = metaData->artist ? strdup(metaData->file) : "";
   args[3] = metaData->artist ? strdup(metaData->artist) : "";
   args[4] = metaData->album ? strdup(metaData->album) : "";
   sprintf(temp, "%d", metaData->track);
   args[5] = strdup(temp);
   sprintf(temp, "%lf", metaData->duration);
   args[6] = strdup(temp);
   sprintf(temp, "%u", metaData->year);
   args[7] = strdup(temp);
   args[8] = metaData->genre ? strdup(metaData->genre) : "";
   args[9] = metaData->comment ? strdup(metaData->comment) : "";
   args[10] = NULL;

   ret = mb_QueryWithArgs(o, MB_ExchangeMetadata, args);
   for(i = 0; i < 10; i++)
      if (strlen(args[i]) > 0)
          free(args[i]);

   if (!ret)
   {
       char err[255];
       mb_GetQueryError(o, err, 255);
       if (!quiet)
       {
          printf("Data submit error: %s\n\n", err);
       }
   }
   else
   {
       if (!quiet)
          printf("MetaData was exchanged with server.\n\n");

       len = mb_GetResultRDFLen(o) + 1;
       rdf = (char *)malloc(len);
       mb_GetResultRDF(o, rdf, len);
       printf("Server response:\n%s\n\n", rdf);
   }

   mb_Delete(o);  

   return rdf;
}

/*------------------------------------------*/

int generate_sig(char *file, char ascii_sig[37])
{
   char           *temp;
   FILE           *fpFile;
   OggVorbis_File  vf;
   int             eof = 0, current_section;
   char           *pcmout;
   long            ret, ok = 0;
   musicbrainz_t   o;
   vorbis_info    *vi;
   char            sig[17];

   fpFile = fopen(file, "rb");
   if (fpFile == NULL)
      return 0;

   memset(&vf, 0, sizeof(vf));
   if (ov_open(fpFile, &vf, NULL, 0) < 0)
   {
      fclose(fpFile);
      return 0;
   }

   vi = ov_info(&vf,-1);

   o = mb_New();
   mb_SetPCMDataInfo(o, vi->rate, vi->channels, 16);
   pcmout = (char *)malloc(PCMOUT_SIZE);
   while(!eof)
   {
       ret=ov_read(&vf,pcmout,PCMOUT_SIZE,0,2,1,&current_section);
       if (ret > 0)
       {
           ret = mb_GenerateSignature(o, pcmout, ret, sig, NULL);
           if (ret)
           {
              ok = 1;
              break;
           }
       }
       else
       if (ret == 0)
       {
           mb_GenerateSignatureNow(o, sig, NULL);
           ok = 1;
       }
       else
       {
           fprintf(stderr, "Encountered error in bitstream.\n");
           exit(0);
       }
   }
   ov_clear(&vf);
   free(pcmout);
   mb_Delete(o);  

   if (ok)
      mb_ConvertSigToASCII(o, sig, ascii_sig);

   return ok;
}

/*------------------------------------------*/
int main(int argc, char *argv[])
{
   char         sig[100], *rdf;
   MetaData    *m;
   struct stat  s;
   int          index = 1;

   if (argc < 2)
   {
       printf("Usage: vmm [-q] [-n] <ogg file>\n");
       exit(0);
   }

   if (strcmp(argv[index], "-q") == 0)
   {
       quiet = 1;
       index++;
   }

   if (strcmp(argv[index], "-n") == 0)
   {
       nosubmit = 1;
       index++;
   }

   rdf = load_metadata(argv[index]);
   if (rdf)
   {
       printf("Found existing metadata:\n%s\n", rdf);
       exit(0);
   }
   else
       printf("No advanced metadata found in file.\n\n");

   m = new_metadata();
   if (get_metadata(argv[index], m))
   {
       stat(argv[index], &s);
       m->size = s.st_size;
       if (!quiet)
       {
          printf("MetaData comments in ogg file:\n");
          printf("    Title: %s\n", m->title);
          printf("    Album: %s\n", m->album);
          printf("   Artist: %s\n", m->artist);
          printf("    Genre: %s\n", m->genre);
          printf("  Comment: %s\n", m->comment);
          printf("    Track: %d\n", m->track);
          printf(" Duration: %.1lf seconds\n", m->duration);
          printf("     Size: %u\n", m->size);
       }
       m->duration *= 1000;

       if (generate_sig(argv[index], sig))
       {
           m->GUID = strdup(sig);
           if (!quiet)
               printf("Signature: ");
           printf("%s\n", sig);
           if (!nosubmit)
           {
              char *rdf;

              rdf = submit_metadata(m);
              if (save_metadata(argv[index], "out.ogg", rdf)) 
                  printf("Saved metadata to file.\n");
              else
                  printf("Saving metadata failed.\n");
              free(rdf);
           }
       }
       else
       {
           if (!quiet)
              printf("Error calculating signature.\n");
       }
   }
   else
   {
       printf("Error getting metadata.\n");
   }

   delete_metadata(m);

   return 0;
}

