#ifndef _gdiBASE_
#define _gdiBASE_
/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: gdiBase
#Project: xdcGdi
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifdef XDCGDI_EXPORTS
#	define gdiEXPORT __declspec(dllexport)
#else
#	ifdef XDCBASE_EMBED
#		define gdiEXPORT
#	else
#		define gdiEXPORT __declspec(dllimport)
#	endif
#endif

#ifndef _xbDATA_
#	include "xbData.h"
#endif

#ifndef _xbBOOL_
#	include "xbBool.h"
#endif

#ifndef _xbCOLOR_
#	include "xbColor.h"
#endif

//Exception gdiEsys: used by gdi system to signal errors
xbEdclStart(gdiEsys,xbEadv,{5118741C-1D4F-41f9-918D-BD8FB5F2F572},gdiEXPORT)
	virtual xbMBiconType GetMBoxIcon() const {return xbMBicon_Stop;}
	virtual const char *GetMBoxTitle() const {return "GDI system error";} 
xbEdclEnd(gdiEsys);

struct gdiEXPORT gdiPointLow:public POINT
{
	gdiPointLow()
	{
		x = y = 0;
	}
	gdiPointLow(long _x,long _y)
	{
		x = _x;
		y = _y;
	}

	void Empty()
	{
		x = y = 0;
	}
	bool operator==(const gdiPointLow &pt) const
	{
		return x == pt.x && y == pt.y;
	}
	bool operator!=(const gdiPointLow &pt) const
	{
		return x != pt.x || y != pt.y;
	}
};

struct gdiEXPORT gdiSizeLow:public SIZE
{
	gdiSizeLow()
	{
		cx = cy = 0;
	}
	gdiSizeLow(long _cx,long _cy)
	{
		cx = _cx;
		cy = _cy;
	}

	void Empty()
	{
		cx = cy = 0;
	}
	bool IsEmpty() const
	{
		return cx <= 0 || cy <= 0;
	}
	bool IsValid() const
	{
		return cx > 0 && cy > 0;
	}

	bool operator==(const gdiSizeLow &sz) const
	{
		return cx == sz.cx && cy == sz.cy;
	}
	bool operator!=(const gdiSizeLow &sz) const
	{
		return cx != sz.cx || cy != sz.cy;
	}
};

struct gdiEXPORT gdiRectLow:public RECT
{
	gdiRectLow()
	{
		left = right = top = bottom = 0;
	}
	gdiRectLow(long _left,long _top,long _right,long _bottom)
	{
		left = _left;
		right = _right;
		top = _top;
		bottom = _bottom;
	}
	gdiRectLow(const gdiPointLow &pt,const gdiSizeLow &sz)
	{
		left = pt.x;
		right = pt.x + sz.cx;
		top = pt.y;
		bottom = pt.y + sz.cy;
	}


	void Empty()
	{
		left = right = top = bottom = 0;
	}
	bool IsEmpty() const
	{
		return left >= right || top >= bottom;
	}
	bool IsValid() const
	{
		return left < right && top < bottom;
	}

	gdiPointLow LeftTop() const {return gdiPointLow(left,top);}
	gdiPointLow LeftBottom() const {return gdiPointLow(left,bottom);}
	gdiPointLow RightTop() const {return gdiPointLow(right,top);}
	gdiPointLow RightBottom() const {return gdiPointLow(right,bottom);}
	gdiPointLow Center() const {return gdiPointLow(left+Width()/2,top+Height()/2);}

	gdiSizeLow Size() const {return gdiSizeLow(right - left,bottom - top);}
	long Width() const {return right - left;}
	long Height() const {return bottom - top;}

	bool operator==(const gdiRectLow &rect) const
	{
		return left == rect.left && right == rect.right
			&& top == rect.top && bottom == rect.bottom;
	}
	bool operator!=(const gdiRectLow &rect) const
	{
		return left != rect.left || right != rect.right
			|| top != rect.top || bottom != rect.bottom;
	}

	// returns true if point is within rectangle
	bool PtInRect(const POINT &pt) const;
	void Normalize();

	// set this rectangle to intersection of two others
	bool Intersect(const RECT &rect1, const RECT &rect2);
};

enum gdiUnitType
{
	gdiUnit_void = -1,
	gdiUnit_pixel = 0,
	gdiUnit_point = 1, //72pt per inch (gdiPOINT_PER_INCH)
	gdiUnit_mm = 2,
	gdiUnit_inch = 3,
	gdiUnit_percent = 4
};

typedef float gdinum;

struct gdiEXPORT gdiPointF
{
	gdinum x,y;

	gdiPointF()
	{
		x = y = 0.0;
	}
	gdiPointF(gdinum _x,gdinum _y)
	{
		x = _x;
		y = _y;
	}

	void Empty()
	{
		x = y = 0;
	}
	bool operator==(const gdiPointF &pt) const
	{
		return x == pt.x && y == pt.y;
	}
	bool operator!=(const gdiPointF &pt) const
	{
		return x != pt.x || y != pt.y;
	}
};

struct gdiEXPORT gdiSizeF
{
	gdinum cx,cy;

	gdiSizeF()
	{
		cx = cy = 0;
	}
	gdiSizeF(gdinum _cx,gdinum _cy)
	{
		cx = _cx;
		cy = _cy;
	}

	void Empty()
	{
		cx = cy = 0;
	}
	bool IsEmpty() const
	{
		return cx <= 0 || cy <= 0;
	}
	bool IsValid() const
	{
		return cx > 0 && cy > 0;
	}

	bool operator==(const gdiSizeF &sz) const
	{
		return cx == sz.cx && cy == sz.cy;
	}
	bool operator!=(const gdiSizeF &sz) const
	{
		return cx != sz.cx || cy != sz.cy;
	}
};

struct gdiEXPORT gdiRectF
{
	gdinum left,top,right,bottom;

	gdiRectF()
	{
		left = right = top = bottom = 0;
	}
	gdiRectF(gdinum _left,gdinum _top,gdinum _right,gdinum _bottom)
	{
		left = _left;
		right = _right;
		top = _top;
		bottom = _bottom;
	}
	gdiRectF(const gdiPointF &pt,const gdiSizeF &sz)
	{
		left = pt.x;
		right = pt.x + sz.cx;
		top = pt.y;
		bottom = pt.y + sz.cy;
	}


	void Empty()
	{
		left = right = top = bottom = 0;
	}
	bool IsEmpty() const
	{
		return left >= right || top >= bottom;
	}
	bool IsValid() const
	{
		return left < right && top < bottom;
	}

	gdiPointF LeftTop() const {return gdiPointF(left,top);}
	gdiPointF LeftBottom() const {return gdiPointF(left,bottom);}
	gdiPointF RightTop() const {return gdiPointF(right,top);}
	gdiPointF RightBottom() const {return gdiPointF(right,bottom);}
	gdiPointF Center() const {return gdiPointF(left+Width()/2,top+Height()/2);}

	gdiSizeF Size() const {return gdiSizeF(right - left,bottom - top);}
	gdinum Width() const {return right - left;}
	gdinum Height() const {return bottom - top;}

	bool operator==(const gdiRectF &rect) const
	{
		return left == rect.left && right == rect.right
			&& top == rect.top && bottom == rect.bottom;
	}
	bool operator!=(const gdiRectF &rect) const
	{
		return left != rect.left || right != rect.right
			|| top != rect.top || bottom != rect.bottom;
	}

	// returns true if point is within rectangle
	bool PtInRect(const gdiPointF &pt) const;
	void Normalize();

	// set this rectangle to intersection of two others
	bool Intersect(const gdiRectF &rect1, const gdiRectF &rect2);
};

struct gdiEXPORT gdiPointU:public gdiPointF
{
	gdiUnitType unitX,unitY;
	gdiPointU()
	{
		unitX = gdiUnit_void;
		unitY = gdiUnit_void;
	}
	gdiPointU(gdinum _x,gdinum _y,gdiUnitType _unit)
		:gdiPointF(_x,_y)
	{
		unitX = _unit;
		unitY = _unit;
	}
	gdiPointU(gdinum _x,gdiUnitType _unitX,gdinum _y,gdiUnitType _unitY)
		:gdiPointF(_x,_y)
	{
		unitX = _unitX;
		unitY = _unitY;
	}
	gdiPointU(const gdiPointF &pt,gdiUnitType _unit)
		:gdiPointF(pt)
	{
		unitX = _unit;
		unitY = _unit;
	}

	void Empty()
	{
		gdiPointF::Empty();
		unitX = gdiUnit_void;
		unitY = gdiUnit_void;
	}
	bool operator==(const gdiPointU &pt) const
	{
		return unitX == pt.unitX && unitY == pt.unitY && gdiPointF::operator ==(pt);
	}
	bool operator!=(const gdiPointU &pt) const
	{
		return unitX != pt.unitX || unitY != pt.unitY || gdiPointF::operator !=(pt);
	}
};

struct gdiEXPORT gdiSizeU:public gdiSizeF
{
	gdiUnitType unitX,unitY;
	gdiSizeU()
	{
		unitX = gdiUnit_void;
		unitY = gdiUnit_void;
	}
	gdiSizeU(gdinum _cx,gdinum _cy,gdiUnitType _unit)
		:gdiSizeF(_cx,_cy)
	{
		unitX = _unit;
		unitY = _unit;
	}
	gdiSizeU(gdinum _cx,gdiUnitType _unitX,gdinum _cy,gdiUnitType _unitY)
		:gdiSizeF(_cx,_cy)
	{
		unitX = _unitX;
		unitY = _unitY;
	}
	gdiSizeU(const gdiSizeF &pt,gdiUnitType _unit)
		:gdiSizeF(pt)
	{
		unitX = _unit;
		unitY = _unit;
	}

	void Empty()
	{
		gdiSizeF::Empty();
		unitX = gdiUnit_void;
		unitY = gdiUnit_void;
	}
	bool operator==(const gdiSizeU &sz) const
	{
		return unitX == sz.unitX && unitY == sz.unitY && gdiSizeF::operator ==(sz);
	}
	bool operator!=(const gdiSizeU &sz) const
	{
		return unitX != sz.unitX || unitY != sz.unitY || gdiSizeF::operator !=(sz);
	}
	bool IsEmpty() const
	{
		return (unitX == gdiUnit_void && unitY == gdiUnit_void) || gdiSizeF::IsEmpty();
	}
	bool IsValid() const
	{
		return unitX != gdiUnit_void && unitY != gdiUnit_void && gdiSizeF::IsValid();
	}
};

struct gdiEXPORT gdiRectU:gdiRectF
{
	gdiUnitType unitX,unitY;

	gdiRectU()
	{
		unitX = gdiUnit_void;
		unitY = gdiUnit_void;
	}
	gdiRectU(gdinum _left,gdinum _top,gdinum _right,gdinum _bottom,gdiUnitType _unit)
		:gdiRectF(_left,_top,_right,_bottom)
	{
		unitX = _unit;
		unitY = _unit;
	}
	gdiRectU(const gdiPointF &pt,const gdiSizeF &sz,gdiUnitType _unit)
		:gdiRectF(pt,sz)
	{
		unitX = _unit;
		unitY = _unit;
	}
	gdiRectU(gdinum _left,gdinum _top,gdinum _right,gdinum _bottom,gdiUnitType _unitX,gdiUnitType _unitY)
		:gdiRectF(_left,_top,_right,_bottom)
	{
		unitX = _unitX;
		unitY = _unitY;
	}
	gdiRectU(const gdiPointF &pt,const gdiSizeF &sz,gdiUnitType _unitX,gdiUnitType _unitY)
		:gdiRectF(pt,sz)
	{
		unitX = _unitX;
		unitY = _unitY;
	}
	gdiRectU(const gdiPointU &pt,const gdiSizeU &sz)
		:gdiRectF(pt,sz)
	{
		if (pt.unitX != sz.unitX || pt.unitY != sz.unitY)
			gdiEsys::Throw("gdiRectU: point and size with inconsistent units");
		unitX = pt.unitX;
		unitY = pt.unitY;
	}


	void Empty()
	{
		gdiRectF::Empty();
		unitX = gdiUnit_void;
		unitY = gdiUnit_void;
	}
	bool operator==(const gdiRectU &rect) const
	{
		return unitX == rect.unitX && unitY == rect.unitY && gdiRectF::operator ==(rect);
	}
	bool operator!=(const gdiRectU &rect) const
	{
		return unitX != rect.unitX || unitY != rect.unitY || gdiRectF::operator !=(rect);
	}
	bool IsEmpty() const
	{
		return (unitX == gdiUnit_void && unitY == gdiUnit_void) || gdiRectF::IsEmpty();
	}
	bool IsValid() const
	{
		return unitX != gdiUnit_void && unitY != gdiUnit_void && gdiRectF::IsValid();
	}

	gdiPointU LeftTop() const {return gdiPointU(left,unitX,top,unitY);}
	gdiPointU LeftBottom() const {return gdiPointU(left,unitX,bottom,unitY);}
	gdiPointU RightTop() const {return gdiPointU(right,unitX,top,unitY);}
	gdiPointU RightBottom() const {return gdiPointU(right,unitX,bottom,unitY);}
	gdiPointU Center() const {return gdiPointU(left+Width()/2,unitX,top+Height()/2,unitY);}

	gdiSizeU Size() const {return gdiSizeU(right - left,unitX,bottom - top,unitY);}

	// returns true if point is within rectangle
	bool PtInRect(const gdiPointU &pt) const
	{
		if (pt.unitX != unitX || pt.unitY != unitY)
			gdiEsys::Throw("gdiRectU.PtInRect: point with inconsistent units");
		return gdiRectF::PtInRect(pt);
	}

	// set this rectangle to intersection of two others
	bool Intersect(const gdiRectU &rect1, const gdiRectU &rect2)
	{
		if (rect1.unitX != rect2.unitX || rect1.unitY != rect2.unitY)
			gdiEsys::Throw("gdiRectU.Intersect: rects with inconsistent units");
		return gdiRectF::Intersect(rect1,rect2);
	}
};

#define gdiPOINT_PER_INCH	72
typedef xblowColor gdiColor;
#define	gdiCOLOR_USED	((const gdiColor*)1)

class gdiBrush;
class gdiPen;
class gdiFont;
class gdiFontObject;
interface IgdiBitmap;

enum gdiBkMode
{
	gdiBk_Opaque = OPAQUE,
	gdiBk_Transparent = TRANSPARENT
};
enum	gdiTextMode
{
	gdiText_Clipped = ETO_CLIPPED,					/* The text will be clipped to the rectangle.									*/
	gdiText_GlyphIndex = ETO_GLYPH_INDEX,			/* The lpString array refers to an array returned from GetCharacterPlacement and*/
													/* should be parsed directly by GDI as no further language-specific processing	*/
													/* is required. Glyph indexing only applies to TrueType fonts, but the flag can */
													/* be used for bitmap and vector fonts to indicate that no further language		*/
													/* processing is necessary and GDI should process the string directly.			*/
													/* Note that all glyph indexes are 16-bit values even though the string is		*/
													/* assumed to be an array of 8-bit values for raster fonts.						*/
													/* For ExtTextOutW, the glyph indexes are saved to a metafile.					*/
													/* However, to display the correct characters the metafile must be played back	*/
													/* using the same font. For ExtTextOutA, the glyph indexes are not saved.		*/
 	gdiText_NumericsLatin = ETO_NUMERICSLATIN,		/* To display numbers, use European digits.										*/
	gdiText_NumericsLocal = ETO_NUMERICSLOCAL,		/* To display numbers, use digits appropriate to the locale.					*/
	gdiText_Opaque = ETO_OPAQUE,					/* The current background color should be used to fill the rectangle.			*/
//	gdiText_PDY = ETO_PDY,							/* When this is set, the array pointed to by lpDx contains pairs of values.		*/
													/* The first value of each pair is, as usual, the distance between origins of	*/
													/* adjacent character cells, but the second value is the displacement along the */
													/* vertical direction of the font.												*/
	gdiText_RTLreading = ETO_RTLREADING				/* Middle East language edition of Windows: If this value is specified and a	*/
													/* Hebrew or Arabic font is selected into the device context, the string is		*/
													/* output using right-to-left reading order. If this value is not specified,	*/
													/* the string is output in left-to-right order. The same effect can be achieved */
													/* by setting the TA_RTLREADING value in SetTextAlign. This value is preserved	*/
													/* for backward compatibility.													*/
};

enum	gdiTextAlignMode
{
	gdiTextAlign_Error = GDI_ERROR,
	gdiTextAlign_BaseLine = TA_BASELINE,			/* The reference point is on the base line of the text.							*/
	gdiTextAlign_Bottom = TA_BOTTOM,				/* The reference point is on the bottom edge of the bounding rectangle.			*/
	gdiTextAlign_Top = TA_TOP,						/* The reference point is on the top edge of the bounding rectangle.			*/
	gdiTextAlign_Center = TA_CENTER,				/* The reference point is aligned horizontally with the center of the bounding	*/
													/* rectangle.																	*/
	gdiTextAlign_Left = TA_LEFT,					/* The reference point is on the left edge of the bounding rectangle.			*/
	gdiTextAlign_Right = TA_RIGHT,					/* The reference point is on the right edge of the bounding rectangle.			*/
	gdiTextAlign_RTLreading = TA_RTLREADING,		/* Middle East language edition of Windows: The text is laid out in right to	*/
													/* left reading order, as opposed to the default left to right order. This only */
													/* applies when the font selected into the device context is either Hebrew or	*/
													/* Arabic.																		*/
	gdiTextAlign_NoUpdateCp = TA_NOUPDATECP,		/* The current position is not updated after each text output call.				*/
	gdiTextAlign_UpdateCp = TA_UPDATECP,			/* The current position is updated after each text output call.					*/


	/* When the current font has a vertical default base line (as with Kanji), the following values are used instead of				*/
	/* TA_BASELINE and TA_CENTER.																									*/

	/* Value Meaning : */
	gdiTextAlign_VbaseLine = VTA_BASELINE,			/* The reference point is on the base line of the text.							*/
	gdiTextAlign_Vcenter = VTA_CENTER				/* The reference point is aligned vertically with the center of the bounding	*/
													/* rectangle.																	*/

	/*
	Remarks:
	The bounding rectangle is a rectangle bounding all of the character cells in a string of text. 
	Its dimensions can be obtained by calling the GetTextExtentPoint32 function. 

	The text-alignment flags determine how the TextOut and ExtTextOut functions align a string of text in relation to the string's 
	reference point provided to TextOut or ExtTextOut. 

	The text-alignment flags are not necessarily single bit flags and may be equal to zero. The flags must be examined in groups of 
	related flags, as shown in the following list. 

	TA_LEFT, TA_RIGHT, and TA_CENTER
	TA_BOTTOM, TA_TOP, and TA_BASELINE
	TA_NOUPDATECP and TA_UPDATECP

	If the current font has a vertical default base line, the related flags are as shown in the following list. 

	TA_LEFT, TA_RIGHT, and VTA_BASELINE
	TA_BOTTOM, TA_TOP, and VTA_CENTER
	TA_NOUPDATECP and TA_UPDATECP
	*/
};

interface gdiEXPORT __declspec(uuid("{8F7B0CA3-D365-4cdc-B320-2599FDDF79D4}"))
	IgdiDC:public IxbObject
{
public:
	virtual void Select(const gdiFont &font) = 0;
	virtual void Select(const gdiFontObject &font) = 0;
	virtual void Select(const gdiPen &pen) = 0;
	virtual void Select(IgdiBitmap *pBmp) = 0;
	virtual void SelectClear() = 0;

	virtual long GetPixelPerInchX() const = 0;
	virtual long GetPixelPerInchY() const = 0;

	virtual gdiBkMode GetBkMode() const = 0;
	virtual void SetBkMode(gdiBkMode bkmode) = 0;

	gdiSizeLow GetTextSizeLow(const xbStringW &str) const
	{
		return GetTextSizeLow(str,str.Length);
	}
	virtual gdiSizeLow GetTextSizeLow(LPCWSTR pStr,int len) const = 0;
	virtual xbStringW GetTextFace() const = 0;
	virtual void SetTextColor(const gdiColor &clr) = 0;
	virtual void TextOut(long x,long y,gdiTextMode textMode,const gdiRectLow *pRect,LPCWSTR pStr,int len,const int *lpDX = NULL) = 0;
	void TextOut(long x,long y,gdiTextMode textMode,const gdiRectLow *pRect,const xbStringW &str,const int *lpDX = NULL)
	{
		TextOut(x,y,textMode,pRect,str,str.Length,lpDX);
	}
	virtual void GetTextExtentLow(LPCWSTR pStr,int len,long maxWidth,int *plenFit,gdiSizeLow *pSize,int *lpDX = NULL) const = 0;
	void GetTextExtentLow(const xbStringW &str,long maxWidth,int *plenFit,gdiSizeLow *pSize,int *lpDX = NULL) const
	{
		GetTextExtentLow(str,str.Length,maxWidth,plenFit,pSize,lpDX);
	}
	virtual void TextMetrics(TEXTMETRICW *pMetrics) const = 0;

	virtual void MoveToLow(long x,long y) = 0;
	virtual void LineToLow(long x,long y) = 0;
	virtual void VideoInvert(const gdiRectLow &rect) = 0;

	virtual xbRefPtr<IgdiDC> CreateCompatibleDC() const = 0;
	virtual void AlphaDraw(long xDst,long yDst,long widthDst,long heightDst,
		IgdiDC &dcSrc,long xSrc,long ySrc,long widthSrc,long heightSrc,
		int cAlpha) = 0;
	virtual void StdDraw(long xDst,long yDst,long widthDst,long heightDst,
		IgdiDC &dcSrc,long xSrc,long ySrc,long widthSrc,long heightSrc) = 0;

	virtual bool HasAlpha() const = 0;

	virtual bool DrawIcon(long xDst,long yDst,long widthDst,long heightDst,HICON hIcon,int nAni,gdiBrush *pBrush,xbOpMode opm = xbOp_Required) = 0;
	virtual void FillRect(const gdiRectLow *pRect,gdiBrush *pBrush) = 0;
};
#endif
