/*
 * Copyright (C) 2005 the xine project
 *
 * This file is part of xine, a free video player.
 *
 * xine 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.
 *
 * xine 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 *
 * xine interface to libmusepack by James Stembridge <jstembridge@gmail.com>
 *
 * TODO:
 *   32bit float output
 *   Seeking??
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define LOG_MODULE "mpc_decoder"
#define LOG_VERBOSE
/*
#define LOG
*/

#include "xine_internal.h"
#include "audio_out.h"
#include "buffer.h"
#include "xineutils.h"

#ifdef HAVE_MPCDEC_MPCDEC_H
# include <mpcdec/mpcdec.h>
#else
# include "musepack/musepack.h"
#endif

#define MPC_DECODER_MEMSIZE  65536
#define MPC_DECODER_MEMSIZE2 (MPC_DECODER_MEMSIZE/2)

#define INIT_BUFSIZE (MPC_DECODER_MEMSIZE*2)

typedef struct {
  audio_decoder_class_t   decoder_class;
} mpc_class_t;

typedef struct mpc_decoder_s {
  audio_decoder_t  audio_decoder;

  xine_stream_t    *stream;

  int              sample_rate;       /* audio sample rate */
  int              bits_per_sample;   /* bits/sample, usually 8 or 16 */
  int              channels;          /* 1 or 2, usually */

  int              output_open;       /* flag to indicate audio is ready */

  unsigned char   *buf;              /* data accumulation buffer */
  unsigned int     buf_max;          /* maximum size of buf */
  unsigned int     read;             /* size of accum. data already read */
  unsigned int     size;             /* size of accumulated data in buf */

  mpc_reader       reader;
  mpc_streaminfo   streaminfo;
  mpc_decoder      decoder;
  
  int              decoder_ok;
  unsigned int     current_frame;
  
  int32_t          file_size;

} mpc_decoder_t;


/**************************************************************************
 * musepack specific functions
 *************************************************************************/
    
/* Reads size bytes of data into buffer at ptr. */
static int32_t mpc_reader_read(void *data, void *ptr, int size) {
  mpc_decoder_t *this = (mpc_decoder_t *) data;
  
  lprintf("mpc_reader_read: size=%d\n", size);
  
  /* Don't try to read more data than we have */
  if (size > (this->size - this->read))
    size = this->size - this->read;

  /* Copy the data */
  xine_fast_memcpy(ptr, &this->buf[this->read], size);
  
  /* Update our position in the data buffer */
  this->read += size;
  
  return size;
}
 
/* Seeks to byte position offset. */
static mpc_bool_t mpc_reader_seek(void *data, int32_t offset) {
  mpc_decoder_t *this = (mpc_decoder_t *) data;
  
  lprintf("mpc_reader_seek: offset=%d\n", offset);
  
  /* seek is only called when reading the header so we can assume
   * that the buffer starts at the start of the file */
  this->read = offset;
  
  return TRUE;
}

/* Returns the current byte offset in the stream. */
static int32_t mpc_reader_tell(void *data) {
  lprintf("mpc_reader_tell\n");
  
  /* Tell isn't used so just return 0 */
  return 0;
}

/* Returns the total length of the source stream, in bytes. */
static int32_t mpc_reader_get_size(void *data) {
  mpc_decoder_t *this = (mpc_decoder_t *) data;
  
  lprintf("mpc_reader_get_size\n");
  
  return this->file_size;
}

/* True if the stream is a seekable stream. */
static mpc_bool_t mpc_reader_canseek(void *data) {
  lprintf("mpc_reader_canseek\n");
  
  return TRUE;
}

/* Convert 32bit float samples into 16bit int samples */
static inline void float_to_int(float *_f, int16_t *s16, int samples) {
  int i;
  float f;
  for (i = 0; i < samples; i++) {
    f = _f[i] * 32767;
    if (f > INT16_MAX) f = INT16_MAX;
    if (f < INT16_MIN) f = INT16_MIN;
    s16[i] = f;
    /* printf("samples[%d] = %f, %d\n", i, _f[i], s16[num_channels*i]); */
  }
}

/* Decode a musepack frame */
static int mpc_decode_frame (mpc_decoder_t *this) {
  float buffer[MPC_DECODER_BUFFER_LENGTH];
  uint32_t frames;
  
  lprintf("mpd_decode_frame\n");
  
  frames = mpc_decoder_decode(&this->decoder, buffer, 0, 0);
  
  if (frames > 0) {
    audio_buffer_t *audio_buffer;  
    int16_t  *int_samples;
    
    lprintf("got %d samples\n", frames);
    
    /* Get audio buffer */
    audio_buffer = this->stream->audio_out->get_buffer (this->stream->audio_out);
    audio_buffer->vpts = 0;
    audio_buffer->num_frames = frames;
    
    /* Convert samples */
    int_samples = (int16_t *) audio_buffer->mem;
    float_to_int(buffer, int_samples, frames*this->channels);
    
    /* Output converted samples */
    this->stream->audio_out->put_buffer (this->stream->audio_out, audio_buffer, this->stream);
  }
  
  return frames;
}

/**************************************************************************
 * xine audio plugin functions
 *************************************************************************/

static void mpc_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {
  mpc_decoder_t *this = (mpc_decoder_t *) this_gen;
  int err;

  lprintf("mpc_decode_data\n");
  
  if (!_x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_HANDLED))
    return;
  
  /* We don't handle special buffers */
  if (buf->decoder_flags & BUF_FLAG_SPECIAL)
    return;
    
  /* Read header */
  if (buf->decoder_flags & BUF_FLAG_HEADER) {
  
    lprintf("header\n");
  
    /* File size is in decoder_info[0] */
    this->file_size = buf->decoder_info[0];
    
    /* Initialise the data accumulation buffer */
    this->buf     = calloc(1, INIT_BUFSIZE);
    this->buf_max = INIT_BUFSIZE;
    this->read    = 0;
    this->size    = 0;
    
    /* Initialise the reader */
    this->reader.read     = mpc_reader_read;
    this->reader.seek     = mpc_reader_seek;
    this->reader.tell     = mpc_reader_tell;
    this->reader.get_size = mpc_reader_get_size;
    this->reader.canseek  = mpc_reader_canseek;
    this->reader.data     = this;
    
    /* Copy header to buffer */
    xine_fast_memcpy(this->buf, buf->content, buf->size);
    this->size = buf->size;
    
    /* Initialise and read stream info */
    mpc_streaminfo_init(&this->streaminfo);
    
    if ((err = mpc_streaminfo_read(&this->streaminfo, &this->reader))) {
      xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
              _("libmusepack: mpc_streaminfo_read failed: %d\n"), err);
              
      _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_HANDLED, 0);
      return;
    }
    
    this->sample_rate     = this->streaminfo.sample_freq;
    this->channels        = this->streaminfo.channels;
    this->bits_per_sample = 16;
    
    /* After the header the demuxer starts sending data from an offset
     * of 28 bytes */
    this->size = 28;
    
    /* We need to keep track of the current frame so we now when we've
     * reached the end of the stream */
    this->current_frame = 0;
        
    /* Setup the decoder */
    mpc_decoder_setup(&this->decoder, &this->reader);
    this->decoder_ok = 0;

    /* Take this opportunity to initialize stream/meta information */
    _x_meta_info_set_utf8(this->stream, XINE_META_INFO_AUDIOCODEC, 
                          "Musepack (libmusepack)");
    _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE, 
                       (int) this->streaminfo.average_bitrate);

    return;
  }

  lprintf("data: %u size=%u read=%u\n", buf->size, this->size, this->read);
  
  /* if the audio output is not open yet, open the audio output */
  if (!this->output_open) {
    this->output_open = (this->stream->audio_out->open) (
      this->stream->audio_out,
      this->stream,
      this->bits_per_sample,
      this->sample_rate,
      _x_ao_channels2mode(this->channels));
  }

  /* if the audio still isn't open, do not go any further with the decode */
  if (!this->output_open)
    return;
    
  /* If we run out of space in our internal buffer we discard what's
   * already been read */
  if (((this->size + buf->size) > this->buf_max) && this->read) {
    lprintf("discarding read data\n");
    this->size -= this->read;
    memmove(this->buf, &this->buf[this->read], this->size);
    this->read = 0;
  }
  
  /* If there still isn't space we have to increase the size of the
   * internal buffer */
  if ((this->size + buf->size) > this->buf_max) {
    xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
            "libmusepack: increasing internal buffer size\n");
    this->buf_max += 2*buf->size;
    this->buf = realloc(this->buf, this->buf_max);
  }
  
  /* Copy data */
  xine_fast_memcpy(&this->buf[this->size], buf->content, buf->size);
  this->size += buf->size;
  
  /* Time to decode */
  if (buf->decoder_flags & BUF_FLAG_FRAME_END)  {
    /* Increment frame count */
    if (this->current_frame++ == this->streaminfo.frames) {
      xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
              _("libmusepack: data after last frame ignored\n"));
      return;
    }
  
    if (!this->decoder_ok) {
      /* We require MPC_DECODER_MEMSIZE bytes to initialise the decoder */
      if ((this->size - this->read) >= MPC_DECODER_MEMSIZE) {
        lprintf("initialise");
      
        if (!mpc_decoder_initialize(&this->decoder, &this->streaminfo)) {
          xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
                  _("libmusepack: mpc_decoder_initialise failed\n"));        
          
          _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_HANDLED, 0);
          return;
        }
        
        this->decoder_ok = 1;
      } else {
        /* Not enough data yet */
        return;
      }
    }
    
    /* mpc_decoder_decode may cause a read of MPC_DECODER_MEMSIZE/2 bytes so
     * make sure we have enough data available */
    if ((this->size - this->read) >= MPC_DECODER_MEMSIZE2) {
      lprintf("decoding\n");
    
      if ((err = mpc_decode_frame(this)) < 0) {
        xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
                _("libmusepack: mpc_decoder_decode failed: %d\n"), err);        
          
        _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_HANDLED, 0);
        return;
      }
    }
    
    /* If we are at the end of the stream we decode the remaining frames as we
     * know we'll have enough data */
    if (this->current_frame == this->streaminfo.frames) {
      lprintf("flushing buffers\n");
      
      do {
        if ((err = mpc_decode_frame(this)) < 0) {
          xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
                  _("libmusepack: mpc_decoder_decode failed: %d\n"), err);
        }
      } while (err > 0);
      
      lprintf("buffers flushed\n");
    }
  }
}

static void mpc_reset (audio_decoder_t *this_gen) {
  mpc_decoder_t *this = (mpc_decoder_t *) this_gen;

  this->size = 0;
  this->read = 0;
}

static void mpc_discontinuity (audio_decoder_t *this_gen) {
  /* mpc_decoder_t *this = (mpc_decoder_t *) this_gen; */
}

static void mpc_dispose (audio_decoder_t *this_gen) {

  mpc_decoder_t *this = (mpc_decoder_t *) this_gen;

  /* close the audio output */
  if (this->output_open)
    this->stream->audio_out->close (this->stream->audio_out, this->stream);
  this->output_open = 0;

  /* free anything that was allocated during operation */
  if (this->buf)
    free(this->buf);
  
  free(this);
}

static audio_decoder_t *open_plugin (audio_decoder_class_t *class_gen, xine_stream_t *stream) {

  mpc_decoder_t *this ;

  this = (mpc_decoder_t *) calloc(1, sizeof(mpc_decoder_t));

  /* connect the member functions */
  this->audio_decoder.decode_data         = mpc_decode_data;
  this->audio_decoder.reset               = mpc_reset;
  this->audio_decoder.discontinuity       = mpc_discontinuity;
  this->audio_decoder.dispose             = mpc_dispose;

  /* connect the stream */
  this->stream = stream;

  /* audio output is not open at the start */
  this->output_open = 0;
  
  /* no buffer yet */
  this->buf = NULL;

  /* initialize the basic audio parameters */
  this->channels = 0;
  this->sample_rate = 0;
  this->bits_per_sample = 0;

  /* return the newly-initialized audio decoder */
  return &this->audio_decoder;
}

static char *get_identifier (audio_decoder_class_t *this) {
  return "mpc";
}

static char *get_description (audio_decoder_class_t *this) {
  return "mpc: musepack audio decoder plugin";
}

static void dispose_class (audio_decoder_class_t *this_gen) {

  mpc_class_t *this = (mpc_class_t *)this_gen;

  free (this);
}

static void *init_plugin (xine_t *xine, void *data) {

  mpc_class_t *this ;

  this = (mpc_class_t *) calloc(1, sizeof(mpc_class_t));

  this->decoder_class.open_plugin     = open_plugin;
  this->decoder_class.get_identifier  = get_identifier;
  this->decoder_class.get_description = get_description;
  this->decoder_class.dispose         = dispose_class;

  return this;
}

static uint32_t audio_types[] = { 
  BUF_AUDIO_MPC,
  0
};

static const decoder_info_t dec_info_audio = {
  audio_types,         /* supported types */
  5                    /* priority        */
};

const plugin_info_t xine_plugin_info[] EXPORTED = {
  /* { type, API version, "name", version, special_info, init_function }, */
  { PLUGIN_AUDIO_DECODER, 15, "mpc", XINE_VERSION_CODE, &dec_info_audio, &init_plugin },
  { PLUGIN_NONE, 0, "", 0, NULL, NULL }
};

