/*
 * Copyright (C) 2002-2003 Fhg Fokus
 *
 * This file is part of SEMS, a free SIP media server.
 *
 * SEMS 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 released under
 * the GPL with the additional exemption that compiling, linking,
 * and/or using OpenSSL is allowed.
 *
 * For a license to use the SEMS software under conditions
 * other than those described here, or to purchase support for this
 * software, please contact iptel.org by e-mail at the following addresses:
 *    info@iptel.org
 *
 * SEMS 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; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "amci.h"
#include "codecs.h" 
#include "log.h"

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <lame/lame.h>

#ifdef WITH_MPG123DECODER
#include "mpg123.h"
#endif

/**
 * @file apps/mp3/mp3.c
 * lame-MP3 support 
 * This plug-in writes MP3 files using lame encoder. 
 *
 * See http://lame.sourceforge.net/ .
 * 
 * If WITH_MPG123DECODER=yes (default), mpg123 mp3 decoding is supported as well.
 * 
 */

#define MP3_phone  1 

static int MP3_2_Pcm16( unsigned char* out_buf, unsigned char* in_buf, 
			unsigned int size, unsigned int channels, unsigned int rate, 
			long h_codec );

static int MP3_ModuleLoad(void);
static void MP3_ModuleDestroy(void);

static int Pcm16_2_MP3( unsigned char* out_buf, unsigned char* in_buf, unsigned int size,
			 unsigned int channels, unsigned int rate, long h_codec );
static long MP3_create(const char* format_parameters, amci_codec_fmt_info_t* format_description);
static void MP3_destroy(long h_inst);
static int MP3_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec);
static int MP3_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec,
					 struct amci_codec_t *codec);
static unsigned int mp3_bytes2samples(long h_codec, unsigned int num_bytes);
static unsigned int mp3_samples2bytes(long h_codec, unsigned int num_samples);

BEGIN_EXPORTS( "mp3", MP3_ModuleLoad, MP3_ModuleDestroy )
    BEGIN_CODECS
CODEC( CODEC_MP3, Pcm16_2_MP3, MP3_2_Pcm16, (amci_plc_t)0, (amci_codec_init_t)MP3_create, (amci_codec_destroy_t)MP3_destroy, mp3_bytes2samples, mp3_samples2bytes)
    END_CODECS
    
    BEGIN_PAYLOADS
    END_PAYLOADS

    BEGIN_FILE_FORMATS
      BEGIN_FILE_FORMAT( "MP3", "mp3", "audio/x-mp3", MP3_open, MP3_close, 0, 0)
        BEGIN_SUBTYPES
          SUBTYPE( MP3_phone,  "MP3",  8000, 1, CODEC_MP3 )
        END_SUBTYPES
      END_FILE_FORMAT
    END_FILE_FORMATS

END_EXPORTS

int MP3_ModuleLoad(void) {
#ifdef WITH_MPG123DECODER
  int res;
  if ((res = mpg123_init()) != MPG123_OK) {
    ERROR("initializing mpg123 failed: %d\n", res);
    return -1;
  }
#endif

  DBG("MP3 module loaded.\n");
  return 0;
}

void MP3_ModuleDestroy(void) {
#ifdef WITH_MPG123DECODER
  mpg123_exit();
#endif
  DBG("MP3 module destroyed.\n");
}

#define MP3_FRAMESAMPLES   1152
/* for 128kbit 44.1 khz  FrameSize = 144 * BitRate / (SampleRate + Padding) */
#define MP3_MAXFRAMEBYTES   417 
#define DECODED_BUF_SIZE MP3_FRAMESAMPLES * 2 * 2 // space for two decoded frames
#define CODED_BUF_SIZE  MP3_MAXFRAMEBYTES * 2     // space for two encoded frames

typedef struct {
  lame_global_flags* gfp;
#ifdef WITH_MPG123DECODER
  mpg123_handle* mpg123_h;
  long rate;
  int channels, enc;

/*   unsigned char decoded_buf[DECODED_BUF_SIZE]; */
/*   size_t decoded_begin; */
/*   size_t decoded_end; */
/*   unsigned char coded_buf[CODED_BUF_SIZE]; */
/*   size_t coded_pos; */
#endif  
} mp3_coder_state;

void no_output(const char *format, va_list ap)
{
    return;
//    (void) vfprintf(stdout, format, ap);
}

long MP3_create(const char* format_parameters, amci_codec_fmt_info_t* format_description) {
  mp3_coder_state* coder_state;
  int ret_code;
  
  coder_state = malloc(sizeof(mp3_coder_state));
  if (!coder_state) {
    ERROR("no memory for allocating mp3 coder state\n");
    return -1;
  }
  
  DBG("MP3: creating lame %s\n", get_lame_version());
  format_description[0].id = 0; 
  coder_state->gfp = lame_init(); 
    
  if (!coder_state->gfp) {
    ERROR("initialiting lame\n");
    free(coder_state);
    return -1;
  }

  lame_set_errorf(coder_state->gfp, &no_output);
  lame_set_debugf(coder_state->gfp, &no_output);
  lame_set_msgf(coder_state->gfp, &no_output);
  
  lame_set_num_channels(coder_state->gfp,1);
  lame_set_in_samplerate(coder_state->gfp,8000);
  lame_set_brate(coder_state->gfp,16);
  lame_set_mode(coder_state->gfp,3); // mono
  lame_set_quality(coder_state->gfp,2);   /* 2=high  5 = medium  7=low */ 
  
  id3tag_init(coder_state->gfp);
  id3tag_set_title(coder_state->gfp, "mp3 voicemail by iptel.org");
  ret_code = lame_init_params(coder_state->gfp);
  
  if (ret_code < 0) {
    ERROR("lame encoder init failed: return code is %d\n", ret_code);
    free(coder_state);
    return -1;
  }
  

#ifdef WITH_MPG123DECODER
  coder_state->mpg123_h = mpg123_new(NULL, NULL);
  if (!coder_state->mpg123_h) {
    ERROR("initializing mpg123 decoder instance\n");
    return -1;
  }

  /* suppress output */
  mpg123_param(coder_state->mpg123_h, MPG123_FLAGS, MPG123_QUIET /* | MPG123_FORCE_MONO */,0);

/*   mpg123_param(coder_state->mpg123_h, MPG123_VERBOSE, 0, 0); */

#endif

  return (long)coder_state;
}

void MP3_destroy(long h_inst) {
  if (h_inst){
    if (((mp3_coder_state*)h_inst)->gfp)
      lame_close(((mp3_coder_state*)h_inst)->gfp);
#ifdef WITH_MPG123DECODER
    mpg123_delete(((mp3_coder_state*)h_inst)->mpg123_h);
#endif

    free((mp3_coder_state*)h_inst);
  }
}

static int Pcm16_2_MP3( unsigned char* out_buf, unsigned char* in_buf, unsigned int size,
		  unsigned int channels, unsigned int rate, long h_codec )
{ 

  if (!h_codec){
    ERROR("MP3 codec not initialized.\n");
    return 0;
  }

  if ((channels!=1)||(rate!=8000)) {
    ERROR("Unsupported input format for MP3 encoder.\n");
    return 0;
  }

/*   DBG("h_codec=0x%lx; in_buf=0x%lx; size=%i\n", */
/*       (unsigned long)h_codec,(unsigned long)in_buf,size);  */

  int ret =  lame_encode_buffer(((mp3_coder_state*)h_codec)->gfp, 
				(short*) in_buf,        // left channel
				/* (short*) in_buf */0, // right channel
				size / 2 ,              // no of samples (size is in bytes!)
				out_buf,
  				AUDIO_BUFFER_SIZE); 

  switch(ret){
    // 0 is valid: if not enough samples for an mp3 
    //frame lame will not return anything
  case 0:  /*DBG("lame_encode_buffer returned 0\n");*/ break; 
  case -1: ERROR("mp3buf was too small\n"); break;
  case -2: ERROR("malloc() problem\n"); break;
  case -3: ERROR("lame_init_params() not called\n"); break;
  case -4: ERROR("psycho acoustic problems. uh!\n"); break;
  }

  return ret;
}


static unsigned int mp3_samples2bytes(long h_codec, unsigned int num_samples)
{
#ifndef WITH_MPG123DECODER
  return num_samples;
#else

  /* 
   * 150 bytes is about one frame as produced by the mp3 writer.
   * for higher bitrate files this will only introduce the small performance 
   * penalty of multiple read() calls per frame-decode.
   */
  return 150;

/*   or a full frame? */
/*    we don't know bitrate - so use 128000 as max bitrate */
/*     144 * BitRate / (SampleRate + Padding) */  
/*   unsigned int res =  144 * 128000 / (((mp3_coder_state*)h_codec)->rate + 1); */
/*   if (res > AUDIO_BUFFER_SIZE) */
/*     res = AUDIO_BUFFER_SIZE; */
/*   return res; */

#endif
}

static int MP3_2_Pcm16( unsigned char* out_buf, unsigned char* in_buf, unsigned int size,
			unsigned int channels, unsigned int rate, 
			long h_codec )
{

#ifndef WITH_MPG123DECODER
    ERROR("MP3 decoding support not compiled in.\n");
    return -1;
#else
    int res;
    size_t decoded_size;
    mp3_coder_state* coder_state;

    if (!h_codec) {
      ERROR("mp3 decoder not initialized!\n");
      return -1;
    }
        
    coder_state = (mp3_coder_state*)h_codec;
    res = mpg123_decode(coder_state->mpg123_h, in_buf, size, 
			out_buf, AUDIO_BUFFER_SIZE, &decoded_size);
    
    if (res == MPG123_NEW_FORMAT) {
      WARN("intermediate mp3 file format change!\n");
    }
    if (res == MPG123_ERR) {
      ERROR("decoding mp3: '%s'\n", 
	    mpg123_strerror(coder_state->mpg123_h));
      return -1;
    }
/*     DBG("mp3: decoded %d\n", decoded_size); */
    return decoded_size;
#endif
}

#define SAFE_READ(buf,s,fp,sr) \
    sr = fread(buf,s,1,fp);\
    if((sr != 1) || ferror(fp)) return -1;

#define SAFE_WRITE(buf,s,fp) \
    fwrite(buf,s,1,fp);\
    if(ferror(fp)) return -1;

static int MP3_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec)
{

#ifdef WITH_MPG123DECODER
  unsigned char mp_rd_buf[20]; 
  size_t sr, decoded_size;
  mp3_coder_state* coder_state;
  int res;
  DBG("mp3_open.\n");
#endif

   if(options == AMCI_RDONLY){
#ifndef WITH_MPG123DECODER
    ERROR("MP3 decoding support not compiled in.\n");
    return -1;
#else
     if (!h_codec) {
       ERROR("mp3 decoder not initialized!\n");
       return -1;
     }
     coder_state = (mp3_coder_state*)h_codec;
     
     DBG("Initializing mpg123 codec state.\n");
     res = mpg123_open_feed(coder_state->mpg123_h);
     if (MPG123_ERR == res) {
       ERROR("mpg123_open_feed returned mpg123 error '%s'\n", 
	     mpg123_strerror(coder_state->mpg123_h));
       return -1;
     }

     /* read until we know the format */
     while (res!= MPG123_NEW_FORMAT) {
       SAFE_READ(mp_rd_buf, 20, fp, sr);

       res = mpg123_decode(coder_state->mpg123_h, mp_rd_buf, 20, 
			   mp_rd_buf, 0, &decoded_size);
       if (res == MPG123_ERR) {
	 ERROR("trying to determine MP3 file format: '%s'\n", 
	       mpg123_strerror(coder_state->mpg123_h));
	 return -1;
       }
     }
          
     mpg123_getformat(coder_state->mpg123_h, 
		      &coder_state->rate, &coder_state->channels, &coder_state->enc);

     DBG("mpg123: New format: %li Hz, %i channels, encoding value %i\n", 
	 coder_state->rate, coder_state->channels, coder_state->enc);

     fmt_desc->subtype   = 1; // ?
     fmt_desc->rate      = coder_state->rate;
     fmt_desc->channels  = coder_state->channels;
     fmt_desc->data_size = -1;

     /* set buffering parameters */
     fmt_desc->buffer_size   = MP3_FRAMESAMPLES * 8;
     fmt_desc->buffer_thresh = MP3_FRAMESAMPLES;
     fmt_desc->buffer_full_thresh = MP3_FRAMESAMPLES * 3; 

     return 0;
#endif
   }  
   return 0;
}

static int MP3_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec,
				    struct amci_codec_t *codec)
{
    int final_samples;

    unsigned char  mp3buffer[7200];    
    DBG("MP3: close. \n");
    if(options == AMCI_WRONLY) {
      if (!h_codec || !((mp3_coder_state*)h_codec)->gfp) {
	ERROR("no valid codec handle!\n");
	return -1;
      }

      if ((final_samples = lame_encode_flush(((mp3_coder_state*)h_codec)->gfp,mp3buffer, 7200))) {
	    fwrite(mp3buffer, 1, final_samples, fp);
	    DBG("MP3: flushing %d bytes from MP3 encoder.\n", final_samples);
	}
	lame_mp3_tags_fid(((mp3_coder_state*)h_codec)->gfp,fp);
    }
    return 0;
}


static unsigned int mp3_bytes2samples(long h_codec, unsigned int num_bytes)
{
    // even though we are using CBR (16kbps) and encoding 
    // 128kbps to 16kbps would give num_samples/8 (or num_bytes*8)
    // as MP3 is not used as payload calculating this would make no 
    // sense. The whole frame is encoded anyway.

	//	WARN("size calculation not possible with MP3 codec.\n");
    return num_bytes;
}