/*
 * iaxclient: a cross-platform IAX softphone library
 *
 * Copyrights:
 * Copyright (C) 2003-2006, Horizon Wimba, Inc.
 * Copyright (C) 2007, Wimba, Inc.
 *
 * Contributors:
 * Steve Kann <stevek@stevek.com>
 *
 * This program is free software, distributed under the terms of
 * the GNU Lesser (Library) General Public License.
 */

#include "codec_gsm.h"
#include "iaxclient_lib.h"
#include "gsm.h"

struct state {
    gsm gsmstate;
    plc_state_t plc;
};


static void destroy ( struct iaxc_audio_codec *c) {

    struct state * encstate = (struct state *) c->encstate;
    struct state * decstate = (struct state *) c->decstate;

    gsm_destroy(encstate->gsmstate);
    gsm_destroy(decstate->gsmstate);
    free(c->encstate);
    free(c->decstate);
    free(c);
}


static int decode ( struct iaxc_audio_codec *c,
    int *inlen, unsigned char *in, int *outlen, short *out ) {
    struct state * decstate = (struct state *) c->decstate;

    /* use generic interpolation */
    if(*inlen == 0) {
        int interp_len = 160;
        if(*outlen < interp_len) interp_len = *outlen;
        plc_fillin(&decstate->plc,out,interp_len);
        *outlen -= interp_len;
        return 0;
    }

    /* need to decode minimum of 33 bytes to 160 byte output */
    while( (*inlen >= 33) && (*outlen >= 160) ) {
	if(gsm_decode(decstate->gsmstate, in, out))
	{
	    fprintf(stderr, "codec_gsm: gsm_decode returned error\n");
	    return -1;
	}

	/* push decoded data to interpolation buffer */
	plc_rx(&decstate->plc,out,160);

	/* we used 33 bytes of input, and 160 bytes of output */
	*inlen -= 33;
	in += 33;
	*outlen -= 160;
	out += 160;
    }

    return 0;
}

static int encode ( struct iaxc_audio_codec *c,
    int *inlen, short *in, int *outlen, unsigned char *out ) {

    struct state * encstate = (struct state *) c->encstate;


    /* need to encode minimum of 160 bytes to 33 byte output */
    while( (*inlen >= 160) && (*outlen >= 33) ) {
	gsm_encode(encstate->gsmstate, in, out);

	/* we used 160 bytes of input, and 33 bytes of output */
	*inlen -= 160;
	in += 160;
	*outlen -= 33;
	out += 33;
    }

    return 0;
}

struct iaxc_audio_codec *codec_audio_gsm_new() {

  struct state * encstate;
  struct state * decstate;
  struct iaxc_audio_codec *c = (struct iaxc_audio_codec *)calloc(sizeof(struct iaxc_audio_codec),1);


  if(!c) return c;

  strcpy(c->name,"gsm 06.10");
  c->format = IAXC_FORMAT_GSM;
  c->encode = encode;
  c->decode = decode;
  c->destroy = destroy;

  c->minimum_frame_size = 160;

  c->encstate = calloc(sizeof(struct state),1);
  c->decstate = calloc(sizeof(struct state),1);

  /* leaks a bit on no-memory */
  if(!(c->encstate && c->decstate))
      return NULL;

  encstate = (struct state *) c->encstate;
  decstate = (struct state *) c->decstate;

  encstate->gsmstate = gsm_create();
  decstate->gsmstate = gsm_create();

  if(!(encstate->gsmstate && decstate->gsmstate))
      return NULL;

  return c;
}

