/****************************************************************************
 *
 *  PALMAP.C
 *
 *  Stream handler to map to a palette.
 *
 ***************************************************************************/

/**************************************************************************
 *
 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
 *  PURPOSE.
 *
 *  Copyright (c) 1992 - 1995  Microsoft Corporation.  All Rights Reserved.
 *
 **************************************************************************/

#define INC_OLE2
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <vfw.h>
#include <string.h>

#include "dibmap.h"
#include "palmap.h"
#include "palmap.rc"

#define	INITGUID
#include <initguid.h>

// Bring in the external GUIDs we need. Apparently compobj.lib doesn't define
DEFINE_OLEGUID(IID_IUnknown,		0x00000000L, 0, 0);


HINSTANCE	ghMod;

STDAPI AVIStreamMakePalette(
	PAVISTREAM pavi,
	LONG lSkip,
	HPALETTE FAR *lphpal,
	LPBYTE lp16to8,
	int nColors)
{
	LPHISTOGRAM lpHist = NULL;
	LONG	l, lEnd;
	LONG	lRet = AVIERR_OK;
	PGETFRAME	pgf = NULL;

	if (!pavi || !lphpal || nColors < 2 || nColors > 256)
		return ResultFromScode(AVIERR_BADPARAM);

	if (lSkip < 1)
		lSkip = 1;

	lpHist = InitHistogram(NULL);
	if (!lpHist)
		return ResultFromScode(AVIERR_MEMORY);

	pgf = AVIStreamGetFrameOpen(pavi, NULL);
	
	l = AVIStreamStart(pavi);
	lEnd = l + AVIStreamLength(pavi);
	for (l = AVIStreamStart(pavi), lEnd = l + AVIStreamLength(pavi);
		l < lEnd;
		l += lSkip) {
		LPBITMAPINFOHEADER lpbi;

		lpbi = AVIStreamGetFrame(pgf, l);

		if (!lpbi) {
			lRet = AVIERR_INTERNAL;
			goto error;
		}
	
		DibHistogram(lpbi, NULL, 0, 0, -1, -1, lpHist);
	}

	*lphpal = HistogramPalette(lpHist, lp16to8, nColors);

	if (!*lphpal)
		lRet = AVIERR_MEMORY;

error:
	if (pgf)
		AVIStreamGetFrameClose(pgf);
	
	if (lpHist)
		FreeHistogram(lpHist);

	return ResultFromScode(lRet);
}



typedef struct {
	IAVIStreamVtbl FAR *	lpVtbl;

	ULONG			ulRefCount;

	//
	// instance data
	//
	PAVISTREAM		pavi;
	PGETFRAME		pgf;
	AVISTREAMINFO		sinfo;
	HPALETTE		hpal;
	LPBYTE			lp16to8;
	LONG			lLastFrame;
	LPBITMAPINFOHEADER	lpdibLast;
} PALMAPSTREAM, FAR*PPALMAPSTREAM;

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE PalMapStreamQueryInterface(
	PAVISTREAM ps,
	REFIID riid,
	LPVOID FAR* ppvObj);
HRESULT STDMETHODCALLTYPE PalMapStreamCreate(
	PAVISTREAM ps,
	LONG lParam1,
	LONG lParam2);
ULONG	STDMETHODCALLTYPE PalMapStreamAddRef(
	PAVISTREAM ps);
ULONG	STDMETHODCALLTYPE PalMapStreamRelease(
	PAVISTREAM ps);
HRESULT STDMETHODCALLTYPE PalMapStreamInfo(
	PAVISTREAM ps,
	AVISTREAMINFOW FAR * psi,
	LONG lSize);
LONG	STDMETHODCALLTYPE PalMapStreamFindKeyFrame(
	PAVISTREAM ps,
	LONG lPos,
	LONG lFlags);
HRESULT STDMETHODCALLTYPE PalMapStreamReadFormat(
	PAVISTREAM ps,
	LONG lPos,
	LPVOID lpFormat,
	LONG FAR *lpcbFormat);
HRESULT STDMETHODCALLTYPE PalMapStreamSetFormat(
	PAVISTREAM ps,
	LONG lPos,
	LPVOID lpFormat,
	LONG cbFormat);
HRESULT STDMETHODCALLTYPE PalMapStreamRead(
	PAVISTREAM ps,
	LONG lStart,
	LONG lSamples,
	LPVOID lpBuffer,
	LONG cbBuffer,
	LONG FAR * plBytes,
	LONG FAR * plSamples);
HRESULT STDMETHODCALLTYPE PalMapStreamWrite(
	PAVISTREAM ps,
	LONG lStart,
	LONG lSamples,
	LPVOID lpBuffer,
	LONG cbBuffer,
	DWORD dwFlags,
	LONG FAR *plSampWritten,
	LONG FAR *plBytesWritten);
HRESULT STDMETHODCALLTYPE PalMapStreamDelete(
	PAVISTREAM ps,
	LONG lStart,
	LONG lSamples);
HRESULT STDMETHODCALLTYPE PalMapStreamReadData(
	PAVISTREAM ps,
	DWORD fcc,
	LPVOID lp,
	LONG FAR *lpcb);
HRESULT STDMETHODCALLTYPE PalMapStreamWriteData(
	PAVISTREAM ps,
	DWORD fcc,
	LPVOID lp,
	LONG cb);
HRESULT STDMETHODCALLTYPE PalMapStreamSetInfo(
        PAVISTREAM ps,
        AVISTREAMINFOW FAR * psi,
        LONG lSize);

IAVIStreamVtbl PalMapStreamHandler = {
	PalMapStreamQueryInterface,
	PalMapStreamAddRef,
	PalMapStreamRelease,
	PalMapStreamCreate,
	PalMapStreamInfo,
	PalMapStreamFindKeyFrame,
	PalMapStreamReadFormat,
	PalMapStreamSetFormat,
	PalMapStreamRead,
	PalMapStreamWrite,
	PalMapStreamDelete,
	PalMapStreamReadData,
	PalMapStreamWriteData,
        PalMapStreamSetInfo
};

STDAPI AVICreateMappedStream(PAVISTREAM FAR *ppsMapped,
	PAVISTREAM ps,
	int nColors)
{
	PPALMAPSTREAM	pavi;
	HRESULT		hr;

	*ppsMapped = 0;
	
	pavi = (PPALMAPSTREAM) GlobalAllocPtr(GHND, sizeof(PALMAPSTREAM));
	if (pavi == NULL)
		return ResultFromScode(AVIERR_MEMORY);

	pavi->lpVtbl = &PalMapStreamHandler;
	
	hr = (pavi->lpVtbl->Create)((PAVISTREAM) pavi, (LONG) ps, nColors);

	if (hr != NOERROR) {
		(pavi->lpVtbl->Release)((PAVISTREAM) pavi);
		return hr;
	}
	
	*ppsMapped = (PAVISTREAM) pavi;

	return AVIERR_OK;


}

///////////////////////////////////////////////////////////////////////////
//
//  PalMapStreamOpen()
//
//  open a single stream of a particular type from a AVI file.
//
//  params:
//      szFile      - PAVISTREAM
//      fccType     - must be streamtypeVIDEO
//      lParam	    - nColors
//
//  returns:
//      a PAVISTREAM for the specifed stream or NULL.
//
///////////////////////////////////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE PalMapStreamCreate(
	PAVISTREAM ps,
	LONG lParam1,
	LONG lParam2)
{
	PPALMAPSTREAM	pavi = (PPALMAPSTREAM) ps;
	TCHAR		achTemp[128];
	TCHAR		achTemplate[64];

	LONG		lRet = AVIERR_OK;

	pavi->ulRefCount = 1;
	
	AVIStreamAddRef((PAVISTREAM) lParam1);

	pavi->pavi = (PAVISTREAM) lParam1;
	
	AVIStreamInfo(pavi->pavi, &pavi->sinfo, sizeof(pavi->sinfo));

	if (pavi->sinfo.fccType != streamtypeVIDEO) {
	lRet = AVIERR_INTERNAL;
	goto error;
	}

	pavi->pgf = AVIStreamGetFrameOpen(pavi->pavi, NULL);

	if (!pavi->pgf) {
	lRet = AVIERR_INTERNAL;
	goto error;
	}

	pavi->sinfo.fccHandler = 0;

	// Fix up stream name
	LoadString(ghMod, IDS_STREAMNAME, achTemplate,
			sizeof(achTemplate) / sizeof(TCHAR));
	wsprintf(achTemp, achTemplate, pavi->sinfo.szName, lParam2);
	lstrcpyn(pavi->sinfo.szName, achTemp,
			sizeof(pavi->sinfo.szName) / sizeof(TCHAR));
	pavi->sinfo.szName[sizeof(pavi->sinfo.szName) / sizeof(TCHAR) - 1] =
                TEXT('\0');

	// default to 256 colors
	if (lParam2 < 2 || lParam2 > 256)
	lParam2 = 256;

	pavi->lp16to8 = GlobalAllocPtr(GMEM_MOVEABLE, 32768L);
	if (!pavi->lp16to8) {
	lRet = AVIERR_MEMORY;
	goto error;
	}
	
	lRet = GetScode(AVIStreamMakePalette(pavi->pavi,
				AVIStreamLength(pavi->pavi) / 30,
				&pavi->hpal, pavi->lp16to8,
				(int) lParam2));

	pavi->lLastFrame = -1;
	
error:
	return ResultFromScode(lRet);
}

///////////////////////////////////////////////////////////////////////////
//
//  PalMapStreamQueryInterface()
//
//      let other people know what interfaces we support
//
///////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE PalMapStreamQueryInterface(
	PAVISTREAM ps,
	REFIID riid,
	LPVOID FAR* ppvObj)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;
	
	if (IsEqualIID(riid, &IID_IUnknown))
		*ppvObj = ps;
	else if (IsEqualIID(riid, &IID_IAVIStream))
		*ppvObj = ps;
	else
		return ResultFromScode(E_NOINTERFACE);
	
	pavi->ulRefCount++;
	return NOERROR;
}

///////////////////////////////////////////////////////////////////////////
//
//  PalMapStreamAddRef()
//
//      increase the reference count of the stream
//
///////////////////////////////////////////////////////////////////////////

ULONG STDMETHODCALLTYPE PalMapStreamAddRef(
	PAVISTREAM ps)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;

	return ++pavi->ulRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
//  PalMapStreamRelease()
//
//      close a PalMapStream stream
//
///////////////////////////////////////////////////////////////////////////

ULONG STDMETHODCALLTYPE PalMapStreamRelease(
	PAVISTREAM ps)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;

	if (!pavi)
		return 0;
	
	if (--pavi->ulRefCount)
		return pavi->ulRefCount;

	if (pavi->pgf)
		AVIStreamGetFrameClose(pavi->pgf);
	pavi->pgf = 0;
	
	if (pavi->pavi)
		AVIStreamClose(pavi->pavi);
	pavi->pavi = 0;

	if (pavi->lp16to8) {
		GlobalFreePtr(pavi->lp16to8);
		pavi->lp16to8 = 0;
	}

	if (pavi->hpal) {
		DeletePalette(pavi->hpal);
		pavi->hpal = 0;
	}
	
	if (pavi->lpdibLast) {
		GlobalFreePtr(pavi->lpdibLast);
		pavi->lpdibLast = 0;
	}
	
	GlobalFreePtr(pavi);
	
	return 0;
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE PalMapStreamReadFormat(
	PAVISTREAM ps,
	LONG lPos,
	LPVOID lpFormat,
	LONG FAR *lpcbFormat)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;
	LONG			lSize;
	
	PalMapStreamRead(ps, lPos, 1, NULL, 0, NULL, NULL);

	if (pavi->lpdibLast == 0)
		return ResultFromScode(AVIERR_INTERNAL);
	lSize = pavi->lpdibLast->biSize
		+ pavi->lpdibLast->biClrUsed * sizeof(RGBQUAD);

	if (lpFormat)
		hmemcpy(lpFormat, pavi->lpdibLast,
			min(*lpcbFormat, lSize));

	*lpcbFormat = lSize;
	
	return 0;
}

LONG STDMETHODCALLTYPE PalMapStreamFindKeyFrame(
	PAVISTREAM ps,
	LONG lPos,
	LONG lFlags)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;

	return lPos;
}


///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE PalMapStreamInfo(
	PAVISTREAM ps,
	AVISTREAMINFOW FAR * psi,        // OLE interfaces are ALWAYS UNICODE
	LONG lSize)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;

	if (psi)
	hmemcpy(psi, &pavi->sinfo, min(lSize, sizeof(pavi->sinfo)));
	
	return 0;
}


///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE PalMapStreamRead(
	PAVISTREAM	ps,
	LONG		lStart,
	LONG		lSamples,
	LPVOID		lpBuffer,
	LONG		cbBuffer,
	LONG FAR *	plBytes,
	LONG FAR *	plSamples)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;

	LPBITMAPINFOHEADER	lpbi;
	LPVOID			lp;

	if (lStart != pavi->lLastFrame) {
		pavi->lLastFrame = -1;
		lpbi = AVIStreamGetFrame(pavi->pgf, lStart);
	
		if (!lpbi)
			goto ReadNothing;

		if (pavi->lpdibLast) {
			GlobalFreePtr(pavi->lpdibLast);
			pavi->lpdibLast = 0;
		}

		pavi->lpdibLast = DibReduce(lpbi, NULL, pavi->hpal, pavi->lp16to8);
		pavi->lLastFrame = lStart;
	}

	lpbi = pavi->lpdibLast;	
	//
	// a NULL buffer means return the size buffer needed to read
	// the given sample.
	//
	lp = (LPBYTE) lpbi + lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
	
	if (plBytes)
		*plBytes = lpbi->biSizeImage;

	if (plSamples)
		*plSamples = 1;

	if (lpBuffer) {
		if (cbBuffer >= (LONG) lpbi->biSizeImage)
			hmemcpy(lpBuffer, lp, lpbi->biSizeImage);
		else
			return ResultFromScode(AVIERR_BUFFERTOOSMALL);
	}
	
	return 0;
	
ReadNothing:
	if (plBytes)
		*plBytes = 0;

	if (plSamples)
		*plSamples = 0;

	return ResultFromScode(AVIERR_BUFFERTOOSMALL);
}



//
//
// Extra unimplemented functions.....
//
//
//
HRESULT STDMETHODCALLTYPE PalMapStreamReadData(
	PAVISTREAM ps,
	DWORD fcc,
	LPVOID lp,
	LONG FAR *lpcb)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;
	return ResultFromScode(AVIERR_UNSUPPORTED);
}

HRESULT STDMETHODCALLTYPE PalMapStreamSetFormat(
	PAVISTREAM ps,
	LONG lPos,
	LPVOID lpFormat,
	LONG cbFormat)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;
	return ResultFromScode(AVIERR_UNSUPPORTED);
}

HRESULT STDMETHODCALLTYPE PalMapStreamWriteData(
	PAVISTREAM ps,
	DWORD fcc,
	LPVOID lp,
	LONG cb)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;
	return ResultFromScode(AVIERR_UNSUPPORTED);
}

HRESULT STDMETHODCALLTYPE PalMapStreamWrite(
	PAVISTREAM ps,
	LONG lStart,
	LONG lSamples,
	LPVOID lpBuffer,
	LONG cbBuffer,
	DWORD dwFlags,
	LONG FAR *plSampWritten,
	LONG FAR *plBytesWritten)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;
	return ResultFromScode(AVIERR_UNSUPPORTED);
}

HRESULT STDMETHODCALLTYPE PalMapStreamDelete(
	PAVISTREAM ps,
	LONG lStart,
	LONG lSamples)
{
	PPALMAPSTREAM pavi = (PPALMAPSTREAM) ps;
	return ResultFromScode(AVIERR_UNSUPPORTED);
}

HRESULT STDMETHODCALLTYPE PalMapStreamSetInfo(
   PAVISTREAM ps,
   AVISTREAMINFOW FAR * psi,
   LONG lSize)
{
	return ResultFromScode(AVIERR_UNSUPPORTED);
}

EXTERN_C int CALLBACK WEP(
	BOOL fSystemExit)
{
	return TRUE;
}


EXTERN_C BOOL APIENTRY DllMain(HANDLE, DWORD, LPVOID);
EXTERN_C BOOL APIENTRY DllMain(
	HANDLE hModule,
	DWORD dwReason,
	LPVOID lpReserved )
{
	switch( dwReason)
	{
		case DLL_PROCESS_ATTACH:
			if(ghMod == NULL)
				ghMod = (HMODULE)hModule;
			break;
		case DLL_THREAD_ATTACH:
			break;
		case DLL_THREAD_DETACH:
			break;
		case DLL_PROCESS_DETACH:
			break;
	}
	return TRUE;
}
