/*
 * Copyright 1999 Marcus Meissner
 * Copyright 2001 Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
 *
 * FIXME - implements editing/writing.
 */

#include <string.h>
#include <stdio.h>
#include <assert.h>

#include "winbase.h"
#include "winnls.h"
#include "mmsystem.h"
#include "winerror.h"
#include "winuser.h"
#include "vfw.h"
#include "wine/debug.h"
#include "avifile_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(avifile);

#define	AVIFILE_STREAMS_MAX	4


static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface);
static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);

struct ICOM_VTABLE(IAVIFile) iavift = {
    ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
    IAVIFile_fnQueryInterface,
    IAVIFile_fnAddRef,
    IAVIFile_fnRelease,
    IAVIFile_fnInfo,
    IAVIFile_fnGetStream,
    IAVIFile_fnCreateStream,
    IAVIFile_fnWriteData,
    IAVIFile_fnReadData,
    IAVIFile_fnEndRecord,
    IAVIFile_fnDeleteStream,
    /* IAVIFILE_fnOpen */ /* FIXME? */
};


typedef struct IAVIFileImpl
{
	ICOM_VFIELD(IAVIFile);
	/* IUnknown stuff */
	DWORD			ref;
	/* IAVIFile stuff */
	HANDLE			hf;
	DWORD			dwAVIFileCaps;
	DWORD			dwAVIFileScale;
	DWORD			dwAVIFileRate;
	DWORD			dwAVIFileLength;
	DWORD			dwAVIFileEditCount;
	MainAVIHeader		hdr;
	IAVIStream*		pStreams[AVIFILE_STREAMS_MAX];
	AVIStreamHeader		strhdrs[AVIFILE_STREAMS_MAX];
	DWORD			dwMoviTop;
	DWORD			dwCountOfIndexEntry;
	AVIINDEXENTRY*		pIndexEntry;
	AVIINDEXENTRY*		pStreamIndexEntry[AVIFILE_STREAMS_MAX+1];
} IAVIFileImpl;


/****************************************************************************
 * AVI file parser.
 */

static HRESULT AVIFILE_IAVIFile_ReadNextChunkHeader(
		IAVIFileImpl* This, FOURCC* pfcc, DWORD* pdwSize )
{
	BYTE	buf[8];
	DWORD	dwRead;

	if ( ( !ReadFile( This->hf, buf, 8, &dwRead, NULL ) ) ||
	     ( 8 != dwRead ) )
		return AVIERR_FILEREAD;
	*pfcc = mmioFOURCC(buf[0],buf[1],buf[2],buf[3]);
	*pdwSize = ( ((DWORD)buf[4])       ) |
		   ( ((DWORD)buf[5]) <<  8 ) |
		   ( ((DWORD)buf[6]) << 16 ) |
		   ( ((DWORD)buf[7]) << 24 );

	return S_OK;
}

static HRESULT AVIFILE_IAVIFile_SkipChunkData(
		IAVIFileImpl* This, DWORD dwChunkSize )
{
	LONG	lHigh = 0;
	DWORD	dwRes;

	if ( dwChunkSize == 0 )
		return S_OK;

	SetLastError(NO_ERROR);
	dwRes = SetFilePointer( This->hf, (LONG)dwChunkSize,
				&lHigh, FILE_CURRENT );
	if ( dwRes == (DWORD)0xffffffff && GetLastError() != NO_ERROR )
		return AVIERR_FILEREAD;

	return S_OK;
}

static HRESULT AVIFILE_IAVIFile_ReadChunkData(
		IAVIFileImpl* This, DWORD dwChunkSize,
		LPVOID lpvBuf, DWORD dwBufSize, LPDWORD lpdwRead )
{
	if ( dwBufSize > dwChunkSize )
		dwBufSize = dwChunkSize;
	if ( ( !ReadFile( This->hf, lpvBuf, dwBufSize, lpdwRead, NULL ) ) ||
	     ( dwBufSize != *lpdwRead ) )
		return AVIERR_FILEREAD;

	return AVIFILE_IAVIFile_SkipChunkData( This, dwChunkSize - dwBufSize );
}

static HRESULT AVIFILE_IAVIFile_SeekToSpecifiedChunk(
		IAVIFileImpl* This, FOURCC fccType, DWORD* pdwLen )
{
	HRESULT	hr;
	FOURCC	fcc;
	BYTE	buf[4];
	DWORD	dwRead;

	while ( 1 )
	{
		hr = AVIFILE_IAVIFile_ReadNextChunkHeader(
				This, &fcc, pdwLen );
		if ( hr != S_OK )
			return hr;
		if ( fcc == fccType )
			return S_OK;

		if ( fcc == FOURCC_LIST )
		{
		    if ( ( !ReadFile( This->hf, buf, 4, &dwRead, NULL ) ) ||
			 ( 4 != dwRead ) )
			return AVIERR_FILEREAD;
		}
		else
		{
		    hr = AVIFILE_IAVIFile_SkipChunkData(
					This, *pdwLen );
		    if ( hr != S_OK )
			return hr;
		}
	}
}


WINE_AVISTREAM_DATA* AVIFILE_Alloc_IAVIStreamData( DWORD dwFmtLen )
{
	WINE_AVISTREAM_DATA*	pData;

	pData = (WINE_AVISTREAM_DATA*)
		HeapAlloc( AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
			   sizeof(WINE_AVISTREAM_DATA) );
	if ( pData == NULL )
		return NULL;
	if ( dwFmtLen > 0 )
	{
		pData->pbFmt = (BYTE*)
			HeapAlloc( AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
				   sizeof(BYTE)*dwFmtLen );
		if ( pData->pbFmt == NULL )
		{
			AVIFILE_Free_IAVIStreamData( pData );
			return NULL;
		}
	}
	pData->dwFmtLen = dwFmtLen;

	return pData;
}

void AVIFILE_Free_IAVIStreamData( WINE_AVISTREAM_DATA* pData )
{
	if ( pData != NULL )
	{
		if ( pData->pbFmt != NULL )
			HeapFree( AVIFILE_data.hHeap,0,pData->pbFmt );
		HeapFree( AVIFILE_data.hHeap,0,pData );
	}
}

static void AVIFILE_IAVIFile_InitIndexTable(
		IAVIFileImpl* This,
		AVIINDEXENTRY* pIndexBuf,
		AVIINDEXENTRY* pIndexData,
		DWORD dwCountOfIndexEntry )
{
	DWORD	dwStreamIndex;
	DWORD	dwIndex;
	FOURCC	ckid;

	dwStreamIndex = 0;
	for ( ; dwStreamIndex < (AVIFILE_STREAMS_MAX+1); dwStreamIndex ++ )
		This->pStreamIndexEntry[dwStreamIndex] = NULL;

	dwStreamIndex = 0;
	for ( ; dwStreamIndex < This->hdr.dwStreams; dwStreamIndex ++ )
	{
		ckid = mmioFOURCC('0','0'+dwStreamIndex,0,0);
		TRACE( "testing ckid %c%c%c%c\n",
			(int)(ckid>> 0)&0xff,
			(int)(ckid>> 8)&0xff,
			(int)(ckid>>16)&0xff,
			(int)(ckid>>24)&0xff );
		This->pStreamIndexEntry[dwStreamIndex] = pIndexBuf;
		FIXME( "pIndexBuf = %p\n", pIndexBuf );
		for ( dwIndex = 0; dwIndex < dwCountOfIndexEntry; dwIndex++ )
		{
		    TRACE( "ckid %c%c%c%c\n",
			    (int)(pIndexData[dwIndex].ckid>> 0)&0xff,
                            (int)(pIndexData[dwIndex].ckid>> 8)&0xff,
                            (int)(pIndexData[dwIndex].ckid>>16)&0xff,
                            (int)(pIndexData[dwIndex].ckid>>24)&0xff );
                            
		    if ( (pIndexData[dwIndex].ckid & mmioFOURCC(0xff,0xff,0,0))
								== ckid )
		    {
			memcpy( pIndexBuf, &pIndexData[dwIndex],
				sizeof(AVIINDEXENTRY) );
			pIndexBuf ++;
		    }
		}
                FIXME( "pIndexBuf = %p\n", pIndexBuf );
	}
	This->pStreamIndexEntry[This->hdr.dwStreams] = pIndexBuf;
}


/****************************************************************************
 * Create an IAVIFile object.
 */

static HRESULT AVIFILE_IAVIFile_Construct( IAVIFileImpl* This );
static void AVIFILE_IAVIFile_Destruct( IAVIFileImpl* This );

HRESULT AVIFILE_CreateIAVIFile(void** ppobj)
{
	IAVIFileImpl	*This;
	HRESULT		hr;

	TRACE("(%p)\n",ppobj);
	*ppobj = NULL;
	This = (IAVIFileImpl*)HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
					sizeof(IAVIFileImpl));
	if ( This == NULL )
		return AVIERR_MEMORY;
	This->ref = 1;
	ICOM_VTBL(This) = &iavift;
	hr = AVIFILE_IAVIFile_Construct( This );
	if ( hr != S_OK )
	{
		AVIFILE_IAVIFile_Destruct( This );
		return hr;
	}

	TRACE("new -> %p\n",This);
	*ppobj = (LPVOID)This;

	return S_OK;
}

/****************************************************************************
 * IUnknown interface
 */

static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj) {
	ICOM_THIS(IAVIFileImpl,iface);

	TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(refiid),obj);
	if ( IsEqualGUID(&IID_IUnknown,refiid) ||
	     IsEqualGUID(&IID_IAVIFile,refiid) )
	{
		*obj = iface;
		IAVIFile_AddRef(iface);
		return S_OK;
	}
	return OLE_E_ENUM_NOMORE;
}

static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface) {
	ICOM_THIS(IAVIFileImpl,iface);

	TRACE("(%p)->AddRef()\n",iface);
	return ++(This->ref);
}

static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface) {
	ICOM_THIS(IAVIFileImpl,iface);

	TRACE("(%p)->Release()\n",iface);
	if ( (--(This->ref)) > 0 )
		return This->ref;

	AVIFILE_IAVIFile_Destruct(This);
	HeapFree(AVIFILE_data.hHeap,0,iface);
	return 0;
}

/****************************************************************************
 * IAVIFile interface
 */

static HRESULT AVIFILE_IAVIFile_Construct( IAVIFileImpl* This )
{
	DWORD	dwIndex;

	This->hf = INVALID_HANDLE_VALUE;
	This->dwAVIFileCaps = 0;
	This->dwAVIFileScale = 0;
	This->dwAVIFileRate = 0;
	This->dwAVIFileLength = 0;
	This->dwAVIFileEditCount = 0;
	for ( dwIndex = 0; dwIndex < AVIFILE_STREAMS_MAX; dwIndex++ )
		This->pStreams[dwIndex] = NULL;
	This->dwCountOfIndexEntry = 0;
	This->pIndexEntry = NULL;

	AVIFILE_data.dwClassObjRef ++;

	return S_OK;
}

static void AVIFILE_IAVIFile_Destruct( IAVIFileImpl* This )
{
	DWORD	dwIndex;

	if ( This->pIndexEntry != NULL )
	{
		HeapFree(AVIFILE_data.hHeap,0,This->pIndexEntry);
		This->pIndexEntry = NULL;
	}

	for ( dwIndex = 0; dwIndex < AVIFILE_STREAMS_MAX; dwIndex++ )
	{
		if ( This->pStreams[dwIndex] != NULL )
		{
			IAVIStream_Release( This->pStreams[dwIndex] );
			This->pStreams[dwIndex] = NULL;
		}
	}

	if ( This->hf != INVALID_HANDLE_VALUE )
		CloseHandle( This->hf );

	AVIFILE_data.dwClassObjRef --;
}

static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size)
{
	ICOM_THIS(IAVIFileImpl,iface);
	AVIFILEINFOW	fiw;

	FIXME("(%p)->Info(%p,%ld)\n",iface,afi,size);

	memset( &fiw, 0, sizeof(fiw) );
	fiw.dwMaxBytesPerSec = This->hdr.dwMaxBytesPerSec;
	fiw.dwFlags = This->hdr.dwFlags;
	fiw.dwCaps = This->dwAVIFileCaps;
	fiw.dwStreams = This->hdr.dwStreams;
	fiw.dwSuggestedBufferSize = This->hdr.dwSuggestedBufferSize;
	fiw.dwWidth = This->hdr.dwWidth;
	fiw.dwHeight = This->hdr.dwHeight;
	fiw.dwScale = This->dwAVIFileScale; /* FIXME */
	fiw.dwRate = This->dwAVIFileRate; /* FIXME */
	fiw.dwLength = This->dwAVIFileLength; /* FIXME */
	fiw.dwEditCount = This->dwAVIFileEditCount; /* FIXME */
	/* fiw.szFileType[64]; */

	if ( size > sizeof(AVIFILEINFOW) )
		size = sizeof(AVIFILEINFOW);
	memcpy( afi, &fiw, size );

	return S_OK;
}

static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam)
{
	ICOM_THIS(IAVIFileImpl,iface);

	FIXME("(%p)->GetStream(%p,0x%08lx,%ld)\n",iface,avis,fccType,lParam);
	if ( fccType != 0 )
		return E_FAIL;
	if ( lParam < 0 || lParam >= This->hdr.dwStreams )
		return E_FAIL;
	*avis = This->pStreams[lParam];
	IAVIStream_AddRef( *avis );

	return S_OK;
}

static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi)
{
	ICOM_THIS(IAVIFileImpl,iface);

	FIXME("(%p,%p,%p)\n",This,avis,asi);
	return E_FAIL;
}

static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size)
{
	ICOM_THIS(IAVIFileImpl,iface);

	FIXME("(%p)->WriteData(0x%08lx,%p,%ld)\n",This,ckid,lpData,size);
	/* FIXME: write data to file */
	return E_FAIL;
}

static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size)
{
	ICOM_THIS(IAVIFileImpl,iface);

	FIXME("(%p)->ReadData(0x%08lx,%p,%p)\n",This,ckid,lpData,size);
	/* FIXME: read at most size bytes from file */

	return E_FAIL;
}

static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface)
{
	ICOM_THIS(IAVIFileImpl,iface);

	FIXME("(%p)->EndRecord()\n",This);
	/* FIXME: end record? */
	return E_FAIL;
}

static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam)
{
	ICOM_THIS(IAVIFileImpl,iface);

	FIXME("(%p)->DeleteStream(0x%08lx,%ld)\n",This,fccType,lParam);
	/* FIXME: delete stream? */
	return E_FAIL;
}

/*****************************************************************************
 *	AVIFILE_IAVIFile_Open (internal)
 */
HRESULT AVIFILE_IAVIFile_Open( PAVIFILE paf, LPCWSTR szFile, UINT uMode )
{
	ICOM_THIS(IAVIFileImpl,paf);
	HRESULT		hr;
	DWORD		dwAcc;
	DWORD		dwShared;
	DWORD		dwCreate;
	BYTE		buf[12];
	DWORD		dwRead;
	FOURCC		fccFileType;
	DWORD		dwLen;
	DWORD		dwIndex;

	FIXME("(%p)->Open(%p,%u)\n",This,szFile,uMode);

	if ( This->hf != INVALID_HANDLE_VALUE )
	{
		CloseHandle( This->hf );
		This->hf = INVALID_HANDLE_VALUE;
	}

	switch ( uMode & 0x3 )
	{
	case OF_READ: /* 0x0 */
		dwAcc = GENERIC_READ;
		dwCreate = OPEN_EXISTING;
		This->dwAVIFileCaps = AVIFILECAPS_CANREAD;
		break;
	case OF_WRITE: /* 0x1 */
		dwAcc = GENERIC_WRITE;
		dwCreate = OPEN_ALWAYS;
		This->dwAVIFileCaps = AVIFILECAPS_CANWRITE;
		break;
	case OF_READWRITE: /* 0x2 */
		dwAcc = GENERIC_READ|GENERIC_WRITE;
		dwCreate = OPEN_ALWAYS;
		This->dwAVIFileCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
		break;
	default:
		return E_FAIL;
	}

	if ( This->dwAVIFileCaps & AVIFILECAPS_CANWRITE )
	{
		FIXME( "editing AVI is currently not supported!\n" );
		return E_FAIL;
	}

	switch ( uMode & 0x70 )
	{
	case OF_SHARE_COMPAT: /* 0x00 */
		dwShared = FILE_SHARE_READ|FILE_SHARE_WRITE;
		break;
	case OF_SHARE_EXCLUSIVE: /* 0x10 */
		dwShared = 0;
		break;
	case OF_SHARE_DENY_WRITE: /* 0x20 */
		dwShared = FILE_SHARE_READ;
		break;
	case OF_SHARE_DENY_READ: /* 0x30 */
		dwShared = FILE_SHARE_WRITE;
		break;
	case OF_SHARE_DENY_NONE: /* 0x40 */
		dwShared = FILE_SHARE_READ|FILE_SHARE_WRITE;
		break;
	default:
		return E_FAIL;
	}
	if ( uMode & OF_CREATE )
		dwCreate = CREATE_ALWAYS;

	This->hf = CreateFileW( szFile, dwAcc, dwShared, NULL,
				dwCreate, FILE_ATTRIBUTE_NORMAL,
				(HANDLE)NULL );
	if ( This->hf == INVALID_HANDLE_VALUE )
		return AVIERR_FILEOPEN;

	if ( dwAcc & GENERIC_READ )
	{
	    if ( !ReadFile( This->hf, buf, 12, &dwRead, NULL ) )
		return AVIERR_FILEREAD;
	    if ( dwRead == 12 )
	    {
		if ( mmioFOURCC(buf[0],buf[1],buf[2],buf[3]) != FOURCC_RIFF )
			return AVIERR_BADFORMAT;

		fccFileType = mmioFOURCC(buf[8],buf[9],buf[10],buf[11]);
		if ( fccFileType != formtypeAVI )
			return AVIERR_BADFORMAT;

		/* get AVI main header. */
		hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
				This, ckidAVIMAINHDR, &dwLen );
		if ( hr != S_OK )
			return hr;
		if ( dwLen < (sizeof(DWORD)*10) )
			return AVIERR_BADFORMAT;
		hr = AVIFILE_IAVIFile_ReadChunkData(
				This, dwLen,
				&(This->hdr), sizeof(MainAVIHeader), &dwLen );
		if ( This->hdr.dwStreams == 0 ||
		     This->hdr.dwStreams > AVIFILE_STREAMS_MAX )
			return AVIERR_BADFORMAT;

		/* get stream headers. */
		dwIndex = 0;
		while ( dwIndex < This->hdr.dwStreams )
		{
			WINE_AVISTREAM_DATA*	pData;

			hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
					This, ckidSTREAMHEADER, &dwLen );
			if ( hr != S_OK )
				return hr;
			if ( dwLen < (sizeof(DWORD)*12) )
				return AVIERR_BADFORMAT;
			hr = AVIFILE_IAVIFile_ReadChunkData(
				This, dwLen,
				&This->strhdrs[dwIndex],
				sizeof(AVIStreamHeader), &dwLen );

			hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
					This, ckidSTREAMFORMAT, &dwLen );
			if ( hr != S_OK )
				return hr;
			pData = AVIFILE_Alloc_IAVIStreamData( dwLen );
			if ( pData == NULL )
				return AVIERR_MEMORY;
			hr = AVIFILE_IAVIFile_ReadChunkData(
				This, dwLen,
				pData->pbFmt, dwLen, &dwLen );
			if ( hr != S_OK )
			{
				AVIFILE_Free_IAVIStreamData( pData );
				return hr;
			}
			pData->dwStreamIndex = dwIndex;
			pData->pstrhdr = &This->strhdrs[dwIndex];

			hr = AVIStreamCreate(&This->pStreams[dwIndex],
					     (LONG)paf, (LONG)(pData), NULL );
			if ( hr != S_OK )
			{
				AVIFILE_Free_IAVIStreamData( pData );
				return hr;
			}

			if ( (This->strhdrs[dwIndex].fccType
					== mmioFOURCC('v','i','d','s')) ||
			     (This->strhdrs[dwIndex].fccType
					== mmioFOURCC('V','I','D','S')) )
			{
				This->dwAVIFileScale =
					This->strhdrs[dwIndex].dwScale;
				This->dwAVIFileRate =
					This->strhdrs[dwIndex].dwRate;
				This->dwAVIFileLength =
					This->strhdrs[dwIndex].dwLength;
			}
			else
			if ( This->dwAVIFileScale == 0 )
			{
				This->dwAVIFileScale =
					This->strhdrs[dwIndex].dwScale;
				This->dwAVIFileRate =
					This->strhdrs[dwIndex].dwRate;
				This->dwAVIFileLength =
					This->strhdrs[dwIndex].dwLength;
			}

			dwIndex ++;
		}

		/* skip movi. */
		while ( 1 )
		{
			hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
					This, FOURCC_LIST, &dwLen );
			if ( hr != S_OK )
				return hr;
			if ( dwLen < 4 )
				return AVIERR_BADFORMAT;

			This->dwMoviTop = SetFilePointer( This->hf,0,NULL,FILE_CURRENT );
			if ( This->dwMoviTop == 0xffffffff )
				return AVIERR_BADFORMAT;

			if ( ( !ReadFile(This->hf, buf, 4, &dwRead, NULL) ) ||
			     ( dwRead != 4 ) )
				return AVIERR_FILEREAD;

			hr = AVIFILE_IAVIFile_SkipChunkData(
					This, dwLen - 4 );
			if ( hr != S_OK )
				return hr;
			if ( mmioFOURCC(buf[0],buf[1],buf[2],buf[3])
				== mmioFOURCC('m', 'o', 'v', 'i') )
				break;
		}

		/* get idx1. */
		hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
				This, ckidAVINEWINDEX, &dwLen );
		if ( hr != S_OK )
			return hr;

		This->dwCountOfIndexEntry = dwLen / sizeof(AVIINDEXENTRY);
		This->pIndexEntry = (AVIINDEXENTRY*)
			HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
				  sizeof(AVIINDEXENTRY) *
				  This->dwCountOfIndexEntry * 2 );
		if ( This->pIndexEntry == NULL )
			return AVIERR_MEMORY;
		hr = AVIFILE_IAVIFile_ReadChunkData(
				This, dwLen,
				This->pIndexEntry + This->dwCountOfIndexEntry,
				sizeof(AVIINDEXENTRY) *
				This->dwCountOfIndexEntry, &dwLen );
		if ( hr != S_OK )
			return hr;
		AVIFILE_IAVIFile_InitIndexTable(
				This, This->pIndexEntry,
				This->pIndexEntry + This->dwCountOfIndexEntry,
				This->dwCountOfIndexEntry );
	    }
	    else
	    {
		/* FIXME - create the handle has GENERIC_WRITE access. */
		return AVIERR_FILEREAD;
	    }
	}
	else
	{
	    return AVIERR_FILEOPEN; /* FIXME */
	}

	return S_OK;
}

/*****************************************************************************
 *	AVIFILE_IAVIFile_GetIndexTable (internal)
 */
HRESULT AVIFILE_IAVIFile_GetIndexTable( PAVIFILE paf, DWORD dwStreamIndex,
					AVIINDEXENTRY** ppIndexEntry,
					DWORD* pdwCountOfIndexEntry )
{
	ICOM_THIS(IAVIFileImpl,paf);

	if ( dwStreamIndex < 0 || dwStreamIndex >= This->hdr.dwStreams )
	{
		FIXME( "invalid stream index %lu\n", dwStreamIndex );
		return E_FAIL;
	}
	FIXME( "cur %p, next %p\n",
		This->pStreamIndexEntry[dwStreamIndex],
		This->pStreamIndexEntry[dwStreamIndex+1] );
	*ppIndexEntry = This->pStreamIndexEntry[dwStreamIndex];
	*pdwCountOfIndexEntry =
		This->pStreamIndexEntry[dwStreamIndex+1] -
			This->pStreamIndexEntry[dwStreamIndex];

	return S_OK;
}

/*****************************************************************************
 *	AVIFILE_IAVIFile_ReadMovieData (internal)
 */
HRESULT AVIFILE_IAVIFile_ReadMovieData( PAVIFILE paf, DWORD dwOffset,
					DWORD dwLength, LPVOID lpvBuf )
{
	ICOM_THIS(IAVIFileImpl,paf);
	LONG	lHigh = 0;
	DWORD	dwRes;

	if ( dwLength == 0 )
		return S_OK;
	SetLastError(NO_ERROR);
	dwRes = SetFilePointer( This->hf, (LONG)(dwOffset+This->dwMoviTop),
				&lHigh, FILE_BEGIN );
	if ( dwRes == (DWORD)0xffffffff && GetLastError() != NO_ERROR )
		return AVIERR_FILEREAD;

	if ( ( !ReadFile(This->hf, lpvBuf, dwLength, &dwRes, NULL) ) ||
	     ( dwLength != dwRes ) )
	{
		FIXME( "error in ReadFile()\n" );
		return AVIERR_FILEREAD;
	}

	return S_OK;
}

