/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "bagel/hodjnpodj/hnplibs/stdafx.h"
#include "bagel/hodjnpodj/hnplibs/dibapi.h"
#include "bagel/hodjnpodj/hnplibs/bitmaps.h"

namespace Bagel {
namespace HodjNPodj {

CBitmap *FetchScreenBitmap(CDC *pDC, CPalette *pPalette, const int x, const int y, const int dx, const int dy) {
	bool        bSuccess = false;
	CDC         *pWorkDC = nullptr;
	CPalette    *pPalOld = nullptr,
	             *pPalOldWork = nullptr;
	CBitmap     *pWork = nullptr,
	             *pWorkOld = nullptr;

	if (pPalette != nullptr) {                                 // map in color palette to be used
		pPalOld = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	pWorkDC = new CDC();                                    // create the context and bitmap objects
	pWork = new CBitmap();

	if ((pWorkDC != nullptr) &&                                // construct an offscreen bitmap that we
	        (pWork != nullptr) &&                                  // ... can use as a work area, and then
	        (*pWorkDC).CreateCompatibleDC(pDC) &&               // ... use as a return value to the caller
	        (*pWork).CreateCompatibleBitmap(pDC, dx, dy)) {     // create a bitmap of the appropriate size
		if (pPalette != nullptr) {                             // map the palette into the work area
			pPalOldWork = (*pWorkDC).SelectPalette(pPalette, false);
			(*pWorkDC).RealizePalette();
		}
		pWorkOld = (*pWorkDC).SelectObject(pWork);          // now map in the work area's bitmap
		if (pWorkOld != nullptr)                               // capture the desired pixels
			bSuccess = (*pWorkDC).BitBlt(0, 0, dx, dy, pDC, x, y, SRCCOPY);
	}

	if (pPalOld != nullptr)                                    // relinquish the resources we built
		(*pDC).SelectPalette(pPalOld, false);
	if (pWorkOld != nullptr)
		(*pWorkDC).SelectObject(pWorkOld);
	if (pPalOldWork != nullptr)
		(*pWorkDC).SelectPalette(pPalOldWork, false);
	if (!bSuccess &&                                        // release the bitmap result we built
	        (pWork != nullptr)) {                                  // ... if something failed
		(*pWork).DeleteObject();
		delete pWork;
		pWork = nullptr;
	}
	if (pWorkDC != nullptr) {
		(*pWorkDC).DeleteDC();
		delete pWorkDC;
	}

	return pWork;
}

CBitmap *FetchBitmap(CDC *pDC, CPalette **pPalette, const char *pszPathName) {
	CDibDoc *pDIB;  // Pointer to our loaded DIB file
	HDIB hDIB = nullptr;
	CBitmap *pBitmap = nullptr;
	CPalette *pThisPalette = nullptr;

	pDIB = new CDibDoc();                               // create an object for our DIB

	if ((pDIB != nullptr) &&                               // verify we have the objects we just created
	        (*pDIB).OpenDocument(pszPathName)) {            // .... bitmap file
		pThisPalette = (*pDIB).GetDocPalette();
		hDIB = (*pDIB).GetHDIB();
		pBitmap = ConvertDIB(pDC, hDIB, pThisPalette);
		if (pPalette != nullptr)
			*pPalette = (*pDIB).DetachPalette();
	}

	delete pDIB;

	return pBitmap;
}

CBitmap *FetchBitmap(CDC *pDC, CPalette *pPalette, const char *pszPathName) {
	CDibDoc     *pDIB = nullptr;                               // pointer to our loaded DIB file
	HDIB        hDIB = nullptr;
	CBitmap     *pBitmap = nullptr;

	pDIB = new CDibDoc();                               // create an object for our DIB

	if ((pDIB != nullptr) &&                               // verify we have the objects we just created
	        (*pDIB).OpenDocument(pszPathName)) {            // .... bitmap file
		hDIB = (*pDIB).GetHDIB();
		pBitmap = ConvertDIB(pDC, hDIB, pPalette);
	}

	if (pDIB != nullptr)
		delete pDIB;

	return pBitmap;
}

CBitmap *FetchResourceBitmap(CDC *pDC, CPalette **pPalette, const char *pszName) {
	CDibDoc     *pDIB = nullptr;                               // pointer to our loaded DIB file
	HDIB        hDIB = nullptr;
	CBitmap     *pBitmap = nullptr;
	CPalette    *pThisPalette = nullptr;

	pDIB = new CDibDoc();                               // create an object for our DIB

	if ((pDIB != nullptr) &&                               // verify we have the objects we just created
	        (*pDIB).OpenResourceDocument(pszName)) {        // .... bitmap file
		pThisPalette = (*pDIB).GetDocPalette();
		hDIB = (*pDIB).GetHDIB();
		pBitmap = ConvertDIB(pDC, hDIB, pThisPalette);
		if (pPalette != nullptr)
			*pPalette = (*pDIB).DetachPalette();
	}

	if (pDIB != nullptr)
		delete pDIB;

	return pBitmap;
}

CBitmap *FetchResourceBitmap(CDC *pDC, CPalette **pPalette, const int nResID) {
	CDibDoc     *pDIB = nullptr;                               // pointer to our loaded DIB file
	HDIB        hDIB = nullptr;
	CBitmap     *pBitmap = nullptr;
	char        chResName[16];
	CPalette    *pThisPalette = nullptr;

	Common::sprintf_s(chResName, "#%d", nResID);

	pDIB = new CDibDoc();                               // create an object for our DIB

	if ((pDIB != nullptr) &&                               // verify we have the objects we just created
	        (*pDIB).OpenResourceDocument(chResName)) {      // .... bitmap file
		pThisPalette = (*pDIB).GetDocPalette();
		hDIB = (*pDIB).GetHDIB();
		pBitmap = ConvertDIB(pDC, hDIB, pThisPalette);
		if (pPalette != nullptr)
			*pPalette = (*pDIB).DetachPalette();
	}

	if (pDIB != nullptr)
		delete pDIB;

	return pBitmap;
}

CBitmap *ExtractBitmap(CDC *pDC, CBitmap *pBitmap, CPalette *pPalette, const int x, const int y, const int dx, const int dy) {
	bool        bSuccess = false;
	CDC         *pWorkDC = nullptr,
	             *pBaseDC = nullptr;
	CPalette    *pPalOld = nullptr,
	             *pPalOldWork = nullptr,
	              *pPalOldBase = nullptr;
	CBitmap     *pWork = nullptr,
	             *pWorkOld = nullptr,
	              *pBaseOld = nullptr;

	if (pPalette != nullptr) {                                 // map in color palette to be used
		pPalOld = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	pWorkDC = new CDC();                                    // create the context and bitmap objects
	pWork = new CBitmap();
	pBaseDC = new CDC();

	if ((pWorkDC != nullptr) &&                                // construct an offscreen bitmap that we
	        (pWork != nullptr) &&                                  // ... can use as a work area, and then
	        (pBaseDC != nullptr) &&                                // ... use as a return value to the caller
	        (*pWorkDC).CreateCompatibleDC(pDC) &&               // setup a context for the source bitmap
	        (*pBaseDC).CreateCompatibleDC(pDC) &&
	        (*pWork).CreateCompatibleBitmap(pDC, dx, dy)) {     // create a bitmap of the appropriate size
		if (pPalette != nullptr) {                             // map the palette into the contexts
			pPalOldWork = (*pWorkDC).SelectPalette(pPalette, false);
			(*pWorkDC).RealizePalette();
			pPalOldBase = (*pBaseDC).SelectPalette(pPalette, false);
			(*pBaseDC).RealizePalette();
		}
		pWorkOld = (*pWorkDC).SelectObject(pWork);          // now map in the work area's bitmap
		pBaseOld = (*pBaseDC).SelectObject(pBitmap);        // ... as well as the source bitmap
		if ((pWorkOld != nullptr) &&
		        (pBaseOld != nullptr))                             // grab the bitmap section if all is well
			bSuccess = (*pWorkDC).BitBlt(0, 0, dx, dy, pBaseDC, x, y, SRCCOPY);
	}

	if (pPalOld != nullptr)                                    // relinquish the resources we built
		(*pDC).SelectPalette(pPalOld, false);
	if (pWorkOld != nullptr)
		(*pWorkDC).SelectObject(pWorkOld);
	if (pBaseOld != nullptr)
		(*pBaseDC).SelectObject(pBaseOld);
	if (pPalOldWork != nullptr)
		(*pWorkDC).SelectPalette(pPalOldWork, false);
	if (pPalOldBase != nullptr)
		(*pBaseDC).SelectPalette(pPalOldBase, false);
	if (!bSuccess &&                                        // release the bitmap result we built
	        (pWork != nullptr)) {                                  // ... if something failed
		(*pWork).DeleteObject();
		delete pWork;
		pWork = nullptr;
	}
	if (pWorkDC != nullptr) {
		(*pWorkDC).DeleteDC();
		delete pWorkDC;
	}
	if (pBaseDC != nullptr) {
		(*pBaseDC).DeleteDC();
		delete pBaseDC;
	}

	return pWork;
}


/*************************************************************************
 *
 * BltBitmap()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  CBitmap *pBitmap        pointer to bitmap to be painted
 *  CRect *pSrcRect         source rectange in bitmap
 *  CRect *pDstRect         destination rectange in context
 *  int nMode               blt mode for transfer
 *
 * Return Value:
 *  bool            success / failure return
 *
 * Description:     paint a rectangular area of the screen from a CBitmap object.
 *
 ************************************************************************/

bool BltBitmap(CDC *pDC, CPalette *pPalette, CBitmap *pBitmap, CRect *pSrcRect, CRect *pDstRect, uint32 dwMode) {
	bool        bSuccess = false;
	CDC         *pWorkDC = nullptr;
	CPalette    *pPalOld = nullptr,
	             *pPalOldWork = nullptr;
	CBitmap     *pWorkOld = nullptr;

	if (pPalette != nullptr) {                                 // map in color palette to be used
		pPalOld = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	pWorkDC = new CDC();                                    // create the context and bitmap objects

	if ((pWorkDC != nullptr) &&                                // verify we got what we asked for
	        (pBitmap != nullptr) &&
	        (*pWorkDC).CreateCompatibleDC(pDC)) {               // create a context for our bitmap
		if (pPalette != nullptr) {                             // map the palette into the context
			pPalOldWork = (*pWorkDC).SelectPalette(pPalette, false);
			(*pWorkDC).RealizePalette();
		}
		pWorkOld = (*pWorkDC).SelectObject(pBitmap);        // now map in our bitmap
		if (pWorkOld != nullptr) {                             // paint back the saved pixels
			if ((((*pSrcRect).right - (*pSrcRect).left) != ((*pDstRect).right - (*pDstRect).left)) ||
			        (((*pSrcRect).bottom - (*pSrcRect).top) != ((*pDstRect).bottom - (*pDstRect).top))) {
				(*pDC).SetStretchBltMode(STRETCH_DELETESCANS);
				assert(GetDeviceCaps((*pDC).m_hDC, RASTERCAPS) & RC_STRETCHBLT);
				bSuccess = (*pDC).StretchBlt((*pDstRect).left,
					(*pDstRect).top,
					(*pDstRect).right - (*pDstRect).left,
					(*pDstRect).bottom - (*pDstRect).top,
					pWorkDC,
					(*pSrcRect).left,
					(*pSrcRect).top,
					(*pSrcRect).right - (*pSrcRect).left,
					(*pSrcRect).bottom - (*pSrcRect).top,
					dwMode);
			} else {
				bSuccess = (*pDC).BitBlt((*pDstRect).left,
					(*pDstRect).top,
					(*pSrcRect).right - (*pSrcRect).left,
					(*pSrcRect).bottom - (*pSrcRect).top,
					pWorkDC,
					(*pSrcRect).left,
					(*pSrcRect).top,
					dwMode);
			}
		}
	}

	if (pPalOld != nullptr)                                    // relinquish the resources we built
		(*pDC).SelectPalette(pPalOld, false);
	if (pWorkOld != nullptr)
		(*pWorkDC).SelectObject(pWorkOld);
	if (pPalOldWork != nullptr)
		(*pWorkDC).SelectPalette(pPalOldWork, false);
	if (pWorkDC != nullptr) {
		(*pWorkDC).DeleteDC();
		delete pWorkDC;
	}

	return bSuccess;
}


/*************************************************************************
 *
 * BltMaskedBitmap()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  CBitmap *pBitmap        pointer to bitmap to be displayed
 *  int x, y                upper lefthand corner of the destination rectangle
 *  CRect *pSrcRect         source rectange in bitmap
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     "mask" paint a bitmap to the specified display context.  White
 *                  pixels are assumed to be transparent, allowing the background
 *                  artwork to show through.
 *
 ************************************************************************/

bool BltMaskedBitmap(CDC *pDC, CPalette *pPalette, CBitmap *pBitmap, CRect *pSrcRect, const int x, const int y) {
	CDC         *pImageDC = nullptr,                       // context for the bitmap image
	             *pWorkDC = nullptr,                        // context for the bitmap work area
	              *pMaskDC = nullptr;                        // context for the image's mask
	CBitmap     *pImage = nullptr,                         // bitmap that has our image
	             *pImageOld = nullptr,                      // bitmap previously mapped to image context
	              *pWork = nullptr,                          // work area bitmap
	               *pWorkOld = nullptr,                       // bitmap previously mapped to work context
	                *pMask = nullptr,                          // image mask bitmap
	                 *pMaskOld = nullptr;                       // bitmap previously mapped to mask context
	CPalette    *pPalOldImage = nullptr,                   // palette previously mapped to image context
	             *pPalOldWork = nullptr;                    // palette previously mapped to work context
	bool        bSuccess = false;
	CPalette    *pPalOld = nullptr;
	CSize       cSize;
	int         dx, dy;
	CRect       dstRect;

	if (pPalette != nullptr) {
		pPalOld = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	pImageDC = new CDC();                               // create the necessary objects
	pMaskDC = new CDC();
	pWorkDC = new CDC();
	pImage = new CBitmap();
	pMask = new CBitmap();
	pWork = new CBitmap();

	if ((pImageDC != nullptr) &&                               // verify we got the requested objects
	        (pMaskDC != nullptr) &&
	        (pWorkDC != nullptr) &&
	        (pImage != nullptr) &&
	        (pMask != nullptr) &&
	        (pWork != nullptr) &&
	        (*pImageDC).CreateCompatibleDC(pDC) &&              // now create all the compatible contexts
	        (*pMaskDC).CreateCompatibleDC(pDC) &&               // ... that we need to hold our bitmaps
	        (*pWorkDC).CreateCompatibleDC(pDC)) {

		dx = (*pSrcRect).right - (*pSrcRect).left;
		dy = (*pSrcRect).bottom - (*pSrcRect).top;

		if (pPalette != nullptr) {                     // map in the color palette if specified
			pPalOldImage = (*pImageDC).SelectPalette(pPalette, false);
			(*pImageDC).RealizePalette();
			pPalOldWork = (*pWorkDC).SelectPalette(pPalette, false);
			(*pWorkDC).RealizePalette();
		}

		if ((*pImage).CreateCompatibleBitmap(pDC, dx, dy) &&
		        (*pWork).CreateCompatibleBitmap(pDC, dx, dy) &&
		        (*pMask).CreateBitmap(dx, dy, 1, 1, nullptr)) {

			pImageOld = (*pImageDC).SelectObject(pImage);
			pMaskOld = (*pMaskDC).SelectObject(pMask);  // map the work and mask bitmaps into
			pWorkOld = (*pWorkDC).SelectObject(pWork);  // ... their contexts

			dstRect.SetRect(0, 0, dx, dy);
			BltBitmap(pImageDC, pPalette, pBitmap, pSrcRect, &dstRect, (uint32) SRCCOPY);

			if ((pMaskOld != nullptr) &&                   // verify so far so good
			        (pWorkOld != nullptr)) {
				(*pWorkDC).BitBlt(     // grab what the background looks like
				    0,                              // ... putting it in the work area
				    0,
				    dx,
				    dy,
				    pDC,
				    x,
				    y,
				    SRCCOPY);
				(*pMaskDC).BitBlt(     // create the mask by inverting the image
				    0, 0,                           // ... i.e. the pixels occupied by the image
				    dx,
				    dy,
				    pImageDC,
				    0, 0,
				    NOTSRCCOPY);
				(*pImageDC).BitBlt(     // remove the image's "transparent" white area
				    0, 0,                           // ... i.e. its background becomes black
				    dx,
				    dy,
				    pMaskDC,
				    0, 0,
				    SRCAND);
				(*pMaskDC).BitBlt(     // invert the mask
				    0, 0,
				    dx,
				    dy,
				    pMaskDC,
				    0, 0,
				    DSTINVERT);
				(*pWorkDC).BitBlt(     // remove the image's space from the work area
				    0, 0,                           // ... i.e. the pixels where the image will go
				    dx,
				    dy,
				    pMaskDC,
				    0, 0
				    , SRCAND);
				(*pWorkDC).BitBlt(     // paint the image into the cleared (black) space
				    0, 0,                           // ... we made in the work area
				    dx,
				    dy,
				    pImageDC,
				    0, 0,
				    SRCPAINT);
				bSuccess = (*pDC).BitBlt(    // now splat the result to the destination context
				               x,
				               y,
				               dx,
				               dy,
				               pWorkDC,
				               0,
				               0,
				               SRCCOPY);
			}
		}
	}

	if (pPalOld != nullptr)
		(*pDC).SelectPalette(pPalOld, false);

	if (pImageOld != nullptr)                              // select out bitmaps out of their contexts
		(*pImageDC).SelectObject(pImageOld);
	if (pWorkOld != nullptr)
		(*pWorkDC).SelectObject(pWorkOld);
	if (pMaskOld != nullptr)
		(*pMaskDC).SelectObject(pMaskOld);

	if (pPalOldImage != nullptr)                           // map out the palettes we used
		(*pImageDC).SelectPalette(pPalOldImage, false);
	if (pPalOldWork != nullptr)
		(*pWorkDC).SelectPalette(pPalOldWork, false);

	if (pImage != nullptr) {                                // delete the bitmaps we created
		(*pImage).DeleteObject();
		delete pImage;
	}
	if (pWork != nullptr) {
		(*pWork).DeleteObject();
		delete pWork;
	}
	if (pMask != nullptr) {
		(*pMask).DeleteObject();
		delete pMask;
	}

	if (pImageDC != nullptr) {                             // delete the contexts we created
		(*pImageDC).DeleteDC();
		delete pImageDC;
	}
	if (pWorkDC != nullptr) {
		(*pWorkDC).DeleteDC();
		delete pWorkDC;
	}
	if (pMaskDC != nullptr) {
		(*pMaskDC).DeleteDC();
		delete pMaskDC;
	}

	return bSuccess;                                  // return success or failure
}


/*************************************************************************
 *
 * PaintBitmap()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  CBitmap *pBitmap        pointer to bitmap to be painted
 *  int x, y                upper left hand corner where bitmap is to be painted
 *  int dx, dy              optional sizing info to force a stretch blt
 *
 * Return Value:
 *  bool            success / failure return
 *
 * Description:     paint a rectangular area of the screen from a CBitmap object.
 *
 ************************************************************************/

bool PaintBitmap(CDC *pDC, CPalette *pPalette, CBitmap *pBitmap, const int x, const int y, const int dx, const int dy) {
	bool        bSuccess = false;
	CDC         *pWorkDC = nullptr;
	CPalette    *pPalOld = nullptr,
	             *pPalOldWork = nullptr;
	CBitmap     *pWorkOld = nullptr;
	BITMAP      cBitmapData;
	CRect       SrcRect, DstRect;

	(*pBitmap).GetObject(sizeof(BITMAP), &cBitmapData);

	if (pPalette != nullptr) {                                 // map in color palette to be used
		pPalOld = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	pWorkDC = new CDC();                                    // create the context and bitmap objects

	if ((*pWorkDC).CreateCompatibleDC(pDC)) {               // create a context for our bitmap
		if (pPalette != nullptr) {                             // map the palette into the context
			pPalOldWork = (*pWorkDC).SelectPalette(pPalette, false);
			(*pWorkDC).RealizePalette();
		}
		pWorkOld = (*pWorkDC).SelectObject(pBitmap);        // now map in our bitmap
		if (pWorkOld != nullptr) {                             // paint back the saved pixels
			if ((dx != 0) && (dy != 0) &&
			        ((dx != cBitmapData.bmWidth) || (dy != cBitmapData.bmHeight))) {
				(*pDC).SetStretchBltMode(STRETCH_DELETESCANS);
				assert(GetDeviceCaps((*pDC).m_hDC, RASTERCAPS) & RC_STRETCHBLT);
				bSuccess = (*pDC).StretchBlt(x, y, dx, dy, pWorkDC, 0, 0, cBitmapData.bmWidth, cBitmapData.bmHeight, SRCCOPY);
			} else {
				bSuccess = (*pDC).BitBlt(x, y, cBitmapData.bmWidth, cBitmapData.bmHeight, pWorkDC, 0, 0, SRCCOPY);
			}
		}
	}

	if (pPalOld != nullptr)                                    // relinquish the resources we built
		(*pDC).SelectPalette(pPalOld, false);
	if (pWorkOld != nullptr)
		(*pWorkDC).SelectObject(pWorkOld);
	if (pPalOldWork != nullptr)
		(*pWorkDC).SelectPalette(pPalOldWork, false);
	if (pWorkDC != nullptr) {
		(*pWorkDC).DeleteDC();
		delete pWorkDC;
	}

	return bSuccess;
}


/*************************************************************************
 *
 * PaintBitmap()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  char *pszPathName       pointer to path string for disk based DIB file
 *  int x, y                upper left hand corner where bitmap is to be painted
 *  int dx, dy              optional sizing info to force a stretch blt
 *
 * Return Value:
 *  bool            success / failure return
 *
 * Description:     paint a rectangular area of the screen from a disk based DIB.
 *
 ************************************************************************/

bool PaintBitmap(CDC *pDC, CPalette *pPalette, const char *pszPathName, const int x, const int y, const int dx, const int dy) {
	CDibDoc myDoc;
	HDIB    hDIB;
	CRect   rcDest;
	CRect   rcDIB;
	int     cxDIB;
	int     cyDIB;
	bool    bSuccess;

	bSuccess = myDoc.OpenDocument(pszPathName);
	if ((pDC != nullptr) && bSuccess) {
		hDIB = myDoc.GetHDIB();
		if (hDIB == nullptr)
			bSuccess = false;
		else {
			cxDIB = (int) DIBWidth(hDIB);
			cyDIB = (int) DIBHeight(hDIB);

			rcDIB.top = rcDIB.left = 0;
			rcDIB.right = cxDIB;
			rcDIB.bottom = cyDIB;
			if ((dx == 0) || (dy == 0))
				rcDest.SetRect(x, y, x + cxDIB, y + cyDIB);
			else
				rcDest.SetRect(x, y, x + dx, y + dy);
			bSuccess = PaintDIB((*pDC).m_hDC, &rcDest, hDIB, &rcDIB, pPalette);
		}
	}

	return bSuccess;
}


/*************************************************************************
 *
 * PaintMaskedDIB()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  char *pszPathName       pointer to path string for disk based DIB file
 *  int x, y                upper lefthand corner of the destination rectangle
 *  int dx, dy              optional sizing info to force a stretch blt
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     Read in a DDB bitmap from disk and "mask" paint it to the
 *                  specified display context.  White pixels are assumed to be
 *                  transparent, allowing the background artwork to show through.
 *
 ************************************************************************/

bool PaintMaskedDIB(CDC *pDC, CPalette *pPalette, const char *pszPathName, const int x, const int y, const int dx, const int dy) {
	bool    bSuccess = false;
	CDibDoc *pDIB = nullptr;                                   // pointer to our loaded DIB file

	pDIB = new CDibDoc();                               // create an object for our DIB

	if ((pDIB != nullptr) &&                               // verify we have the objects we just created
	        (*pDIB).OpenDocument(pszPathName))              // .... bitmap file
		bSuccess = PaintMaskedDIB(pDC, pPalette, pDIB, x, y, dx, dy) ;

	if (pDIB != nullptr)
		delete pDIB;

	return bSuccess;                                  // return success or failure
}


/*************************************************************************
 *
 * PaintMaskedResource()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  int resId               resource number for the bitmap in the .RC file
 *  int x, y                upper lefthand corner of the destination rectangle
 *  int dx, dy              optional sizing info to force a stretch blt
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     Read in a DDB bitmap resource and "mask" paint it to the
 *                  specified display context.  White pixels are assumed to be
 *                  transparent, allowing the background artwork to show through.
 *
 ************************************************************************/

bool PaintMaskedResource(CDC *pDC, CPalette *pPalette, const int nResID, const int x, const int y, const int dx, const int dy) {
	bool    bSuccess = false;
	CDibDoc *pDIB = nullptr;                                   // pointer to our loaded DIB file
	char    chResName[16];

	Common::sprintf_s(chResName, "#%d", nResID);

	pDIB = new CDibDoc();                               // create an object for our DIB

	if ((pDIB != nullptr) &&                               // verify we have the objects we just created
	        (*pDIB).OpenResourceDocument(chResName))        // .... bitmap file
		bSuccess = PaintMaskedDIB(pDC, pPalette, pDIB, x, y, dx, dy) ;

	if (pDIB != nullptr)
		delete pDIB;

	return bSuccess;                                  // return success or failure
}



/*************************************************************************
 *
 * PaintMaskedResource()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  char *pszName           resource name for the bitmap in the .RC file
 *  int x, y                upper lefthand corner of the destination rectangle
 *  int dx, dy              optional sizing info to force a stretch blt
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     Read in a DDB bitmap resource and "mask" paint it to the
 *                  specified display context.  White pixels are assumed to be
 *                  transparent, allowing the background artwork to show through.
 *
 ************************************************************************/

bool PaintMaskedResource(CDC *pDC, CPalette *pPalette, const char *pszName, const int x, const int y, const int dx, const int dy) {
	bool    bSuccess = false;
	CDibDoc *pDIB = nullptr;                                   // pointer to our loaded DIB file

	pDIB = new CDibDoc();                               // create an object for our DIB

	if ((pDIB != nullptr) &&                               // verify we have the objects we just created
	        (*pDIB).OpenResourceDocument(pszName))          // .... bitmap file
		bSuccess = PaintMaskedDIB(pDC, pPalette, pDIB, x, y, dx, dy) ;

	if (pDIB != nullptr)
		delete pDIB;

	return bSuccess;                                  // return success or failure
}


/*************************************************************************
 *
 * PaintMaskedDIB()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  CDibDoc *pDIB           pointer to DIB to be displayed
 *  int x, y                upper lefthand corner of the destination rectangle
 *  int dx, dy              optional sizing info to force a stretch blt
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     "mask" paint a DIB to the specified display context.  White
 *                  pixels are assumed to be transparent, allowing the background
 *                  artwork to show through.
 *
 ************************************************************************/

bool PaintMaskedDIB(CDC *pDC, CPalette *pPalette, CDibDoc *pDIB, const int x, const int y, const int dx, const int dy) {
	bool        bSuccess = false;
	CBitmap     *pBitmap = nullptr;
	CPalette    *pMyPalette = nullptr;
	HDIB        hDIB = nullptr;

	if (pPalette == nullptr)
		pMyPalette = (*pDIB).GetDocPalette();
	else
		pMyPalette = pPalette;

	hDIB = (*pDIB).GetHDIB();
	pBitmap = ConvertDIB(pDC, hDIB, pMyPalette);

	if (pBitmap != nullptr) {
		bSuccess = PaintMaskedBitmap(pDC, pMyPalette, pBitmap, x, y, dx, dy);
		(*pBitmap).DeleteObject();
		delete pBitmap;
	}

	return bSuccess;
}


/*************************************************************************
 *
 * PaintMaskedBitmap()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  CBitmap *pBitmap        pointer to bitmap to be displayed
 *  int x, y                upper lefthand corner of the destination rectangle
 *  int dx, dy              optional sizing info to force a stretch blt
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     "mask" paint a bitmap to the specified display context.  White
 *                  pixels are assumed to be transparent, allowing the background
 *                  artwork to show through.
 *
 ************************************************************************/

bool PaintMaskedBitmap(CDC *pDC, CPalette *pPalette, CBitmap *pBitmap, const int x, const int y, const int dx, const int dy) {
	BITMAP      myBitmap;                               // area for bitmap info
	CDC         *pImageDC = nullptr,                       // context for the bitmap image
	             *pWorkDC = nullptr,                        // context for the bitmap work area
	              *pMaskDC = nullptr;                        // context for the image's mask
	CBitmap     *pImage = nullptr,                         // bitmap that has our image
	             *pImageOld = nullptr,                      // bitmap previously mapped to image context
	              *pWork = nullptr,                          // work area bitmap
	               *pWorkOld = nullptr,                       // bitmap previously mapped to work context
	                *pMask = nullptr,                          // image mask bitmap
	                 *pMaskOld = nullptr;                       // bitmap previously mapped to mask context
	CPalette    *pPalOldImage = nullptr,                   // palette previously mapped to image context
	             *pPalOldWork = nullptr;                    // palette previously mapped to work context
	bool        bSuccess = false;
	CPalette    *pPalOld = nullptr;
	CSize       cSize;

	if (pPalette != nullptr) {
		pPalOld = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	pImageDC = new CDC();                               // create the necessary objects
	pMaskDC = new CDC();
	pWorkDC = new CDC();
	pImage = new CBitmap();
	pMask = new CBitmap();
	pWork = new CBitmap();

	if ((pImageDC != nullptr) &&                               // verify we got the requested objects
	        (pMaskDC != nullptr) &&
	        (pWorkDC != nullptr) &&
	        (pImage != nullptr) &&
	        (pMask != nullptr) &&
	        (pWork != nullptr) &&
	        (*pImageDC).CreateCompatibleDC(pDC) &&              // now create all the compatible contexts
	        (*pMaskDC).CreateCompatibleDC(pDC) &&               // ... that we need to hold our bitmaps
	        (*pWorkDC).CreateCompatibleDC(pDC)) {

		(*pBitmap).GetObject(sizeof(BITMAP), &myBitmap);  // get the image's sizing info
		cSize.cx = myBitmap.bmWidth;
		cSize.cy = myBitmap.bmHeight;

		if ((dx != 0) && (dy != 0) &&
		        ((dx != cSize.cx) || (dy != cSize.cy))) {
			cSize.cx = dx;
			cSize.cy = dy;
		}

		if (pPalette != nullptr) {                     // map in the color palette if specified
			pPalOldImage = (*pImageDC).SelectPalette(pPalette, false);
			(*pImageDC).RealizePalette();
			pPalOldWork = (*pWorkDC).SelectPalette(pPalette, false);
			(*pWorkDC).RealizePalette();
		}

		if ((*pImage).CreateCompatibleBitmap(pDC, cSize.cx, cSize.cy) &&
		        (*pWork).CreateCompatibleBitmap(pDC, cSize.cx, cSize.cy) &&
		        (*pMask).CreateBitmap(cSize.cx, cSize.cy, 1, 1, nullptr)) {

			pImageOld = (*pImageDC).SelectObject(pImage);
			pMaskOld = (*pMaskDC).SelectObject(pMask);  // map the work and mask bitmaps into
			pWorkOld = (*pWorkDC).SelectObject(pWork);  // ... their contexts

			PaintBitmap(pImageDC, pPalette, pBitmap, 0, 0, dx, dy);

			if ((pMaskOld != nullptr) &&                   // verify so far so good
			        (pWorkOld != nullptr)) {
				(*pWorkDC).BitBlt(     // grab what the background looks like
				    0,                              // ... putting it in the work area
				    0,
				    cSize.cx,
				    cSize.cy,
				    pDC,
				    x,
				    y,
				    SRCCOPY);
				(*pMaskDC).BitBlt(     // create the mask by inverting the image
				    0, 0,                           // ... i.e. the pixels occupied by the image
				    cSize.cx,
				    cSize.cy,
				    pImageDC,
				    0, 0,
				    NOTSRCCOPY);
				(*pImageDC).BitBlt(     // remove the image's "transparent" white area
				    0, 0,                           // ... i.e. its background becomes black
				    cSize.cx,
				    cSize.cy,
				    pMaskDC,
				    0, 0,
				    SRCAND);
				(*pMaskDC).BitBlt(     // invert the mask
				    0, 0,
				    cSize.cx,
				    cSize.cy,
				    pMaskDC,
				    0, 0,
				    DSTINVERT);
				(*pWorkDC).BitBlt(     // remove the image's space from the work area
				    0, 0,                           // ... i.e. the pixels where the image will go
				    cSize.cx,
				    cSize.cy,
				    pMaskDC,
				    0, 0
				    , SRCAND);
				(*pWorkDC).BitBlt(     // paint the image into the cleared (black) space
				    0, 0,                           // ... we made in the work area
				    cSize.cx,
				    cSize.cy,
				    pImageDC,
				    0, 0,
				    SRCPAINT);
				bSuccess = (*pDC).BitBlt(    // now splat the result to the destination context
				               x,
				               y,
				               cSize.cx,
				               cSize.cy,
				               pWorkDC,
				               0,
				               0,
				               SRCCOPY);
			}
		}
	}

	if (pPalOld != nullptr)
		(*pDC).SelectPalette(pPalOld, false);

	if (pImageOld != nullptr)                              // select out bitmaps out of their contexts
		(*pImageDC).SelectObject(pImageOld);
	if (pWorkOld != nullptr)
		(*pWorkDC).SelectObject(pWorkOld);
	if (pMaskOld != nullptr)
		(*pMaskDC).SelectObject(pMaskOld);

	if (pPalOldImage != nullptr)                           // map out the palettes we used
		(*pImageDC).SelectPalette(pPalOldImage, false);
	if (pPalOldWork != nullptr)
		(*pWorkDC).SelectPalette(pPalOldWork, false);

	if (pImage != nullptr) {                                // delete the bitmaps we created
		(*pImage).DeleteObject();
		delete pImage;
	}
	if (pWork != nullptr) {
		(*pWork).DeleteObject();
		delete pWork;
	}
	if (pMask != nullptr) {
		(*pMask).DeleteObject();
		delete pMask;
	}

	if (pImageDC != nullptr) {                             // delete the contexts we created
		(*pImageDC).DeleteDC();
		delete pImageDC;
	}
	if (pWorkDC != nullptr) {
		(*pWorkDC).DeleteDC();
		delete pWorkDC;
	}
	if (pMaskDC != nullptr) {
		(*pMaskDC).DeleteDC();
		delete pMaskDC;
	}

	return bSuccess;                                  // return success or failure
}


/*************************************************************************
 *
 * PaintBlockEffect()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CBitmap *pBitmap        pointer to the DIB to be displayed
 *  CPalette *pPalette      pointer to color palette to be used for the DIB
 *  int nBlockSize          size of block to be painted at each step
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     randomly paint a DIB to the specified display context
 *                  a section/block at a time, without repeating any blocks;
 *                  the DIB is converted to a bitmap and then a companion
 *                  routine is called.
 *
 ************************************************************************/

#define BIT0    0x00000001
#define BIT2    0x00000004
#define BIT15   0x00008000
#define BIT16   0x00010000
#define BIT17   0x00020000
#define SEQ     (BIT17)
#define BLOCK   4


bool PaintBlockEffect(CDC *pDC, CDibDoc *pDIB, CPalette* pPalette, int nBlockSize) {
	HDIB    hDIB = nullptr;
	CBitmap *pBitmap = nullptr;
	bool    bSuccess = false;

	if ((pDC == nullptr) ||
	        (pDIB == nullptr))
		return false;

	hDIB = (*pDIB).GetHDIB();
	pBitmap = ConvertDIB(pDC, hDIB, pPalette);

	bSuccess = PaintBlockEffect(pDC, pBitmap, pPalette, nBlockSize);

	if (pBitmap != nullptr)
		delete pBitmap;

	return bSuccess;
}


/*************************************************************************
 *
 * PaintBlockEffect()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  CBitmap *pBitmap        pointer to bitmap to be displayed
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  int nBlockSize          size of block to be painted at each step
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     randomly paint a bitmap to the specified display context
 *                  a section/block at a time, without repeating blocks.
 *
 ************************************************************************/

bool PaintBlockEffect(CDC *pDC, CBitmap *pBitmap, CPalette* pPalette, int nBlockSize) {
	CPalette *pOldPal = nullptr;
	CPalette *pOldPal2 = nullptr;
	CBitmap  *pBitmapOld = nullptr;
	CDC      *pMemDC = nullptr;
	BITMAP   myBitmap;
	bool     bSuccess = false;
	unsigned long   seed;
	unsigned long   value, maxvalue;
	unsigned long   x, y;
	unsigned long   fill,
	         mask = (BIT16 | BIT15 | BIT2 | BIT0);
	unsigned long   tmp, cnt, shft;
	unsigned long   width, height;

	if ((pDC == nullptr) ||
	        (pBitmap == nullptr))
		return false;

	if (pPalette != nullptr) {
		pOldPal = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	pMemDC = new CDC();
	if ((pMemDC == nullptr) ||
	        ((*pMemDC).CreateCompatibleDC(pDC) == false))
		goto clean_up;

	pOldPal2 = (*pMemDC).SelectPalette(pPalette, false);
	(*pMemDC).RealizePalette();
	pBitmapOld = (*pMemDC).SelectObject(pBitmap);

	(*pBitmap).GetObject(sizeof(BITMAP), &myBitmap);
	width = myBitmap.bmWidth / nBlockSize;
	height = myBitmap.bmHeight;

	maxvalue = height / nBlockSize * width;
	seed = SEQ - 1;

	for (value = seed; ;) {
		fill = 0L;
		tmp = (value & mask);
		shft = BIT0;
		cnt = 0;
		while (shft < SEQ) {
			if (tmp & shft)
				cnt++;
			shft <<= 1;
		}
		if (cnt & BIT0)
			fill = BIT0;
		value <<= 1;
		value |= fill;
		value &= (SEQ - 1);
		if (value == seed)
			break;
		if (value > maxvalue)
			continue;
		y = (value / width) * nBlockSize;
		x = (value % width) * nBlockSize;
		(*pDC).BitBlt((uint16) x,
		              (uint16) y,
		              nBlockSize,
		              nBlockSize,
		              pMemDC,
		              (uint16) x,
		              (uint16) y,
		              SRCCOPY);
	}

	(*pDC).BitBlt(0,
	              0,
	              nBlockSize,
	              nBlockSize,
	              pMemDC,
	              0,
	              0,
	              SRCCOPY);

	bSuccess = true;

clean_up:
	if (pBitmapOld != nullptr)
		(*pMemDC).SelectObject(pBitmapOld);
	if (pOldPal2 != nullptr)
		(*pMemDC).SelectPalette(pOldPal2, false);
	if (pMemDC != nullptr)
		delete pMemDC;

	if (pOldPal != nullptr)
		(*pDC).SelectPalette(pOldPal, false);

	return bSuccess;
}


/*************************************************************************
 *
 * PaintBlockEffect()
 *
 * Parameters:
 *  CDC *pDC                pointer to device context to be used for display
 *  COLORREF                color to be displayed
 *  CPalette *pPalette      pointer to color palette to be used for the bitmap
 *  int nBlockSize          size of block to be painted at each step
 *
 * Return Value:
 *  bool                    success/failure condition
 *
 * Description:     randomly paint a solid background color to the specified
 *                  display context a section/block at a time, without repeating blocks.
 *
 ************************************************************************/

bool PaintBlockEffect(CDC *pDC, COLORREF rgbColor, CPalette* pPalette, int nBlockSize, int nX, int nY, int nWidth, int nHeight) {
	CBrush   myBrush;
	CRect    thisRect, fillRect;
	CPalette *pOldPal = nullptr;
	bool     bSuccess = false;
	unsigned long   seed;
	unsigned long   value, maxvalue;
	unsigned long   x, y;
	unsigned long   fill,
	         mask = (BIT16 | BIT15 | BIT2 | BIT0);
	unsigned long   tmp, cnt, shft;
	unsigned long   width, height;

	if (pDC == nullptr)
		return false;

	if (pPalette != nullptr) {
		pOldPal = (*pDC).SelectPalette(pPalette, false);
		(*pDC).RealizePalette();
	}

	myBrush.CreateSolidBrush(rgbColor);

	width = nWidth / nBlockSize;
	height = nHeight;

	maxvalue = height / nBlockSize * width;
	seed = SEQ - 1;

	for (value = seed; ;) {
		fill = 0L;
		tmp = (value & mask);
		shft = BIT0;
		cnt = 0;
		while (shft < SEQ) {
			if (tmp & shft)
				cnt++;
			shft <<= 1;
		}
		if (cnt & BIT0)
			fill = BIT0;
		value <<= 1;
		value |= fill;
		value &= (SEQ - 1);
		if (value == seed)
			break;
		if (value > maxvalue)
			continue;
		y = (value / width) * nBlockSize;
		x = (value % width) * nBlockSize;
		fillRect.SetRect(nX + x, nY + y, nX + x + nBlockSize, nY + y + nBlockSize);
		(*pDC).FillRect(&fillRect, &myBrush);
	}

	fillRect.SetRect(nX, nY, nX + nBlockSize, nY + nBlockSize);
	(*pDC).FillRect(&fillRect, &myBrush);

	bSuccess = true;

	if (pOldPal != nullptr)
		(*pDC).SelectPalette(pOldPal, false);

	return bSuccess;
}


/*************************************************************************
 *
 * GetBitmapSize()
 *
 * Parameters:
 *  CBitmap *pBitmap        pointer to bitmap
 *
 * Return Value:
 *  CSize           size of the bitmap
 *
 * Description:     get sizing information for a bitmap objext.
 *
 ************************************************************************/

CSize GetBitmapSize(CBitmap *pBitmap) {
	CSize   mySize;
	BITMAP  cBitmapData;

	(*pBitmap).GetObject(sizeof(BITMAP), &cBitmapData);
	mySize.cx = cBitmapData.bmWidth;
	mySize.cy = cBitmapData.bmHeight;
	return mySize;
}

} // namespace HodjNPodj
} // namespace Bagel
