#ifndef _COMMON_PUREDEVICE_HPP_
#define _COMMON_PUREDEVICE_HPP_
#ifndef _COMMON_RGBCOLOR_HPP_
#include <common/rgbcolor.hpp>
#endif
#ifndef _COMMON_STRING_HPP_
#include <common/string.hpp>
#endif
#ifndef _COMMON_PEN_HPP_
#include <common/pen.hpp>
#endif
#ifndef _COMMON_ARRAY_HPP_
#include <common/array.hpp>
#endif
#ifndef _COMMON_POINT_HPP_
#include <common/point.hpp>
#endif
#ifndef _COMMON_GDIOBJ_HPP_
#include <common/gdiobj.hpp>
#endif
#ifndef _COMMON_RECTANGLE_HPP_
#include <common/rect.hpp>
#endif

class Window;
class Brush;

class PureDevice
{
public:
	enum Disposition{DeleteDC,ReleaseDC,AssumedDC,InvalidDC};
	enum DrawMode{INVALIDROP=0,R2BLACK=R2_BLACK,R2WHITE=R2_WHITE,R2NOP=R2_NOP,R2NOT=R2_NOT,
								R2COPYPEN=R2_COPYPEN,R2NOTCOPYPEN=R2_NOTCOPYPEN,R2MERGEPENNOT=R2_MERGEPENNOT,
								R2MASKPENNOT=R2_MASKPENNOT,R2MERGENOTPEN=R2_MERGENOTPEN,R2MASKNOTPEN=R2_MASKNOTPEN,
								R2MERGEPEN=R2_MERGEPEN,R2NOTMERGEPEN=R2_NOTMERGEPEN,R2MASKPEN=R2_MASKPEN,
	              R2NOTMASKPEN=R2_NOTMASKPEN,R2XORPEN=R2_XORPEN,R2NOTXORPEN=R2_NOTXORPEN};
	enum BltMode{Blackness=BLACKNESS,DstInvert=DSTINVERT,MergeCopy=MERGECOPY,MergePaint=MERGEPAINT,
							 NotSourceCopy=NOTSRCCOPY,NotSourceErase=NOTSRCERASE,PatternCopy=PATCOPY,
							 PatternInvert=PATINVERT,PatternPaint=PATPAINT,SourceAnd=SRCAND,
							 SourceCopy=SRCCOPY,SourceErase=SRCERASE,SourceInvert=SRCINVERT,
							 SourcePaint=SRCPAINT,White=WHITENESS};
	enum BkMode{Opaque=OPAQUE,Transparent=TRANSPARENT};
	PureDevice(void);
	PureDevice(const Window &someWindow);
	PureDevice(HWND hWnd);
	PureDevice(HDC hDC);
	PureDevice(const PureDevice &somePureDevice);
	virtual ~PureDevice();
	PureDevice &operator=(const PureDevice &somePureDevice);
	PureDevice &operator=(const Window &someWindow);
	PureDevice &operator=(HWND hWnd);
	operator HDC(void)const;
	void destroyDevice(void);
	WORD select(const GDIObj someGDIObj,WORD selState=TRUE);
	WORD compatibleDevice(const PureDevice &somePureDevice);
	WORD compatibleDevice(void);
	WORD displayDevice(void);
	WORD screenDevice(void);
	WORD line(const Point &firstPoint,const Point &secondPoint,const Pen &somePen)const;
	WORD line(const Point &firstPoint,const Point &secondPoint,const RGBColor &rgbColor)const;
  WORD line(const Point &firstPoint,const Point &secondPoint)const;
	WORD move(const Point &movePoint)const;
	WORD square(const Point &centerPoint,WORD lineLength,RGBColor rgbColor);
  WORD roundRectangle(const Point &upperLeftPoint,const Point &lowerRightPoint,DWORD width,DWORD height);
  WORD roundRectangle(const Point &centerPoint,int radius);
	WORD colorRect(const Rect &rect,const RGBColor &fillColor)const;
	WORD outlineRect(const Rect &rect,const Pen &pen);
	WORD horizontalResolution(void)const;
	WORD verticalResolution(void)const;
	WORD aspectValue(const Window &someWindow)const;
	WORD isOkay(void)const;
	WORD circle(Window &displayWindow,const Point &xyPoint,WORD radius,const RGBColor &someRGBColor)const;
	WORD circle(const Point &xyPoint,WORD radius,const RGBColor &someRGBColor)const;
	WORD crossHair(const Point &midPoint,WORD sectionlength,const Pen &somePen);
	WORD crossHairs(Array<Point> &midPoints,WORD sectionLength,const Pen &somePen);
  WORD crossHairs(Array<Point> &midPoints,WORD sectionLength,RGBColor rgbColor);
	WORD floodFill(const Point &xyPoint,const RGBColor &fillColor,const RGBColor &boundaryColor)const;
	WORD bitBlt(const Rect &dstRect,const PureDevice &srcDevice,const Point &srcPoint,BltMode bltMode=SourceCopy);
  WORD stretchBlt(const Rect &dstRect,const PureDevice &srcDevice,const Rect &srcRect,BltMode bltMode=SourceCopy);
	WORD setDrawMode(DrawMode ropCode);
	RGBColor setPixel(const Point &somePoint,const RGBColor &paletteColor)const;
	BOOL setPixelv(const Point &somePoint,const RGBColor &paletteColor)const;
	RGBColor setTextColor(const RGBColor &rgbColor)const;
	RGBColor setBkColor(const RGBColor &rgbColor)const;
  RGBColor getBkColor(void)const;
	BOOL fillRect(const Rect &rect,Brush &brush);
	BOOL textOut(int xLoc,int yLoc,const char *lpszString)const;
	bool drawText(const Point &point,const String &strText)const;
	BOOL getTextExtentPoint32(LPCTSTR lpszString,LPSIZE lpSizeStruct)const;
	int setBkMode(BkMode bkMode)const;
	RGBColor setPixel(const Point &somePoint,WORD paletteIndex)const;
	RGBColor getPixel(const Point &somePoint)const;
	Disposition disposition(void)const;
	void disposition(Disposition disposition);
	HDC getDC(void)const;
private:
	enum {DefaultAspect=100};
  void unselect(void);
	WORD circle(WORD aspectValue,const Point &xyPoint,WORD radius,const RGBColor &someRGBColor)const;
  void lineDraw(const Point &firstPoint,const Point &secondPoint)const;

	HDC mhDC;
	HWND mhParent;
  GDIObj mGDIObjPrev;
	Disposition mDisposition;
  int mPrevDrawMode;
};

inline
PureDevice::PureDevice(void)
: mhDC(0), mhParent(0), mDisposition(InvalidDC), mGDIObjPrev(0), mPrevDrawMode(INVALIDROP)
{
}

inline
PureDevice::PureDevice(HWND hParent)
: mhDC(0), mhParent(hParent), mDisposition(ReleaseDC), mGDIObjPrev(0), mPrevDrawMode(INVALIDROP)
{
	if(!mhParent||!::IsWindow(mhParent))return;
	mhDC=::GetDC(mhParent);
}

inline
PureDevice::PureDevice(const PureDevice &somePureDevice)
: mhDC(0), mhParent(0), mDisposition(DeleteDC), mGDIObjPrev(0), mPrevDrawMode(INVALIDROP)
{
	if(!somePureDevice.mhDC)return;
	mhDC=::CreateCompatibleDC(somePureDevice.mhDC);
}

inline
PureDevice::PureDevice(HDC hDC)
: mhDC(hDC), mhParent(0), mDisposition(AssumedDC), mGDIObjPrev(0), mPrevDrawMode(INVALIDROP)
{
}

inline
void PureDevice::unselect(void)
{
	if(!isOkay())return;
	if(mGDIObjPrev){::SelectObject(mhDC,mGDIObjPrev);mGDIObjPrev=0;}
}

inline
RGBColor PureDevice::setPixel(const Point &somePoint,const RGBColor &paletteColor)const
{
	return RGBColor(::SetPixel(mhDC,somePoint.x(),somePoint.y(),paletteColor));
}

inline
BOOL PureDevice::setPixelv(const Point &somePoint,const RGBColor &paletteColor)const
{
  return RGBColor(::SetPixelV(mhDC,somePoint.x(),somePoint.y(),paletteColor));
}

inline
RGBColor PureDevice::setPixel(const Point &somePoint,WORD paletteIndex)const
{
	return RGBColor(::SetPixel(mhDC,somePoint.x(),somePoint.y(),PALETTEINDEX(paletteIndex)));
}

inline
RGBColor PureDevice::getPixel(const Point &somePoint)const
{
	return RGBColor(::GetPixel(mhDC,somePoint.x(),somePoint.y()));
}

inline
PureDevice::operator HDC(void)const
{
	return mhDC;
}

inline
WORD PureDevice::circle(const Point &xyPoint,WORD radius,const RGBColor &someRGBColor)const
{
	return circle(DefaultAspect,xyPoint,radius,someRGBColor);
}

inline
void PureDevice::lineDraw(const Point &firstPoint,const Point &secondPoint)const
{
	::MoveToEx(mhDC,firstPoint.x(),firstPoint.y(),0);
  ::LineTo(mhDC,secondPoint.x(),secondPoint.y());
}

inline
WORD PureDevice::roundRectangle(const Point &upperLeftPoint,const Point &lowerRightPoint,DWORD width,DWORD height)
{
	if(!isOkay())return FALSE;
  return ::RoundRect(*this,upperLeftPoint.x(),upperLeftPoint.y(),lowerRightPoint.x(),lowerRightPoint.y(),width,height);
}

inline
WORD PureDevice::setDrawMode(DrawMode ropCode)
{
	if(!isOkay())return FALSE;
	if(INVALIDROP!=mPrevDrawMode)::SetROP2(*this,mPrevDrawMode);
	if(INVALIDROP!=ropCode)mPrevDrawMode=::SetROP2(*this,ropCode);
	else mPrevDrawMode=ropCode;
	return TRUE;
}

inline
WORD PureDevice::bitBlt(const Rect &dstRect,const PureDevice &srcDevice,const Point &srcPoint,BltMode bltMode)
{
  if(!isOkay())return FALSE;
	return ::BitBlt(*this,dstRect.left(),dstRect.top(),dstRect.right(),dstRect.bottom(),srcDevice,srcPoint.x(),srcPoint.y(),bltMode);
}

inline
WORD PureDevice::stretchBlt(const Rect &dstRect,const PureDevice &srcDevice,const Rect &srcRect,BltMode bltMode)
{
  if(!isOkay())return FALSE;
	return ::StretchBlt(*this,dstRect.left(),dstRect.top(),dstRect.right(),dstRect.bottom(),srcDevice,srcRect.left(),srcRect.top(),srcRect.right(),srcRect.bottom(),bltMode);
}

inline
RGBColor PureDevice::setBkColor(const RGBColor &rgbColor)const
{
  if(!isOkay())return RGBColor(0,0,0);
  return ::SetBkColor(mhDC,rgbColor);
}

inline
RGBColor PureDevice::getBkColor(void)const
{
  return RGBColor(::GetBkColor(mhDC));
}

inline
WORD PureDevice::move(const Point &movePoint)const
{
  if(!isOkay())return FALSE;
  return ::MoveToEx(mhDC,movePoint.x(),movePoint.y(),(LPPOINT)0);
}

inline
RGBColor PureDevice::setTextColor(const RGBColor &rgbColor)const
{
  if(!isOkay())return RGBColor();
	return ::SetTextColor(mhDC,rgbColor);
}

inline
BOOL PureDevice::textOut(int xLoc,int yLoc,const char *lpszString)const
{
  if(!isOkay()||!lpszString)return FALSE;
	return ::TextOut(mhDC,xLoc,yLoc,lpszString,::strlen(lpszString));
}

inline
bool PureDevice::drawText(const Point &point,const String &strText)const
{
  if(!isOkay()||strText.isNull())return FALSE;
	return ::TextOut(mhDC,point.x(),point.y(),strText.str(),strText.length());
}

inline
BOOL PureDevice::getTextExtentPoint32(LPCTSTR lpszString,LPSIZE lpSizeStruct)const
{
  if(!isOkay()||!lpszString)return FALSE;
	return ::GetTextExtentPoint32(mhDC,lpszString,::strlen(lpszString),lpSizeStruct);
}

inline
int PureDevice::setBkMode(BkMode bkMode)const
{
  if(!isOkay())return FALSE;
	return ::SetBkMode(mhDC,bkMode);
}

inline
PureDevice::Disposition PureDevice::disposition(void)const
{
  return mDisposition;
}

inline
void PureDevice::disposition(Disposition disposition)
{
  mDisposition=disposition;
}

inline
HDC PureDevice::getDC(void)const
{
  return mhDC;
}
#endif
