/********************************************************

         Windows ACM-based audio decoder
	 Copyright 2000 Eugene Kuznetsov  (divx@euro.ru)

*********************************************************/

#include "acm.h"

#include <except.h>
#include <cstdio>

//#define TIMING
#define __MODULE__ "ACM decoder"
//#define TIMING
using namespace std;
/*char cfmt[]={0x61, 0x01, 0x02, 0x00, 0x22, 0x56, 0x00, 0x00,
0xa5, 0x0f, 0x00, 0x00, 0xba, 0x00, 0x10, 0x00,
0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00,
0xba, 0x00, 0x00, 0x00};*/
ACM_Decoder::ACM_Decoder(const CodecInfo& info, WAVEFORMATEX* pwf)
    :IAudioDecoder(info, pwf)
{
    memcpy(ext_info, pwf, min(64, 18 + in_fmt.cbSize));
    if (in_fmt.nSamplesPerSec == 0)
	throw FATAL("Bad argument");

//    MSACM_RegisterAllDrivers();

    wf.nChannels=in_fmt.nChannels;
    wf.nSamplesPerSec=in_fmt.nSamplesPerSec;
    wf.nAvgBytesPerSec=2*wf.nSamplesPerSec*wf.nChannels;
    wf.wFormatTag=WAVE_FORMAT_PCM;
    wf.nBlockAlign=2*in_fmt.nChannels;
    wf.wBitsPerSample=16;
    wf.cbSize=0;
#if 0
    cout << "IN: nch " << in_fmt.nChannels << endl
	<< "nSamplesPerSec " << in_fmt.nSamplesPerSec << endl
	<< "nAvgBytesPerSec " << in_fmt.nAvgBytesPerSec << endl
        << "nBlockAlign " << in_fmt.nBlockAlign << endl
        << "wFormat " << in_fmt.wFormatTag << endl
        << "cbSize " << in_fmt.cbSize << endl;

    cout << "OUT: nch " << wf.nChannels << endl
	<< "nSamplesPerSec " << wf.nSamplesPerSec << endl
	<< "nAvgBytesPerSec " << wf.nAvgBytesPerSec << endl
        << "nBlockAlign " << wf.nBlockAlign << endl
        << "wFormat " << wf.wFormatTag << endl
        << "cbSize " << wf.cbSize << endl;
#endif
   HRESULT hr = acmStreamOpen(&srcstream, (HACMDRIVER)NULL,
			      (WAVEFORMATEX*)ext_info,
			      (WAVEFORMATEX*)&wf,
			      NULL, 0, 0, 0);
    //ACM_STREAMOPENF_NONREALTIME );

    if (hr != S_OK)
    {
    	if (hr == ACMERR_NOTPOSSIBLE)
	    throw FATAL("Unappropriate audio format");

	char s[256];
	sprintf(s, "ACM_Decoder: acmStreamOpen error %d", (int) hr);
	throw FATAL(s);
    }
}

ACM_Decoder::~ACM_Decoder()
{
    acmStreamClose(srcstream, 0);
}

int ACM_Decoder::Convert(const void* in_data, size_t in_size,
			 void* out_data, size_t out_size,
			 size_t* size_read, size_t* size_written)
{
    HRESULT hr = -1;

    if (!in_data || !out_data)
	goto fail;

    DWORD srcsize;
//    printf("ACM_Decoder: data starts with %X %X %X\n",
//        *(int*)in_data, *(int*)(in_data+4),  *(int*)(in_data+8));

#ifdef TIMING
    printf("ACM_Decoder: received request to convert %d bytes to %d bytes\n",
	    in_size, out_size);
#endif
//    acmStreamSize(srcstream, in_size, &srcsize, ACM_STREAMSIZEF_SOURCE);
//    printf("%d\n", srcsize);
    acmStreamSize(srcstream, out_size, &srcsize, ACM_STREAMSIZEF_DESTINATION);
    if (srcsize > in_size)
	srcsize = in_size;
//    printf("%d\n", srcsize);
    ACMSTREAMHEADER ash;
    memset(&ash, 0, sizeof(ash));
    ash.cbStruct = sizeof(ash);
    ash.fdwStatus = 0;
    ash.dwUser = 0;
    ash.pbSrc = (uint8_t*)in_data;
    ash.cbSrcLength = srcsize;
    ash.pbDst = (uint8_t*)out_data;
    ash.cbDstLength = out_size;

#ifdef TIMING
    printf("ACM_Decoder: src: %X %X %X\n", ash.cbStruct, ash.cbSrcLength, ash.cbDstLength);
#endif
    hr = acmStreamPrepareHeader(srcstream, &ash, 0);
    if (hr != S_OK)
    {
	//cerr<<"ACM_Decoder: acmStreamPrepareHeader error "<<hr<<endl;
    	goto fail;
    }


    hr = acmStreamConvert(srcstream, &ash, 0);
    if (hr != S_OK)
    {
	//cerr<<"ACM_Decoder: acmStreamConvert error "<<hr<<endl;
	goto fail;
    }
/*    if(qw==0)
    {
	FILE* qz=fopen("./test.qqq", "wb");
	fwrite(out_data, 1, out_size, qz);
	fclose(qz);
	qw=1;
    }		*/

//    printf("Uncompressed %d bytes from %d\n", ash.cbDstLengthUsed, ash.cbSrcLengthUsed);
    if (ash.cbSrcLengthUsed > in_size)
	ash.cbSrcLengthUsed = in_size;

    if (size_read)
	*size_read = ash.cbSrcLengthUsed;
    if (size_written)
	*size_written = ash.cbDstLengthUsed;

    hr=acmStreamUnprepareHeader(srcstream, &ash, 0);
    return 0;
fail:
    if (size_read)
	*size_read = in_size;
    if (size_written)
	*size_written = 0;

    return -1;
}

size_t ACM_Decoder::GetMinSize()
{
    long in_size=in_fmt.nBlockAlign;
    DWORD srcsize = 0U;

    acmStreamSize(srcstream, in_size, &srcsize, ACM_STREAMSIZEF_SOURCE);
//    if(!zz){zz=1;
//    cerr.setf(ios::dec, ios::basefield);
//    cerr<<"Audio data block align "<<in_size<<" ; minimal buffer size "<<srcsize<<endl;
//    }
    return 2*srcsize;
}

int ACM_Decoder::GetSrcSize(int dest_size)
{
    long in_size = in_fmt.nBlockAlign;
    unsigned long srcsize = 0;
    acmStreamSize(srcstream, dest_size, &srcsize, ACM_STREAMSIZEF_DESTINATION);

    return srcsize;
}
