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

	Video encoder implementation
	Copyright 2000 Eugene Kuznetsov (divx@euro.ru)

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

#include <default.h>
#include <fourcc.h>
#include <image.h>
#include <registry.h>
//#include <dirent.h>
#include <dlfcn.h>

//#define TIMING
#include <except.h>
//#undef TIMING
#ifdef TIMING
#include <cpuinfo.h>
#endif

#include "VideoEncoder.h"
#include "VideoCodec.h"
struct ICINFO
{
	long	dwSize;		/* 00: */
	long	fccType;	/* 04:compressor type     'vidc' 'audc' */
	long	fccHandler;	/* 08:compressor sub-type 'rle ' 'jpeg' 'pcm '*/
	long	dwFlags;	/* 0c:flags LOshort is type specific */
	long	dwVersion;	/* 10:version of the driver */
	long	dwVersionICM;	/* 14:version of the ICM used */
	/*
	 * under Win32, the driver always returns UNICODE strings.
	 */
	short	szName[16];		/* 18:short name */
	short	szDescription[128];	/* 38:long name */
	short	szDriver[128];		/* 138:driver that contains compressor*/
					/* 238: */
};
struct ICCOMPRESSFRAMES
{
    long               dwFlags;
    LPBITMAPINFOHEADER  lpbiOutput;
    void*              lOutput;
    LPBITMAPINFOHEADER  lpbiInput;
    void*              lInput;
    long                lStartFrame;
    long                lFrameCount;
    long                lQuality;
    long                lDataRate;
    long                lKeyRate;
    long               dwRate;
    long               dwScale;
    long               dwOverheadPerFrame;
    long               dwReserved2;
    long (*GetData)(void* lInput,long lFrame,void* lpBits,long len);
    long (*PutData)(void* lOutput,long lFrame,void* lpBits,long len);
};


#include <time.h>

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <strstream>


#define HKEY_CLASSES_ROOT       (0x80000000)
#define HKEY_CURRENT_USER       (0x80000001)
#define HKEY_LOCAL_MACHINE      (0x80000002)
#define HKEY_USERS              (0x80000003)
#define HKEY_PERFORMANCE_DATA   (0x80000004)
#define HKEY_CURRENT_CONFIG     (0x80000005)
#define HKEY_DYN_DATA           (0x80000006)
#define REG_DWORD		4	/* DWORD in little endian format */

#define ICCOMPRESS_KEYFRAME     0x00000001L

#define __MODULE__ "VideoEncoder"

using namespace std;

VideoEncoder::VideoEncoder(const CodecInfo& info, fourcc_t compressor, const BITMAPINFOHEADER& format)
    :IVideoEncoder(info), codec(0), m_iState(0), m_bh(0), m_obh(0), m_prev(0)
{
    try
    {
	unsigned bihs = (format.biSize < (int) sizeof(BITMAPINFOHEADER)) ?
	    sizeof(BITMAPINFOHEADER) : format.biSize;
	m_bh = (BitmapInfo*) new char[bihs];
	memcpy(m_bh, &format, bihs);

	switch (compressor)
	{
	    case fccMP41:
	    case fccMP43:
		compressor = fccDIV3;
	        break;
	}
	codec = control.Create(compressor, info, Module::Compress);
	m_comp_id = compressor;
	int h = codec->GetFormat(m_bh, 0);
	if (h < 0)
	    throw FATAL("Can't handle this format");
	m_obh = (BITMAPINFOHEADER*)new char[h];
	memset(m_obh, 0, h);
	m_obh->biSize=h;
	h = codec->GetFormat(m_bh, m_obh);
	m_obh->biHeight=abs(m_obh->biHeight);
	m_obh->biBitCount=m_bh->biBitCount;
        if (h != 0)
	    throw FATAL("Can't handle this format");
//	m_obh->biHeight*=-1;
//        ICGetDefaultQuality(hic, &m_iQual);
	codec->GetDefaultQuality(&m_iQual);
	m_iState=1;
//        if(ICGetDefaultKeyFrameRate(hic, &m_iKfFreq)!=0)
	if (codec->GetDefaultKeyFrameRate(&m_iKfFreq) != 0)
 	    m_iKfFreq = 0xFFFF;
	if (m_bh->biSizeImage == 0)
	    m_bh->biSizeImage = abs(m_bh->biWidth*m_bh->biHeight*((m_bh->biBitCount+7)/8));
// Make sure that we can work with this format!
	ICINFO ici;
	codec->GetInfo(&ici);

	ICCOMPRESSFRAMES icf;

	memset(&icf, 0, sizeof icf);

	icf.dwFlags	= (long)&icf.lKeyRate;
        icf.lStartFrame = 0;
	icf.lFrameCount = 9999;
        icf.lQuality	= m_iQual;
	icf.lDataRate	= 910000;
	icf.lKeyRate	= (m_iKfFreq>=0)?m_iKfFreq:0;
	icf.dwRate	= 1000000;
        icf.dwScale	= 40000;

	codec->CompressFramesInfo(&icf);
	HRESULT hr=codec->CompressBegin(m_bh, m_obh);
	if(hr!=0)
	    throw FATAL("CompressBegin failed ( probably unsupported input format )");
	codec->CompressEnd();
    }
    catch(...)
    {
	delete codec;
	delete m_bh;
	delete m_obh;
	delete m_prev;
//	if(hic)ICClose(hic);
	throw;
    }
}

VideoEncoder::~VideoEncoder()
{
    if (m_iState != 1)
	Stop();
    delete codec;
    delete m_bh;
    delete m_obh;
    delete m_prev;
}

void VideoEncoder::Start()
{
    //cout << "STARTCOMPRESSION " << m_iState << endl;
    if (m_iState != 1)
	return;//wrong state
//    HRESULT hr=ICCompressBegin(hic, m_bh, m_obh);
    ICINFO ici;

    codec->GetInfo(&ici);

    ICCOMPRESSFRAMES icf;

    memset(&icf, 0, sizeof icf);

    icf.dwFlags		= (long)&icf.lKeyRate;
    icf.lStartFrame = 0;
    icf.lFrameCount = 9999;
    icf.lQuality	= m_iQual;
    icf.lDataRate	= 910000;
    icf.lKeyRate	= (m_iKfFreq>=0)?m_iKfFreq:0;
    icf.dwRate		= 1000000;
    icf.dwScale		= 40000;

    codec->CompressFramesInfo(&icf);

    HRESULT hr=codec->CompressBegin(m_bh, m_obh);
/*	printf("Starting compression \n");
	printf("  biSize %d\n", m_bh->biSize);
	printf("  biWidth %d\n", m_bh->biWidth);
	printf("  biHeight %d\n", m_bh->biHeight);
	printf("  biPlanes %d\n", m_bh->biPlanes);
	printf("  biBitCount %d\n", m_bh->biBitCount);
	printf("  biCompression %d='%.4s'\n", m_bh->biCompression, (char*) &m_bh->biCompression);
	printf("  biSizeImage %d\n", m_bh->biSizeImage);
	printf("Dest fmt:\n");
	printf("  biSize %d\n", m_obh->biSize);
	printf("  biWidth %d\n", m_obh->biWidth);
	printf("  biHeight %d\n", m_obh->biHeight);
	printf("  biPlanes %d\n", m_obh->biPlanes);
	printf("  biBitCount %d\n", m_obh->biBitCount);
	printf("  biCompression %d='%.4s'\n", m_obh->biCompression, (char*) &m_obh->biCompression);
	printf("  biSizeImage %d\n", m_obh->biSizeImage);
 */
    if(hr!=0)
	cerr<<"ICCompressBegin() failed ( shouldn't happen ), error code "<<hr<<endl;
    else
    {
	m_iFrameNum = 0;
	m_iState = 2;
    }
}

void VideoEncoder::Stop()
{
    //cout << "STOPCOMPRESSION " << m_iState << endl;
    if (m_iState == 2)
    {
	//    HRESULT hr=ICCompressEnd(hic);
	HRESULT hr=codec->CompressEnd();
	if(hr!=0)
	    cerr<<"ICCompressEnd() failed ( shouldn't happen ), error code "<<hr<<endl;
	m_iState=1;
    }
}

int VideoEncoder::QueryOutputSize() const
{
//    return ICCompressGetSize(hic, m_bh, m_obh);
    return codec->CompressGetSize(m_bh, m_obh);
}

int VideoEncoder::EncodeFrame(CImage* src, void* dest, int* is_keyframe, size_t* size, int* lpckid)
{
    //int st1, st2;
    static BITMAPINFOHEADER m_prevbh;
    if (m_iState != 2 || !src)
	return -1;
#ifdef TIMING
    st1=localcount();
#endif
    CImage* temp=0;
    //	if(src->depth()!=m_bh->biBitCount)
    //	    temp=new CImage(src, m_bh->biBitCount);
    if(!src->IsFmt(m_bh))
	temp=new CImage(src, m_bh);
    //	int result=ICCompress(hic, (m_iKfFreq ? ((m_iFrameNum%m_iKfFreq)? 0 : ICCOMPRESS_KEYFRAME) : 0),
    //	  m_obh, dest, m_bh, src,
    //	  (long*)lpckid, (long*)is_keyframe, m_iFrameNum, 0,
    //	  m_iQual, &m_prevbh, m_prev);
    int result=codec->Compress((m_iKfFreq ? ((m_iFrameNum%m_iKfFreq)? 0 : ICCOMPRESS_KEYFRAME) : 0),
			       m_obh, dest, m_bh, temp ? temp->Data() : src->Data(),
			       (long*)lpckid, (long*)is_keyframe,
			       m_iFrameNum, 0,
			       m_iQual, &m_prevbh, m_prev);
    if (temp)
	temp->Release();

    if (result == 0)
    {
	//if(m_prev==0)m_prev=new char[m_bh->biSizeImage];
	//memcpy(m_prev, src, m_bh->biSizeImage);
	if (!m_prev)
	    m_prev = new uint8_t[codec->CompressGetSize(m_bh, m_obh)];
	memcpy(m_prev, dest, m_obh->biSizeImage);
	m_prevbh = *m_bh;
    }
    m_iFrameNum++;
    *size = m_obh->biSizeImage;
    return result;
}

int VideoEncoder::SetQuality(int quality)
{
    if(quality<0)
	return -1;
    if(quality>10000)
	return -1;
    m_iQual=quality;
    return 0;
}

int VideoEncoder::GetQuality() const
{
    return m_iQual;
}

int VideoEncoder::SetKeyFrame(int freq)
{
    if (freq <= 0)
	codec->GetDefaultKeyFrameRate(&m_iKfFreq);
    else
	m_iKfFreq = freq;

    return 0;
}

const BITMAPINFOHEADER& VideoEncoder::QueryOutputFormat() const
{
    return *m_obh;
}
