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

	AviReadStreamA ( audio AVI stream ) Functions

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

#include "AviRead.h"
#include "AviReadStreamA.h"
#include <avifmt.h>
#include <creators.h>
#include <cpuinfo.h>

#include <iostream>
#include <cstdio>

#define Debug if(0)
#define __MODULE__ "AviReadStreamV"

using namespace Creators;
using namespace std;

/*
double AviReadStreamA::Seek(double time)
{
    cout<<"AviReadStreamA::SeekToTime"<<endl;
    if(time<0)
	return -1;
    if(m_pIStream->SeekTime(time)!=0)
        return -1;
    return time;
}
*/
const unsigned int wfmtx_size = 18;
//sizeof(...) depends on packing
// in this case we should use some attributes
// if this depends on packing we could not read it anyway

AviReadStreamA::AviReadStreamA(Unsigned id, IMediaReadStream* stream)
    :AviReadStream(id, stream), audiodecoder(0)
{
}

HRESULT AviReadStreamA::GetAudioFormatInfo(void* bi, char** ext) const
{
    if (bi == 0)
        return E_ERROR;
    memcpy(bi, m_format, wfmtx_size);

    if (ext == 0)
        return 0;

    unsigned cbSize = ((LPWAVEFORMATEX) bi)->cbSize;

    if (cbSize == 0)
    {
	*ext = 0;
	return 0;
    }

    if(m_format_size < wfmtx_size)
    {
	// cout<<"WARNING: GetAudioFormatInfo: bad audio structure"<<endl;
	*ext = 0;
	return E_ERROR;
    }
    if (m_format_size < (wfmtx_size + cbSize))
    {
	((LPWAVEFORMATEX)bi)->cbSize = cbSize = wfmtx_size - m_format_size;
	// cout<<"WARNING: GetAudioFormatInfo: bad audio structure"<<endl;
	// *ext=0;
	// return E_ERROR;
    }
    *ext = new char[cbSize + 18];
    memcpy(*ext, m_format, cbSize + 18);
    return 0;
}

size_t AviReadStreamA::GetFrameSize() const
{
    return audiodecoder->GetMinSize();
}

framepos_t AviReadStreamA::GetPos() const
{
    unsigned int dwSampleSize = m_pIStream->GetSampleSize();
    Debug cout<<"AviReadStreamA::GetPos() Sample "<<m_pIStream->GetPos()<<", rem_size"<<rem_size<<", sample_size "<<dwSampleSize<<endl;

    if (dwSampleSize == 0)
	return m_pIStream->GetPos();

    if (unsigned(rem_size/dwSampleSize) > m_pIStream->GetPos())
	return 0;

    return m_pIStream->GetPos() - (framepos_t) (rem_size/dwSampleSize);
}

double AviReadStreamA::GetTime() const
{
    int dwBps = ((WAVEFORMATEX*)m_format)->nAvgBytesPerSec;
    double smtime = m_pIStream->GetSampleTime();

    if (dwBps != 0)
    {
	smtime -= rem_size/(double)dwBps;

	if (smtime < 0.0)
	    smtime = 0.0;
    }
    Debug cout << "AviReadStreamA::GetTime() " << smtime << ", rem_size:" << rem_size << ", avg bps " << dwBps <<endl;

    return smtime;
}

HRESULT AviReadStreamA::GetOutputFormat(void* format, size_t size) const
{
    if (!audiodecoder || !format || size < 18)
	return -1;

    return audiodecoder->GetOutputFormat((WAVEFORMATEX*)format);
}

bool AviReadStreamA::IsStreaming() const
{
    return (audiodecoder != NULL);
}

// few CPU cycles are wasted here
// FIXME later
HRESULT AviReadStreamA::ReadFrames(void* buffer, size_t bufsize, size_t samples,
				   size_t& samples_read, size_t& bytes_read)
{
    if (!audiodecoder || !samples)
	return -1;

    size_t rem_diff = 0;
    size_t lBytes, lSamples;
    samples_read = 0;
    bytes_read = 0;
    HRESULT hr = -1;

    char* temp_buffer = new char[bufsize * 2];

    if (rem_buffer)
    {
	rem_diff = min(rem_size, bufsize);
	memcpy(temp_buffer, rem_buffer, rem_diff);
	if (rem_diff < rem_size)
	    memcpy(rem_buffer, rem_buffer + rem_diff, rem_size - rem_diff);
    }

    lBytes = 0;
    if (bufsize > rem_diff)
    {
	int srcsize = audiodecoder->GetSrcSize(bufsize - rem_diff);
	//cout << "bufsize: " << bufsize << "  remdiff " << rem_diff << "  ss " << srcsize << endl;
	//m_pIStream->Read(1, 0, 0, &lBytes, &lSamples);
	hr = m_pIStream->Read(samples, temp_buffer + rem_diff, srcsize,
			      &lBytes, &lSamples);
	Debug printf("AviReadStreamA: Read %d samples ( %d bytes ) from pos %d\n",
		     lSamples, lBytes, m_pIStream->GetPos());
	//printf("AviReadStreamA: Read %d samples ( %d bytes ) from pos %d\n",
	//       lSamples, lBytes, m_pIStream->GetPos());
        samples_read += lSamples;
    }

    lBytes += rem_diff;
    size_t local_read, ocnt;
    if (lBytes)
    {
	//cout << "lBytes " << lBytes << "   bs " << bufsize << endl;
	hr = audiodecoder->Convert(temp_buffer, lBytes, buffer, bufsize, &local_read, &ocnt);
	//cout << "lBytes " << lBytes << "   locread " << local_read << endl;
	if (local_read > lBytes)
	    local_read = lBytes;
    }
    else
    {
        local_read = 0;
        ocnt = 0;
	m_iEof++;
    }

    if (!rem_buffer && (lBytes > local_read))
    {
	rem_limit = max(2 * audiodecoder->GetMinSize(), 50000U);
	rem_buffer = new char[rem_limit];
    }
    rem_size = min(lBytes-local_read, (size_t)rem_limit);
    Debug printf("AviReadStreamA::ReadFrames: converted %d bytes of %d to %d, bufsize %d\n",
		 (int)local_read, (int)lBytes, (int)ocnt, (int)bufsize);
    if (!local_read && !ocnt)
    {
	//shouldn't happen. Just skip some data, hoping that things will be OK later.
	local_read = lBytes;
	ocnt = 0;
	rem_size = 0;
    }
    else
    {
	//cout << "REMSIZE " << rem_size << endl;
	memcpy(rem_buffer, temp_buffer + local_read, rem_size);
	bytes_read = ocnt;
    }
    delete[] temp_buffer;
    Debug cout << "AviReadStreamA: new m_sample is " << m_pIStream->GetPos() << endl;
    return hr;
}

HRESULT AviReadStreamA::StartStreaming()
{
    if (audiodecoder)
	return -1;

    audiodecoder = CreateAudioDecoder((WAVEFORMATEX*)m_format);
    if (!audiodecoder)
    {
	printf("Failed to initialize audio decoder for format 0x%x\n",
	       ((WAVEFORMATEX*)m_format)->wFormatTag);
        return -1;
    }
    rem_size = 0;
    return 0;
}

HRESULT AviReadStreamA::StopStreaming()
{
    if (audiodecoder)
    {
	FreeAudioDecoder(audiodecoder);
	audiodecoder = 0;

	/*
         // what has been the reason for this code - kabi@i.am ????
	int dwSampleSize = m_pIStream->SampleSize();
	if (dwSampleSize)
	    if(unsigned(rem_size/dwSampleSize)<m_pIStream->Pos())
		m_pIStream->Seek(m_pIStream->Pos()-rem_size/dwSampleSize);
	rem_size=0;
	*/
    }
    return 0;
}
