#pragma once

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

GDI Helper Classes, MFC Version, by G. Bavestrelli, Techint S.p.A.
Any feedback is welcome, you can contact me at:
					  giovanni.bavestrelli@pomini.it

class CSelStock
class CSelPen
class CSelBrush
class CSelFont
class CSelBitmap
class CSelPalette
class CSelROP2
class CSelBkMode
class CSelBkColor
class CSelTextColor
class CSelTextAlign
class CSelMapMode
class CSaveDC

class CSelStock
	CSelStock(CDC * pDC, int index)
	void Select(int index)
	CGdiObject * Old()

class CSelPen
	CSelPen(CDC * pDC, COLORREF col, int sty=PS_SOLID, int wid = 0)
	CSelPen(CDC * pDC, CPen * pPen)
	void Select(CPen * pPen)
	void Select(COLORREF col, int sty=PS_SOLID, int wid = 0)

class CSelBrush
	CSelBrush(CDC * pDC, CBrush * pBrush)
	CSelBrush(CDC * pDC, COLORREF crColor)
	CSelBrush(CDC * pDC, int index, COLORREF crColor)    // Hatch brush
	CSelBrush(CDC * pDC, CBitmap * pBitmap)              // Pattern brush
	CSelBrush(CDC * pDC, HGLOBAL hPackedDib, UINT usage) // DIB Pattern brush
	void Select(CBrush * pBrush)
	void Select(COLORREF col)
	void Select(int index, COLORREF col)                 // Hatch brush
	void Select(CBitmap * pBitmap)                       // Pattern brush
	void Select(HGLOBAL hPackedDib, UINT usage)          // DIB Pattern brush

class CSelFont
	CSelFont(CDC * pDC, int size, LPCTSTR face = NULL, BOOL bold = 0,
				BOOL italic = 0, BOOL underlined = 0, BOOL fixed = 0,
				BOOL hiquality = 0, int angleindegrees = 0)
	CSelFont(CDC * pDC, CFont * pFont)
	CSelFont(CDC * pDC, const LOGFONT* lpLogFont)

	void Select(int size, LPCTSTR face = NULL, BOOL bold = 0,
					BOOL italic = 0, BOOL underlined = 0, BOOL fixed = 0,
					BOOL hiquality = 0, int angleindegrees = 0)
	void Select(CFont * pFont)
	void Select(const LOGFONT* lpLogFont)

class CSelBitmap
	CSelBitmap(CDC * pDC, CBitmap * pBitmap)
	CSelBitmap(CDC * SelectInDC, CDC * pCompatibleToDC, int w, int h)
	void Select(CBitmap * pBitmap)
	void Select(CDC * pCompatibleToDC, int w, int h)

class CSelPalette
	CSelPalette(CDC * pDC, CPalette * pPalette,
	            BOOL fForceBackground = FALSE, BOOL fRealize = TRUE)
	UINT Select(CPalette * pPalette,
	            BOOL fForceBackground = FALSE, BOOL fRealize = TRUE)
	void ChangeRestoreFlags(BOOL fForceBackground, BOOL fRealize)

class CSelROP2
	CSelROP2(CDC * pDC, int drawMode)
	void Select(int drawmode)

class CSelBkMode
	CSelBkMode(CDC * pDC, int BkMode)
	void Select(int bkmode)

class CSelBkColor
	CSelBkColor(CDC * pDC, COLORREF BkColor)
	void Select(COLORREF color)

class CSelTextColor
	CSelTextColor(CDC * pDC, COLORREF TextColor)
	void Select(COLORREF color)

class CSelTextAlign
	CSelTextAlign(CDC * pDC, UINT TextAlign)
	void Select(UINT align)

class CSelMapMode
	CSelMapMode(CDC * pDC, int MapMode)
	void Select(int mode)

class CSaveDC
	CSaveDC(CDC * pDC) // saving the complete state of the DC

every class also have:
	<Constructor>(CDC* pDC) // constructor w/o selection
	void Restore()          // restores original object and destroys new object if neccessary
	<retval> Old()          // returns original object

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

//******************************************************************
// Base class, stores CDC *
//******************************************************************

class CSelect
{
protected:
	CDC * const m_pDC;
	CSelect(CDC * pDC):m_pDC(pDC) 	{ ASSERT(m_pDC);}
	virtual ~CSelect() {}

private:

	// Disable copying
	CSelect& operator=(const CSelect& d);
	CSelect(const CSelect &);
};

//******************************************************************
// Class for Stock Objects
//******************************************************************

class CSelStock : public CSelect
{
	CGdiObject * m_pOldObj;

public:

	CSelStock(CDC * pDC)
		:CSelect(pDC), m_pOldObj(NULL) {}

	CSelStock(CDC * pDC, int index)
		:CSelect(pDC), m_pOldObj(NULL)
		{ Select(index); }

	~CSelStock() { Restore(); }

	void Select(int index)
	{
		CGdiObject * pOld = m_pDC->SelectStockObject(index); ASSERT(pOld);
		if (!m_pOldObj) m_pOldObj = pOld;
	}

	void Restore()
	{
		if (m_pOldObj) VERIFY(m_pDC->SelectObject(m_pOldObj));
		m_pOldObj = NULL;
	}

	CGdiObject * Old() const { return m_pOldObj; }
};

//******************************************************************
// Pens
//******************************************************************

class CSelPen	: public CSelect
{
	CPen	 m_NewPen;
	CPen * m_pOldPen;

public:

	CSelPen(CDC * pDC)
	  :CSelect(pDC), m_pOldPen(NULL){}

	CSelPen(CDC * pDC, COLORREF col, int sty = PS_SOLID, int wid = 0)
		:CSelect(pDC), m_pOldPen(NULL)
		{ Select(col, sty, wid); }

	CSelPen(CDC * pDC, CPen * pPen)
		:CSelect(pDC), m_pOldPen(NULL)
		{ Select(pPen); }

	~CSelPen() { Restore(); }

	void Select(CPen * pPen)
	{
		ASSERT(pPen);
		ASSERT(pPen != &m_NewPen);
		CPen * pOld = m_pDC->SelectObject(pPen); ASSERT(pOld);
		if (!m_pOldPen) m_pOldPen = pOld;
		m_NewPen.DeleteObject();
	}

	void Select(COLORREF col, int sty = PS_SOLID, int wid = 0)
	{
		if (m_pOldPen) Select(m_pOldPen);
		VERIFY(m_NewPen.CreatePen(sty, wid, col));
		VERIFY(m_pOldPen = m_pDC->SelectObject(&m_NewPen));
	}

	void Restore()
	{
		if (m_pOldPen) VERIFY(m_pDC->SelectObject(m_pOldPen));
		m_pOldPen = NULL;
		m_NewPen.DeleteObject();
	}

	CPen * Old() const	 { return m_pOldPen; }
};

//******************************************************************
// Brushes
//******************************************************************

class CSelBrush  : public CSelect
{
	 CBrush	 m_NewBrush;
	 CBrush * m_pOldBrush;

public:
	CSelBrush(CDC * pDC)
		:CSelect(pDC), m_pOldBrush(NULL) {}

	// Solid brush
	CSelBrush(CDC * pDC, COLORREF crColor)
		:CSelect(pDC), m_pOldBrush(NULL)
		{ Select(crColor); }

	// Hatch brush
	CSelBrush(CDC * pDC, int index, COLORREF crColor)
		:CSelect(pDC), m_pOldBrush(NULL)
		{ Select(index, crColor); }

	// Pattern brush
	CSelBrush(CDC * pDC, CBitmap * pBitmap)
		:CSelect(pDC), m_pOldBrush(NULL)
		{ Select(pBitmap); }

	// DIB Pattern brush
	CSelBrush(CDC * pDC, HGLOBAL hPackedDib, UINT usage)
	  :CSelect(pDC), m_pOldBrush(NULL)
		{ Select(hPackedDib, usage); }

	CSelBrush(CDC * pDC, CBrush * pBrush)
	  :CSelect(pDC), m_pOldBrush(NULL)
		{ Select(pBrush); }

	~CSelBrush() { Restore(); }

	void Select(CBrush * pBrush)
	{
		ASSERT(pBrush);
		ASSERT(pBrush != &m_NewBrush);
		CBrush * pOld = m_pDC->SelectObject(pBrush); ASSERT(pOld);
		if (!m_pOldBrush) m_pOldBrush=pOld;
		m_NewBrush.DeleteObject();
	}

	// Solid brush
	void Select(COLORREF col)
	{
		if (m_pOldBrush) Select(m_pOldBrush);
		VERIFY(m_NewBrush.CreateSolidBrush(col));
		VERIFY(m_pOldBrush = m_pDC->SelectObject(&m_NewBrush));
	}

	// Hatch brush
	void Select(int index, COLORREF col)
	{
		if (m_pOldBrush) Select(m_pOldBrush);
		VERIFY(m_NewBrush.CreateHatchBrush(index, col));
		VERIFY(m_pOldBrush = m_pDC->SelectObject(&m_NewBrush));
	}

	// Pattern brush
	void Select(CBitmap * pBitmap)
	{
		ASSERT(pBitmap);
		if (m_pOldBrush) Select(m_pOldBrush);
		VERIFY(m_NewBrush.CreatePatternBrush(pBitmap));
		VERIFY(m_pOldBrush = m_pDC->SelectObject(&m_NewBrush));
	}

	// DIB Pattern brush
	void Select(HGLOBAL hPackedDib, UINT usage)
	{
		if (m_pOldBrush) Select(m_pOldBrush);
		VERIFY(m_NewBrush.CreateDIBPatternBrush(hPackedDib, usage));
		VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));
	}

	void Restore()
	{
		if (m_pOldBrush) VERIFY(m_pDC->SelectObject(m_pOldBrush));
		m_pOldBrush = NULL;
		m_NewBrush.DeleteObject();
	}

	CBrush * Old() const 	{ return m_pOldBrush; }
};



//******************************************************************
// My own font with different constructor and creation function
//******************************************************************

class CMyFont : public CFont
{
public:
	CMyFont(){}
	CMyFont(CDC * pDC, int size, LPCTSTR face = NULL, BOOL bold = 0,
	        BOOL italic = 0, BOOL underlined = 0, BOOL fixed = 0,
	        BOOL hiquality = 0, int angleindegrees = 0)
	{
		VERIFY(MyCreateFont(pDC, size, face, bold, italic, underlined,
		                    fixed, hiquality, angleindegrees));
	}
	BOOL MyCreateFont(CDC * pDC, int size, LPCTSTR face = NULL,
	                  BOOL bold = 0, BOOL italic = 0, BOOL underlined = 0,
	                  BOOL fixed = 0, BOOL hiquality = 0,
	                  int angleindegrees = 0)
	{
		ASSERT(pDC);
		CSize Size(0, MulDiv(size, pDC->GetDeviceCaps(LOGPIXELSY), 72));
		pDC->DPtoLP(&Size);
		return CreateFont(-abs(Size.cy), 0, 10*angleindegrees, 0,
		                  bold ? FW_BOLD : FW_NORMAL, BYTE(italic),
		                  BYTE(underlined), 0, ANSI_CHARSET,
		                  OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		                  BYTE(hiquality ? PROOF_QUALITY : DEFAULT_QUALITY),
		                  BYTE(fixed ? FF_MODERN|FIXED_PITCH :
		                               FF_SWISS|VARIABLE_PITCH),
		                  face && *face ? face : NULL);
	}
};


//******************************************************************
// Fonts
//******************************************************************


class CSelFont  : public CSelect
{
	CMyFont m_NewFont;
	CFont * m_pOldFont;

public:

	CSelFont(CDC * pDC)
		: CSelect(pDC), m_pOldFont(NULL) {}

	CSelFont(CDC * pDC, int size, LPCTSTR face = NULL, BOOL bold = 0,
	         BOOL italic = 0, BOOL underlined = 0, BOOL fixed = 0,
	         BOOL hiquality = 0, int angleindegrees = 0)
		: CSelect(pDC), m_pOldFont(NULL)
	{
		Select(size, face, bold, italic, underlined, 
		       fixed, hiquality, angleindegrees);
	}

	CSelFont(CDC * pDC, CFont * pFont)
		: CSelect(pDC), m_pOldFont(NULL)
		{ Select(pFont); }

	CSelFont(CDC * pDC, const LOGFONT* lpLogFont)
		: CSelect(pDC), m_pOldFont(NULL)
		{ Select(lpLogFont); }

	~CSelFont() { Restore(); }

	void Select(CFont * pFont)
	{
		ASSERT(pFont);
		ASSERT(pFont != &m_NewFont);
		CFont * pOld = m_pDC->SelectObject(pFont); ASSERT(pOld);
		if (!m_pOldFont) m_pOldFont = pOld;
		m_NewFont.DeleteObject();
	}

	void Select(int size, LPCTSTR face = NULL, BOOL bold = 0,
	            BOOL italic = 0, BOOL underlined = 0, BOOL fixed = 0,
	            BOOL hiquality = 0, int angleindegrees = 0)
	{
		if (m_pOldFont) Select(m_pOldFont);
		VERIFY(m_NewFont.MyCreateFont(m_pDC, size, face, bold, italic,
		                              underlined, fixed, hiquality, angleindegrees));
		VERIFY(m_pOldFont = m_pDC->SelectObject(&m_NewFont));
	}

	void Select(const LOGFONT* lpLogFont)
	{
		if (m_pOldFont) Select(m_pOldFont);
		VERIFY(m_NewFont.CreateFontIndirect(lpLogFont));
		VERIFY(m_pOldFont = m_pDC->SelectObject(&m_NewFont));
	}

	void Restore()
	{
		if (m_pOldFont) VERIFY(m_pDC->SelectObject(m_pOldFont));
		m_pOldFont = NULL;
		m_NewFont.DeleteObject();
	}

	CFont * Old() const	{ return m_pOldFont; }
};


//******************************************************************
// Bitmaps
//******************************************************************

class CSelBitmap	: public CSelect
{
	CBitmap	 m_NewBmp;
	CBitmap * m_pOldBmp;

public:
	CSelBitmap(CDC * pDC)
		: CSelect(pDC), m_pOldBmp(NULL) {}

	CSelBitmap(CDC * SelectInDC, CDC * pCompatibleToDC, int w, int h)
		: CSelect(SelectInDC), m_pOldBmp(NULL)
		{ Select(pCompatibleToDC, w, h); }

	CSelBitmap(CDC * pDC, CBitmap * pBitmap)
		: CSelect(pDC), m_pOldBmp(NULL)
		{ Select(pBitmap); }

	~CSelBitmap() { Restore(); }

	void Select(CBitmap * pBitmap)
	{
		ASSERT(pBitmap);
		ASSERT(pBitmap != &m_NewBmp);
		CBitmap * pOld = m_pDC->SelectObject(pBitmap);	ASSERT(pOld);
		if (!m_pOldBmp) m_pOldBmp = pOld;
		m_NewBmp.DeleteObject();
	}

	void Select(CDC * pCompatibleToDC, int w, int h)
	{
		ASSERT(pCompatibleToDC);
		if (m_pOldBmp) Select(m_pOldBmp);
		VERIFY(m_NewBmp.CreateCompatibleBitmap(pCompatibleToDC, w, h));
		VERIFY(m_pOldBmp = m_pDC->SelectObject(&m_NewBmp));
	}

	void Restore()
	{
		if (m_pOldBmp) VERIFY(m_pDC->SelectObject(m_pOldBmp));
		m_pOldBmp = NULL;
		m_NewBmp.DeleteObject();
	}

	CBitmap * Old() const { return m_pOldBmp; }
};

// This class is a bit different
class CSelPalette  : public CSelect
{
	// You need your own palette, use CPalette
	CPalette * m_pOldPalette;
	BOOL m_fForceBackground;
	BOOL m_fRealizePalette;

public:

	CSelPalette(CDC * pDC)
		: CSelect(pDC), m_pOldPalette(NULL) {}

	CSelPalette(CDC * pDC, CPalette * pPalette,
	            BOOL fForceBackground = FALSE, BOOL fRealize = TRUE)
		: CSelect(pDC), m_pOldPalette(NULL)
		{ Select(pPalette, fForceBackground, fRealize); }

	~CSelPalette() { Restore(); }

	UINT Select(CPalette * pPalette,
	            BOOL fForceBackground = FALSE, BOOL fRealize = TRUE)
	{
		ASSERT(pPalette);
		ASSERT(m_pDC->GetDeviceCaps(RASTERCAPS)&RC_PALETTE);
		CPalette * pOld=m_pDC->SelectPalette(pPalette, fForceBackground);
		ASSERT(pOld);
		if (!m_pOldPalette) m_pOldPalette=pOld;
		m_fForceBackground = fForceBackground;
		m_fRealizePalette = fRealize;
		return fRealize ? m_pDC->RealizePalette() : 0;
	}

	void ChangeRestoreFlags(BOOL fForceBackground, BOOL fRealize)
	{
		m_fForceBackground = fForceBackground;
		m_fRealizePalette = fRealize;
	}
	
	void Restore()
	{
		if (!m_pOldPalette)
			return;

		VERIFY(m_pDC->SelectPalette(m_pOldPalette, m_fForceBackground));
		if (m_fRealizePalette) 
			m_pDC->RealizePalette();
		m_pOldPalette = NULL;
	}

	CPalette * Old() const	{ return m_pOldPalette; }
};


//******************************************************************
// Set and restore other characteristics of the DC (no GDI objects)
//******************************************************************


class CSelROP2 : public CSelect
{
	int m_OldRop;

public:

	CSelROP2(CDC * pDC)
		: CSelect(pDC), m_OldRop(0)
		{ /*VERIFY(m_OldRop=m_pDC->GetROP2());*/ }

	CSelROP2(CDC * pDC, int drawMode)
		: CSelect(pDC), m_OldRop(0)
		{ Select(drawMode); }

	~CSelROP2() { Restore(); }

	void Select(int drawmode)
	{
		int old = m_pDC->SetROP2(drawmode); ASSERT(old);
		if (!m_OldRop) m_OldRop = old;
	}

	void Restore()
	{
		if (m_OldRop) VERIFY(m_pDC->SetROP2(m_OldRop));
		m_OldRop = 0;
	}

	int Old() const { return m_OldRop; }
};


class CSelBkMode : public CSelect
{
	int m_OldBkMode;

public:

	CSelBkMode(CDC * pDC)
		: CSelect(pDC), m_OldBkMode(0)
		{ /*VERIFY(m_OldBkMode = m_pDC->GetBkMode());*/ }

	CSelBkMode(CDC * pDC, int BkMode)
		: CSelect(pDC), m_OldBkMode(0)
		{ Select(BkMode); }

	~CSelBkMode() { Restore(); }

	void Select(int BkMode)
	{
		int old = m_pDC->SetBkMode(BkMode); ASSERT(old);
		if (!m_OldBkMode) m_OldBkMode = old;
	}

	void Restore()
	{
	  if (m_OldBkMode) VERIFY(m_pDC->SetBkMode(m_OldBkMode));
	  m_OldBkMode = 0;
	}

	int Old() const { return m_OldBkMode; }
};


class CSelBkColor : public CSelect
{
	COLORREF m_OldBkColor;

public:

	CSelBkColor(CDC * pDC)
		: CSelect(pDC), m_OldBkColor(CLR_INVALID)
		{ m_OldBkColor = m_pDC->GetBkColor(); }

	CSelBkColor(CDC * pDC, COLORREF BkColor)
	  :CSelect(pDC), m_OldBkColor(CLR_INVALID)
		{ Select(BkColor); }

	~CSelBkColor() { Restore(); }

	void Select(COLORREF color)
	{
		ASSERT(color != CLR_INVALID);
		int old = m_pDC->SetBkColor(color);	ASSERT(old != CLR_INVALID);
		if (m_OldBkColor == CLR_INVALID) m_OldBkColor = old;
	}

	void Restore()
	{
		if(m_OldBkColor == CLR_INVALID) return;
		VERIFY(m_pDC->SetBkColor(m_OldBkColor) != CLR_INVALID);
		m_OldBkColor = CLR_INVALID;
	}

	COLORREF Old() const { return m_OldBkColor; }
};


class CSelTextColor : public CSelect
{
	COLORREF m_OldTextColor;

public:

	CSelTextColor(CDC * pDC)
		: CSelect(pDC), m_OldTextColor(CLR_INVALID)
		{ m_OldTextColor = m_pDC->GetTextColor(); }

	CSelTextColor(CDC * pDC, COLORREF TextColor)
	  :CSelect(pDC), m_OldTextColor(CLR_INVALID)
		{ Select(TextColor); }

	~CSelTextColor() { Restore(); }

	void Select(COLORREF color)
	{
		ASSERT(color != CLR_INVALID);
		int old = m_pDC->SetTextColor(color);	ASSERT(old != CLR_INVALID);
		if (m_OldTextColor == CLR_INVALID) m_OldTextColor = old;
	}

	void Restore()
	{
		if(m_OldTextColor == CLR_INVALID) return;
		VERIFY(m_pDC->SetTextColor(m_OldTextColor) != CLR_INVALID);
		m_OldTextColor = CLR_INVALID;
	}

	COLORREF Old() const { return m_OldTextColor; }
};


class CSelTextAlign : public CSelect
{
	UINT m_OldTextAlign;

public:

	CSelTextAlign(CDC * pDC)
		: CSelect(pDC), m_OldTextAlign(GDI_ERROR)
		{ m_OldTextAlign = m_pDC->GetTextAlign(); }

	CSelTextAlign(CDC * pDC, UINT TextAlign)
		: CSelect(pDC), m_OldTextAlign(GDI_ERROR)
		{ Select(TextAlign); }

	~CSelTextAlign() { Restore(); }

	void Select(UINT align)
	{
		ASSERT(align != GDI_ERROR);
		int old = m_pDC->SetTextAlign(align);	ASSERT(old != GDI_ERROR);
		if (m_OldTextAlign == GDI_ERROR) m_OldTextAlign = old;
	}

	void Restore()
	{
		if(m_OldTextAlign == GDI_ERROR) return;
		VERIFY(m_pDC->SetTextAlign(m_OldTextAlign) != GDI_ERROR);
		m_OldTextAlign = GDI_ERROR;
	}

	UINT Old() const { return m_OldTextAlign; }
};


class CSelMapMode : public CSelect
{
	int m_OldMapMode;

public:

	CSelMapMode(CDC * pDC)
		: CSelect(pDC), m_OldMapMode(0)
		{ /*VERIFY(m_OldMapMode = m_pDC->GetMapMode());*/ }

	CSelMapMode(CDC * pDC, int MapMode)
		: CSelect(pDC), m_OldMapMode(0)
		{ Select(MapMode); }

	~CSelMapMode() { Restore(); }

	void Select(int MapMode)
	{
		int old = m_pDC->SetMapMode(MapMode); ASSERT(old);
		if (!m_OldMapMode) m_OldMapMode = old;
	}

	void Restore()
	{
		if (m_OldMapMode) VERIFY(m_pDC->SetMapMode(m_OldMapMode));
		m_OldMapMode = 0;
	}

	UINT Old() const { return m_OldMapMode; }
};

//******************************************************************
// Class for saving the complete state of the DC
//******************************************************************

class CSaveDC : public CSelect
{
	int m_SavedDC;

public:

	CSaveDC(CDC * pDC)
		: CSelect(pDC)
		{ VERIFY(m_SavedDC = m_pDC->SaveDC()); }

	~CSaveDC() { Restore(); }

	void Restore()
	{
		if (m_SavedDC) VERIFY(m_pDC->RestoreDC(m_SavedDC));
		m_SavedDC = 0;
	}

	int Old() const { return m_SavedDC; }
};
