#ifndef _COMMON_DIBITMAP_HPP_
#define _COMMON_DIBITMAP_HPP_
#ifndef _COMMON_WINDOWS_HPP_
#include <common/windows.hpp>
#endif
#ifndef _COMMON_BITMAPINFO_HPP_
#include <common/bminfo.hpp>
#endif
#ifndef _COMMON_PUREPALETTE_HPP_
#include <common/purepal.hpp>
#endif
#ifndef _COMMON_EXCEPTION_HPP_
#include <common/except.hpp>
#endif
#ifndef _COMMON_SMARTPOINTER_HPP_
#include <common/pointer.hpp>
#endif

class BitmapOverlay;
class Bitmap;
class ResBitmap;
class Vector2D;
class Point;

class DIBitmap : public BitmapInfo
{
public:
	DIBitmap(const PureDevice &pureDevice,DWORD bmWidth,DWORD bmHeight,const PurePalette &purePalette);
	DIBitmap(const PureDevice &pureDevice,const BitmapInfo &someBitmapInfo,const PurePalette &purePalette);
	DIBitmap(const BitmapInfo &someBitmapInfo);
  DIBitmap(const DIBitmap &someDIBitmap);
	virtual ~DIBitmap();
  DIBitmap &operator=(const DIBitmap &someDIBitmap);
	DIBitmap &operator+=(BitmapOverlay &someBitmapOverlay);
  bool overlay(Bitmap &bitmap,const Point &placementPoint);
  bool overlay(ResBitmap &resBitmap,const Point &placementPoint);
  bool overlay(DIBitmap &bitmap,const Point &placementPoint);
  bool getAt(SmartPointer<DIBitmap> &bitmap,const Rect &areaRect);
	void setByte(WORD row,WORD col,BYTE byteValue);
	BYTE getByte(WORD row,WORD col)const;
	void line(const Point &firstPoint,const Point &secondPoint,BYTE byteValue);
	void outlineRect(const Rect &outlineRect,BYTE byteValue=0);
	void colorRect(const Rect &rect,BYTE byteValue=0);
	WORD square(const Point &centerPoint,WORD lineLength,const RGBColor &lineColor);
  WORD square(const Point &centerPoint,WORD lineLength,BYTE indexColor);
  WORD circle(WORD aspectValue,const Point &xyPoint,WORD radius,WORD palIndex);
	WORD bitBlt(PureDevice &displayDevice,PureDevice::BltMode ropCode=PureDevice::SourceCopy);
  WORD bitBlt(PureDevice &displayDevice,const Rect &dstRect,const Point &srcPoint,PureDevice::BltMode ropCode=PureDevice::SourceCopy);
  WORD bitBlt(PureDevice &displayDevice,const Point &srcPoint,PureDevice::BltMode ropCode=PureDevice::SourceCopy);
  BOOL stretchBlt(PureDevice &displayDevice,const Rect &dstRect,PureDevice::BltMode ropCode=PureDevice::SourceCopy);
	void setBits(BYTE charByte);
	bool copyBits(unsigned char *ptrData,DWORD length);
	void clearBits(void);
	WORD size(DWORD newWidth,DWORD newHeight);
  void *ptrData(void);
	DWORD imageExtent(void)const;
	BOOL usePalette(BOOL usage);
  BOOL usePalette(PureDevice &pureDevice,BOOL usage);
	WORD isOkay(void)const;
	PurePalette &getPalette(void);
	PureDevice &getDevice(void);
	HBITMAP getBitmap(void);
private:
  bool overlay(const Point &placementPoint,int srcWidth,int srcHeight,UCHAR *pBmBits);
  void copyBits(unsigned char *lpDstPtr,unsigned char *lpSrcPtr,DWORD length)const;
	void createBitmap(const PureDevice &somePureDevice);
	void createBitmap(void);
	void destroyBitmap(void);
  void verifyDimensions(DWORD &desiredWidth,DWORD desiredHeight)const;

	HBITMAP mhBitmap;
	void *mlpBmBits;
	DWORD mImageExtent;
	PureDevice mBitmapDevice;
  PurePalette mBitmapPalette;
};

inline
DIBitmap::DIBitmap(const DIBitmap &someDIBitmap)
: mhBitmap(0), mImageExtent(0), mlpBmBits(0)
{
	*this=someDIBitmap;
}

inline
DIBitmap::DIBitmap(const PureDevice &somePureDevice,DWORD bmWidth,DWORD bmHeight,const PurePalette &purePalette)
: mhBitmap(0), mImageExtent(0), mlpBmBits(0)
{
	verifyDimensions(bmWidth,bmHeight);
	width(bmWidth);
	height(bmHeight);
  static_cast<BitmapInfo&>(*this)=purePalette;
	mBitmapPalette=purePalette;
	createBitmap(somePureDevice);
	if(!isOkay())return;
	mBitmapDevice.compatibleDevice(somePureDevice);
	mBitmapDevice.select(mhBitmap);
}

inline
DIBitmap::DIBitmap(const PureDevice &somePureDevice,const BitmapInfo &someBitmapInfo,const PurePalette &purePalette)
: mhBitmap(0), mImageExtent(0), mlpBmBits(0), BitmapInfo(someBitmapInfo)
{
	DWORD bmWidth(width());
  DWORD bmHeight(height());
	verifyDimensions(bmWidth,bmHeight);
	width(bmWidth);
	height(bmHeight);
	static_cast<BitmapInfo&>(*this)=purePalette;
	mBitmapPalette=purePalette;
	createBitmap(somePureDevice);
	if(!isOkay())return;
	mBitmapDevice.compatibleDevice(somePureDevice);
	mBitmapDevice.select(mhBitmap);
}

inline
DIBitmap::DIBitmap(const BitmapInfo &someBitmapInfo)
: mhBitmap(0), mImageExtent(0), mlpBmBits(0), BitmapInfo(someBitmapInfo)
{
	DWORD bmWidth(width());
  DWORD bmHeight(height());
	verifyDimensions(bmWidth,bmHeight);
	width(bmWidth);
	height(bmHeight);
	createBitmap();
	if(!isOkay())return;
}

inline
DIBitmap::~DIBitmap()
{
	if(isOkay())mBitmapDevice.select(mhBitmap,FALSE);
	destroyBitmap();
}

inline
DIBitmap &DIBitmap::operator=(const DIBitmap &someDIBitmap)
{
  destroyBitmap();
	(BitmapInfo&)*this=(BitmapInfo&)someDIBitmap;
	mBitmapPalette=((DIBitmap&)someDIBitmap).getPalette();
	createBitmap(someDIBitmap.mBitmapDevice);
	if(!isOkay())return *this;
  copyBits((unsigned char*)ptrData(),(unsigned char*)((DIBitmap&)someDIBitmap).ptrData(),imageExtent());
	mBitmapDevice.compatibleDevice(someDIBitmap.mBitmapDevice);
	mBitmapDevice.select(mhBitmap);
	return *this;
}

inline
void DIBitmap::destroyBitmap(void)
{
	if(!mhBitmap)return;
  mBitmapDevice.select(mhBitmap,FALSE);
	::DeleteObject(mhBitmap);
	mhBitmap=0;
	mlpBmBits=0;
	mImageExtent=0;
}

inline
void DIBitmap::setByte(WORD row,WORD col,BYTE byteValue)
{
	if(!isOkay()||row>=height()||col>=width())return;
	BYTE *lpImage=(BYTE*)mlpBmBits+width()*height();
	*((lpImage-((LONG)row*width()+width()))+(LONG)col)=byteValue;
}

inline
BYTE DIBitmap::getByte(WORD row,WORD col)const
{
	if(!isOkay()||row>=height()||col>=width())return (BYTE)FALSE;
	BYTE *lpImage=(BYTE*)mlpBmBits+width()*height();
	return *((lpImage-((LONG)row*width()+width()))+(LONG)col);
}

inline
WORD DIBitmap::bitBlt(PureDevice &displayDevice,PureDevice::BltMode ropCode)
{
	if(!isOkay())return FALSE;
	return ::BitBlt(displayDevice,0,0,width(),height(),mBitmapDevice,0,0,ropCode);
}

inline
WORD DIBitmap::bitBlt(PureDevice &displayDevice,const Point &srcPoint,PureDevice::BltMode ropCode)
{
	if(!isOkay())return FALSE;
	return ::BitBlt(displayDevice,0,0,width(),height(),mBitmapDevice,srcPoint.x(),srcPoint.y(),ropCode);
}

inline
WORD DIBitmap::bitBlt(PureDevice &displayDevice,const Rect &dstRect,const Point &srcPoint,PureDevice::BltMode ropCode)
{
	if(!isOkay())return FALSE;
	return ::BitBlt(displayDevice,dstRect.left(),dstRect.top(),dstRect.right(),dstRect.bottom(),mBitmapDevice,srcPoint.x(),srcPoint.y(),ropCode);
}

inline
BOOL DIBitmap::stretchBlt(PureDevice &displayDevice,const Rect &dstRect,PureDevice::BltMode ropCode)
{
  if(!isOkay())return FALSE;
	return ::StretchBlt(displayDevice,dstRect.left(),dstRect.top(),dstRect.right(),dstRect.bottom(),mBitmapDevice,0,0,width(),height(),ropCode);
}

inline
void *DIBitmap::ptrData(void)
{
	if(!mlpBmBits)throw(NullError());
	return mlpBmBits;
}

inline
DWORD DIBitmap::imageExtent(void)const
{
	return mImageExtent;
}

inline
WORD DIBitmap::square(const Point &centerPoint,WORD lineLength,const RGBColor &lineColor)
{
  return square(centerPoint,lineLength,mBitmapPalette.paletteIndex(lineColor));
}

inline
WORD DIBitmap::isOkay(void)const
{
  return (mhBitmap?TRUE:FALSE);
}

inline
PurePalette &DIBitmap::getPalette(void)
{
  return mBitmapPalette;
}

inline
PureDevice &DIBitmap::getDevice(void)
{
  return mBitmapDevice;
}

inline
HBITMAP DIBitmap::getBitmap(void)
{
  return mhBitmap;
}

inline
BOOL DIBitmap::usePalette(BOOL usage)
{
  if(!isOkay()||!mBitmapDevice.isOkay())return FALSE;
  mBitmapPalette.usePalette(mBitmapDevice,usage);
	return TRUE;
}

inline
BOOL DIBitmap::usePalette(PureDevice &pureDevice,BOOL usage)
{
  if(!isOkay())return FALSE;
	mBitmapPalette.usePalette(pureDevice,usage);
	return TRUE;
}
#endif
