//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2013 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: TAK file/header handling

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT stdout

#include "mpxplay.h"

#ifdef MPXPLAY_LINK_INFILE_TAK

#include "in_rawau.h"
#include "tagging.h"
#include "decoders/ffmpegac/tak.h"

typedef struct tak_parser_data_s{
 int  mlast_frame;
 mpxp_int64_t data_begin, data_end, data_len;
 mpxp_int64_t samplenum;

 long seek_tabsize;
 long *seek_table;
 long seek_lastvalidframe;

}tak_parser_data_s;

void intak_parse_streaminfo(struct mpxplay_infile_info_s *miis, unsigned char *data, unsigned int size)
{
 struct mpxplay_audio_decoder_info_s *adi = miis->audio_decoder_infos;
 struct tak_parser_data_s *taki = miis->private_data;
 struct mpxplay_bitstreambuf_s bs;
 uint64_t channel_mask = 0;
 int i;

 mpxplay_bitstream_init(&bs, data, size);
 mpxplay_bitstream_skipbits(&bs, TAK_ENCODER_CODEC_BITS); // 6
 mpxplay_bitstream_skipbits(&bs, TAK_ENCODER_PROFILE_BITS); // 4
 mpxplay_bitstream_skipbits(&bs, TAK_SIZE_FRAME_DURATION_BITS); // 4
 taki->samplenum = mpxplay_bitstream_getbits_le64(&bs, TAK_SIZE_SAMPLES_NUM_BITS); // 35

 mpxplay_bitstream_skipbits(&bs, TAK_FORMAT_DATA_TYPE_BITS ); // 3

 adi->freq = mpxplay_bitstream_getbits_le24(&bs, TAK_FORMAT_SAMPLE_RATE_BITS) + TAK_SAMPLE_RATE_MIN; // 18
 adi->bits = mpxplay_bitstream_getbits_le24(&bs, TAK_FORMAT_BPS_BITS) + TAK_BPS_MIN; // 5
 adi->filechannels = mpxplay_bitstream_getbits_le24(&bs, TAK_FORMAT_CHANNEL_BITS) + TAK_CHANNELS_MIN; // 4

 if(mpxplay_bitstream_getbit1_le(&bs)) {
  mpxplay_bitstream_skipbits(&bs, TAK_FORMAT_VALID_BITS);
  if(mpxplay_bitstream_getbit1_le(&bs)) {
   for (i = 0; i < adi->filechannels; i++) {
    int value = mpxplay_bitstream_getbits_le24(&bs, TAK_FORMAT_CH_LAYOUT_BITS);
    //if (value < FF_ARRAY_ELEMS(tak_channel_layouts))
    // channel_mask |= tak_channel_layouts[value];
   }
  }
 }

 //s->ch_layout     = channel_mask;
}

static int INTAK_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis,mpxp_uint32_t openmode)
{
 struct tak_parser_data_s *taki;
 //struct mpxplay_audio_decoder_info_s *adi = miis->audio_decoder_infos;
 struct mpxplay_streampacket_info_s *spi=miis->audio_stream;
 int retcode, finished = 0;
 struct mpxplay_bitstreambuf_s bs;
 unsigned char readbuf[128];

 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tak open begin");

 retcode = INRAWAU_infile_open(fbfs, fbds, filename, miis);
 if(retcode != MPXPLAY_ERROR_INFILE_OK)
  return retcode;

 if(fbfs->get_le32(fbds) != PDS_GET4C_LE32('t', 'B', 'a', 'K'))
  return MPXPLAY_ERROR_INFILE_CANTOPEN;

 taki = pds_calloc(1,sizeof(*taki));
 if(!taki)
  return MPXPLAY_ERROR_INFILE_MEMORY;
 miis->private_data = taki;

 while(!fbfs->eof(fbds)){
  enum TAKMetaDataType type;
  int size;
  mpxp_filesize_t filepos;

  type = fbfs->get_byte(fbds) & 0x7f;
  size = fbfs->get_le24(fbds);

  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tak meta:%d len:%d",(int)type,size);

  filepos = fbfs->ftell(fbds) + size;

  switch(type){
   case TAK_METADATA_STREAMINFO:
    if(size > sizeof(readbuf))
     return MPXPLAY_ERROR_INFILE_CANTOPEN;
    if(fbfs->fread(fbds, readbuf, size) != size)
     return MPXPLAY_ERROR_INFILE_EOF;
    intak_parse_streaminfo(miis, readbuf, size);
    break;
   case TAK_METADATA_LAST_FRAME:
    if(size != 11)
     return MPXPLAY_ERROR_INFILE_CANTOPEN;
    if(fbfs->fread(fbds, readbuf, size) != size)
     return MPXPLAY_ERROR_INFILE_EOF;
    mpxplay_bitstream_init(&bs, readbuf, size);
    taki->mlast_frame = 1;
    taki->data_end = mpxplay_bitstream_getbits_be64(&bs, TAK_LAST_FRAME_POS_BITS) +
                     mpxplay_bitstream_getbits_be24(&bs, TAK_LAST_FRAME_SIZE_BITS);
    break;
   case TAK_METADATA_END:
    taki->data_begin = filepos;
    taki->data_end += fbfs->ftell(fbds);
    finished = 1;
    break;
  }
  if(fbfs->fseek(fbds, filepos, SEEK_SET) != filepos)
   return MPXPLAY_ERROR_INFILE_EOF;
  if(finished)
   break;
 }

 taki->data_len = taki->data_end - taki->data_begin;
 if(taki->data_len <= 0)
  return MPXPLAY_ERROR_INFILE_CANTOPEN;

 miis->audio_stream->wave_id = MPXPLAY_WAVEID_TAK;
 miis->longname = "  TAK   ";
 funcbit_disable(spi->flags,MPXPLAY_SPI_FLAG_NEED_PARSING);

 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tak open end freq:%d chan:%d bits:%d sn:%d",
  adi->freq, adi->filechannels, adi->bits, (long)taki->samplenum);

 fbfs->fread(fbds,readbuf,4);

 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tak open end begin:%d end:%d len:%d %8.8X",
  (long)taki->data_begin,(long)taki->data_end,(long)taki->data_len,*((unsigned long *)&readbuf[0]));

 return MPXPLAY_ERROR_INFILE_OK;
}

void INTAK_infile_close(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct tak_parser_data_s *taki = miis->private_data;
 if(taki){
  pds_free(taki);
 }
 fbfs->fclose(fbds);
}

long INTAK_fseek(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,long newframenum)
{
 struct tak_parser_data_s *taki = miis->private_data;
 mpxp_filesize_t newfilepos;
 if(!taki)
  return MPXPLAY_ERROR_INFILE_MEMORY;
 newfilepos = (mpxp_filesize_t)((float)newframenum*(float)taki->data_len/(float)miis->allframes);
 newfilepos += taki->data_begin;
 if(fbfs->fseek(fbds,newfilepos,SEEK_SET)<0)
  return MPXPLAY_ERROR_INFILE_EOF;
 return newframenum;
}

struct mpxplay_infile_func_s IN_TAK_funcs={
 (MPXPLAY_TAGTYPE_PUT_SUPPORT(MPXPLAY_TAGTYPE_ID3V1|MPXPLAY_TAGTYPE_APETAG)
 |MPXPLAY_TAGTYPE_PUT_PRIMARY(MPXPLAY_TAGTYPE_APETAG)),
 NULL,
 NULL,
 &INTAK_infile_open,
 &INTAK_infile_close,
 &INRAWAU_infile_decode,
 &INTAK_fseek,
 NULL,
 NULL,
 NULL,
 NULL,
 {"TAK",NULL}
};

#endif // MPXPLAY_LINK_INFILE_TAK
