/****************************************************************************
 *
 *  PUZZPROC.C
 *
 *  Modification of standard AVI drawing handler.
 *
 ***************************************************************************/
/**************************************************************************
 *
 *  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 - 1994  Microsoft Corporation.  All Rights Reserved.
 *
 **************************************************************************/

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

#include "puzzproc.h"
#include "puzzle.h"

#define LOCAL  __stdcall
#define SZCODE char

static SZCODE szDescription[] = "Microsoft Puzzle Draw handler";
static SZCODE szName[]        = "MS Puzzle";

#define FOURCC_AVIDraw          mmioFOURCC('P','U','Z','Z')
#define VERSION_AVIDraw         0x00010000      // 1.00

extern PUZZLE   gPuzzle;

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

typedef struct {
    HDRAWDIB            hdd;
    HDC                 hdc;            // HDC to draw to
    int                 xDst;           // destination rectangle
    int                 yDst;
    int                 dxDst;
    int                 dyDst;
    int                 xSrc;           // source rectangle
    int                 ySrc;
    int                 dxSrc;
    int                 dySrc;
    LPBYTE              lpBuffer;
} INSTINFO;


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

typedef ICOPEN *                LPICOPEN;
typedef ICINFO *                LPICINFO;
typedef ICDRAW *                LPICDRAW;
typedef ICDRAWSUGGEST *         LPICDRAWSUGGEST;
typedef ICDRAWBEGIN *           LPICDRAWBEGIN;
typedef INSTINFO *              LPINSTINFO;

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

// static stuff in this file.
static LONG LOCAL AVIDrawOpen(LPICOPEN);
static LONG LOCAL AVIDrawClose(LPINSTINFO);
static LONG LOCAL AVIDrawGetInfo(LPICINFO, LONG);
static LONG LOCAL AVIDrawQuery(LPINSTINFO, LPBITMAPINFOHEADER);
static LONG LOCAL AVIDrawSuggestFormat(LPINSTINFO, LPICDRAWSUGGEST, LONG);
static LONG LOCAL AVIDrawBegin(LPINSTINFO, LPICDRAWBEGIN, LONG);
static LONG LOCAL AVIDraw(LPINSTINFO, LPICDRAW, LONG);
static LONG LOCAL AVIDrawEnd(LPINSTINFO);
static LONG LOCAL AVIDrawChangePalette(LPINSTINFO, LPBITMAPINFOHEADER);

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

LONG CALLBACK ICAVIDrawProc(
DWORD id,
HDRVR hDriver,
UINT uiMessage,
LPARAM lParam1,
LPARAM lParam2)
{
        LPINSTINFO pi = (LPINSTINFO)id;

        switch (uiMessage) {

                case DRV_LOAD:
                case DRV_FREE:
                        return 1L;

                //open
                case DRV_OPEN:
                        if (lParam2 == 0L)
                                return 1L;
                        else
                                return AVIDrawOpen((LPICOPEN)lParam2);

                //close
                case DRV_CLOSE:
                        return AVIDrawClose(pi);

                //Configure and Info messages
                case DRV_QUERYCONFIGURE:    // configuration from drivers applet
                        return 0;

                case DRV_CONFIGURE:
                        return 1;

                case ICM_CONFIGURE:
                case ICM_ABOUT:
                        return ICERR_UNSUPPORTED;

                case ICM_GETINFO:
                        return AVIDrawGetInfo((LPICINFO)lParam1, lParam2);

                //state messages
                case ICM_GETSTATE:
                case ICM_SETSTATE:
                        return 0L;

                //draw messages
                case ICM_DRAW_QUERY:
                        return AVIDrawQuery(pi, (LPBITMAPINFOHEADER)lParam1);

                case ICM_DRAW_SUGGESTFORMAT:
                        return AVIDrawSuggestFormat(pi, (LPICDRAWSUGGEST) lParam1, lParam2);

                case ICM_DRAW_BEGIN:
                        return AVIDrawBegin(pi, (LPICDRAWBEGIN) lParam1, lParam2);

                case ICM_DRAW_REALIZE:
                        pi->hdc = (HDC) lParam1;
                        if (!pi->hdc || !pi->hdd)
                                break;
                        return DrawDibRealize(pi->hdd, pi->hdc, (BOOL) lParam2);

                case ICM_DRAW_GET_PALETTE:
                        if (!pi->hdd)
                                break;
                        return (LONG) (UINT) DrawDibGetPalette(pi->hdd);

                case ICM_DRAW:
                        return AVIDraw(pi, (LPICDRAW)lParam1, lParam2);

                case ICM_DRAW_CHANGEPALETTE:
                        return AVIDrawChangePalette(pi, (LPBITMAPINFOHEADER) lParam1);

                case ICM_DRAW_END:
                        return AVIDrawEnd(pi);

                //standard driver messages
                case DRV_DISABLE:
                case DRV_ENABLE:
                        return 1L;

                case DRV_INSTALL:
                case DRV_REMOVE:
                        return 1L;
        }
        if (uiMessage < DRV_USER)
                return DefDriverProc(id,hDriver,uiMessage,lParam1,lParam2);
        else
                return ICERR_UNSUPPORTED;
}
/*****************************************************************************
 *
 * AVIDrawOpen() is called from the DRV_OPEN message
 *
 ****************************************************************************/

static LONG LOCAL AVIDrawOpen(
LPICOPEN icopen)
{
        LPINSTINFO pinst;

        // refuse to open if we are not being opened as a video draw device
        if (icopen->fccType != streamtypeVIDEO)
                return 0L;

        if (icopen->dwFlags == ICMODE_COMPRESS)
                return 0L;

        if (icopen->dwFlags == ICMODE_DECOMPRESS)
                return 0L;

        pinst = (LPINSTINFO)GlobalAllocPtr(GHND, sizeof(INSTINFO));

        if (!pinst) {
                icopen->dwError = ICERR_MEMORY;
                return 0L;
        }

        // init structure
        pinst->hdd = DrawDibOpen();

        // return success.
        icopen->dwError = ICERR_OK;

        return (LONG)pinst;
}
/*****************************************************************************
 *
 * AVIDrawClose() is called on the DRV_CLOSE message.
 *
 ****************************************************************************/
static LONG LOCAL AVIDrawClose(
LPINSTINFO pi)
{
        if (pi->hdd)
                DrawDibClose(pi->hdd);

        if (pi->lpBuffer)
                GlobalFreePtr(pi->lpBuffer);

        GlobalFreePtr(pi);
        return 1L;
}
/*****************************************************************************
 *
 * AVIDrawGetInfo() implements the ICM_GETINFO message
 *
 ****************************************************************************/
static LONG LOCAL AVIDrawGetInfo(
LPICINFO icinfo,
LONG lSize)
{
        if (icinfo == NULL)
                return sizeof(ICINFO);

        if (lSize < sizeof(ICINFO))
                return 0L;

        icinfo->dwSize          = sizeof(ICINFO);
        icinfo->fccType         = ICTYPE_VIDEO;
        icinfo->fccHandler      = FOURCC_AVIDraw;
        icinfo->dwFlags         = VIDCF_DRAW;
        icinfo->dwVersion       = VERSION_AVIDraw;
        icinfo->dwVersionICM    = ICVERSION;
        MultiByteToWideChar(CP_ACP, 0, szDescription, -1, icinfo->szDescription, sizeof(icinfo->szDescription));
        MultiByteToWideChar(CP_ACP, 0, szName, -1, icinfo->szDescription, sizeof(icinfo->szDescription));
        return sizeof(ICINFO);
}
/*****************************************************************************
 *
 * AVIDrawQuery() implements ICM_DRAW_QUERY
 *
 ****************************************************************************/
static LONG LOCAL AVIDrawQuery(
LPINSTINFO pi,
LPBITMAPINFOHEADER lpbiIn)
{
        // determine if the input DIB data is in a format we like.
        if (lpbiIn == NULL)
                return ICERR_BADFORMAT;

        // determine if the input DIB data is in a format we like.
        if (lpbiIn->biCompression != BI_RGB)
                return ICERR_BADFORMAT;

        return ICERR_OK;
}
/*****************************************************************************
 *
 * AVIDrawSuggestFormat() implements ICM_DRAW_SUGGESTFORMAT
 *
 ****************************************************************************/

static LONG LOCAL AVIDrawSuggestFormat(
LPINSTINFO pi,
LPICDRAWSUGGEST lpicd,
LONG cbicd)
{
        HIC hic;
        LONG l;

        if (lpicd->lpbiSuggest == NULL)
                return sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);

        // Call COMPMAN to get a good format to display data in....
        hic = ICGetDisplayFormat(NULL, lpicd->lpbiIn, lpicd->lpbiSuggest,
                                                0, lpicd->dxDst, lpicd->dyDst);

        if (hic)
                ICClose(hic);

        l = lpicd->lpbiSuggest->biClrUsed;

        if (lpicd->lpbiSuggest)
                if (lpicd->lpbiSuggest->biCompression == BI_RLE8)
                        lpicd->lpbiSuggest->biCompression = BI_RGB;

        // !!! Should check this format here to make sure it's RGB...
        // !!! If not, we could force it to 8-bit....

        l = l * sizeof(RGBQUAD);
        return l + sizeof(BITMAPINFOHEADER);
}

/*****************************************************************************
 *
 * AVIDrawBegin() implements ICM_DRAW_BEGIN
 *
 ****************************************************************************/

static LONG LOCAL AVIDrawBegin(
LPINSTINFO pi,
LPICDRAWBEGIN lpicd,
LONG cbicd)
{
        LONG l = AVIDrawQuery(pi, lpicd->lpbi);

        if ((l != 0) || (lpicd->dwFlags & ICDRAW_QUERY))
                return l;

        // Copy over whatever we want to remember
        pi->hdc = lpicd->hdc;
        pi->xDst = lpicd->xDst;
        pi->yDst = lpicd->yDst;
        pi->dxDst = lpicd->dxDst;
        pi->dyDst = lpicd->dyDst;
        pi->xSrc = lpicd->xSrc;
        pi->ySrc = lpicd->ySrc;
        pi->dxSrc = lpicd->dxSrc;
        pi->dySrc = lpicd->dySrc;

        SetStretchBltMode(pi->hdc, COLORONCOLOR);

        if (!DrawDibBegin(
                        pi->hdd,
                        pi->hdc,
                        pi->dxDst,
                        pi->dyDst,
                        lpicd->lpbi,
                        pi->dxSrc,
                        pi->dySrc,
                        0)) {  // !!! Flags?
                return ICERR_UNSUPPORTED;
        }

        // !!! error check

        // Allocate a buffer for the scrambled picture
        if (pi->lpBuffer)
                GlobalFreePtr(pi->lpBuffer);

        pi->lpBuffer = GlobalAllocPtr(GMEM_MOVEABLE, lpicd->lpbi->biSizeImage);

        if (!pi->lpBuffer)
                return ICERR_MEMORY;

        return ICERR_OK;
}
/*****************************************************************************
 *
 * AVIDraw() implements ICM_DRAW
 *
 ****************************************************************************/

static LONG LOCAL AVIDraw(
LPINSTINFO pi,
LPICDRAW lpicd,
LONG cbicd)
{
        UINT wFlags = DDF_SAME_HDC;

        if ((lpicd->dwFlags & ICDRAW_NULLFRAME) || lpicd->lpData == NULL) {
                if (lpicd->dwFlags & ICDRAW_UPDATE)
                        wFlags |= DDF_UPDATE;
                else
                        return ICERR_OK;            // nothing to draw
        }

        if (lpicd->dwFlags & ICDRAW_PREROLL)
                wFlags |= DDF_DONTDRAW;

        if (lpicd->dwFlags & ICDRAW_HURRYUP)
                wFlags |= DDF_HURRYUP;

        // This is the only part that actually has to do with the puzzle:
        // Mix up the picture into our extra buffer.
        if (lpicd->lpData)
                MixPicture(&gPuzzle, lpicd->lpFormat, lpicd->lpData, pi->lpBuffer);

        if (!DrawDibDraw(
                        pi->hdd,
                        pi->hdc,
                        pi->xDst,
                        pi->yDst,
                        pi->dxDst,
                        pi->dyDst,
                        lpicd->lpFormat,
                        pi->lpBuffer,
                        pi->xSrc,
                        pi->ySrc,
                        pi->dxSrc,
                        pi->dySrc,
                        wFlags)) {
                if (wFlags & DDF_UPDATE)
                        return ICERR_CANTUPDATE;
                else
                        return ICERR_UNSUPPORTED;
        }

        return ICERR_OK;
}
/*****************************************************************************
 *
 * AVIDrawChangePalette() implements ICM_DRAW_CHANGE_PALETTE
 *
 ****************************************************************************/

static LONG LOCAL AVIDrawChangePalette(
LPINSTINFO pi,
LPBITMAPINFOHEADER lpbi)
{
        PALETTEENTRY ape[256];
        LPRGBQUAD lprgb = (LPRGBQUAD) ((LPBYTE) lpbi + lpbi->biSize);
        int i;

        for (i = 0; i < (int) lpbi->biClrUsed; i++) {
                ape[i].peRed = lprgb[i].rgbRed;
                ape[i].peGreen = lprgb[i].rgbGreen;
                ape[i].peBlue = lprgb[i].rgbBlue;
                ape[i].peFlags = 0;
        }

        DrawDibChangePalette(pi->hdd, 0, (int) lpbi->biClrUsed, (LPPALETTEENTRY)ape);
        return ICERR_OK;
}
/*****************************************************************************
 *
 * AVIDrawEnd() implements ICM_DRAW_END
 *
 ****************************************************************************/

static LONG LOCAL AVIDrawEnd(
LPINSTINFO pi)
{
        // Note: do not call DrawDibEnd here, as we still may be asked to
        // update our current display, and calling DrawDibEnd would wipe
        // that out.

        return ICERR_OK;
}
