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

        AviReadStreamV ( video AVI stream ) functions

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

#include "AviRead.h"
#include "AviReadStreamV.h"
#include <creators.h>
#include <fourcc.h>
#include <iostream>

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

using namespace std;
using namespace Creators;

AviReadStreamV::AviReadStreamV(streamid_t id, IMediaReadStream* stream)
    :AviReadStream(id, stream), videodecoder(0), flip(0)
{
}

AviReadStreamV::~AviReadStreamV()
{
    StopStreaming();
}

framepos_t AviReadStreamV::SeekToKeyFrame(framepos_t pos)
{
    Debug cout << "AviReadStreamV::SeekToKeyFrame() " << pos << endl;

    framepos_t n = AviReadStream::SeekToKeyFrame(pos);
    if (!m_pIStream->IsKeyFrame(n))
    {
 	n = m_pIStream->SeekToPrevKeyFrame();
	//cout << "Reseek different " << n << endl;
    }

    //cout << "AviReadStreamV::SeekToKeyFrame() flushing " << c << "   " << n << endl;
    if (videodecoder)
	videodecoder->FlushCache();

    return n;
}

double AviReadStreamV::SeekTimeToKeyFrame(double timepos)
{
    Debug cout << "AviReadStreamV::SeekTimeToKeyFrame() " << timepos << endl;

    AviReadStream::SeekTimeToKeyFrame(timepos);
    if (!m_pIStream->IsKeyFrame())
	SeekToPrevKeyFrame();

    if (videodecoder)
	videodecoder->FlushCache();

    return m_pIStream->GetSampleTime();
}

framepos_t AviReadStreamV::GetPos() const
{
    if (videodecoder)
    {
	framepos_t pos = videodecoder->GetPos();
	//cout << "AviReadStreamV::GetPos() " << pos << endl;
	if (pos != m_pIStream->ERR)
	    return pos;
    }
    //cout << "AviReadStreamV::StreamGetPos() " << m_pIStream->GetPos() << endl;
    return m_pIStream->GetPos();
}

double AviReadStreamV::GetTime(framepos_t frame) const
{
    if (frame == ERR && videodecoder)
    {
	double t = videodecoder->GetTime();
	//cout << "GetTIME ********** " << t << "      " << m_pIStream->GetSampleTime() << endl;
	if (t >= 0.0)
	    return t;
    }

    return m_pIStream->GetSampleTime(frame);
}

bool AviReadStreamV::DropFrame() const
{
    return (videodecoder) ? videodecoder->DropFrame() : false;
}

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

    These functions are meaningless for 'unknown type' stream

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

HRESULT AviReadStreamV::GetVideoFormatInfo(void* bi, size_t size) const
{
    if (bi == 0)
	return E_ERROR;
    if (size > m_format_size)
        size = m_format_size;
    memcpy(bi, m_format, size);

    return 0;
}

/*
HRESULT AviReadStreamV::SkipFrame()
{
    int size;
    HRESULT hr=m_pIStream->Read(1, 0, 0, &size, 0);
    if(hr!=0)
	return -1;
    if(size<0)
	return -1;
    char* temp_buffer=new char[size];
    hr=m_pIStream->Read(1, temp_buffer, size, 0, 0);
    delete temp_buffer;
    return hr;
}
HRESULT AviReadStreamV::SkipTo(double pos)
{
    if(pos>m_pIStream->LengthTime())
	return -1;
    while(pos<m_pIStream->GetSampleTime())
	if(SkipFrame())
	    return -1;
    return 0;
}
*/

CImage* AviReadStreamV::GetFrame(bool readframe)
{
    if (videodecoder)
    {
	// we decode frame here in direct mode
        // it's not being decode in separe thread
	if (readframe)
	{
	    // check if videodecoder has free buffer
            // (as it may have couple of frames precached
	    if (videodecoder->GetFreeBuffers() > 0)
		if (ReadFrame() < 0)
		    return 0;
	}

	return videodecoder->GetFrame();
    }
    return 0;
}

size_t AviReadStreamV::GetFrameSize() const
{
    return (videodecoder) ? videodecoder->DestFmt().biSizeImage : 0;
}

HRESULT AviReadStreamV::GetOutputFormat(void* format, size_t size) const
{
    if ((size < sizeof(BITMAPINFOHEADER)) || !format || !videodecoder)
	return -1;

    *(BITMAPINFOHEADER*)format = videodecoder->DestFmt();

    return 0;
}

bool AviReadStreamV::IsStreaming() const
{
    return (videodecoder != NULL);
}

HRESULT AviReadStreamV::SetOutputFormat()
{
    if (!videodecoder)
	return -1;
    // FIXME
    cerr << "AviReadStreamV::SetOutputFormat: not implemented!" << endl;

    return -1; //not implemented
}

HRESULT AviReadStreamV::ReadFrame(bool render)
{
    if (!videodecoder)
	return -1;

    if (GetType() != Video || m_iEof)
    {
        m_iEof++;
	return -1;
    }

    size_t size;

    HRESULT hr = m_pIStream->Read(1, 0, 0, &size, 0);

    if (hr != 0)
    {
	// regular Eof usually - we should check if we have reached last pos
	if (m_pIStream->GetPos() < (GetLength() - 1))
	    cout << "AviReadStreamV::ReadFrame  WARNING: Read() failed "
		"(stream pos = "  << m_pIStream->GetPos() << ", stream "
		"length = " << GetLength() << " )" << endl;
        else
	    m_iEof++;
	return -1;
    }

    //cout << "*************** READFRAME1 " << m_pIStream->GetPos() << "   " << size << endl;
    if (size > 0x7fffffff)
    {
	cout << "AviReadStreamV::ReadFrame  WARNING: size=" << size << endl;
	return -1;
    }

    uint8_t* temp_buffer = new uint8_t[size + 8];

    size_t lBytes, lSamples;
    hr = m_pIStream->Read(1, temp_buffer, size, &lBytes, &lSamples);

    //cout << " READVRAME " << hr << " lsample " << lSamples << " lbytes " << lBytes << endl;
    if (hr == 0 && lSamples == 1)
    {
	int flags;
	GetFrameFlags(&flags);
	hr = videodecoder->DecodeFrame(temp_buffer, lBytes, m_pIStream->GetPos(),
				       m_pIStream->GetSampleTime(), flags, render);
    }

    delete[] temp_buffer;

    return hr;
}

HRESULT AviReadStreamV::Seek(framepos_t pos)
{
    Debug cout << "AviReadStreamV::Seek() " << pos << endl;
    HRESULT hr = AviReadStream::Seek(pos);
    m_iEof = 0;

    if (videodecoder)
	videodecoder->FlushCache();

    return hr;
}

HRESULT AviReadStreamV::SeekTime(double timepos)
{
    Debug cout << "AviReadStreamV::SeekTimeTo() " << timepos << endl;
    HRESULT hr = AviReadStream::SeekTime(timepos);
    m_iEof = 0;

    if (videodecoder)
	videodecoder->FlushCache();

    return hr;
}

HRESULT AviReadStreamV::StartStreaming()
{
    if (videodecoder)
    {
	cerr << "AviReadStreamV already streaming!" << endl;
	return 0;
    }

    BITMAPINFOHEADER* bh = (BITMAPINFOHEADER*) m_format;
    videodecoder = CreateVideoDecoder(*bh, 24, flip);

    if (!videodecoder)
	return -1;

    videodecoder->Start();

    return 0;
}

HRESULT AviReadStreamV::StopStreaming()
{
    if (videodecoder)
    {
	//cout << "AviReadStreamV::StopStreaming() call stop" << endl;
	videodecoder->Stop();
	FreeVideoDecoder(videodecoder);
	videodecoder = 0;
    }

    return 0;
}
