// Utility.h : Deklaracja klas użytkowych

#ifndef __UTILITY_H_
#define __UTILITY_H_

/////////////////////////////////////////////////////////////////////////////
// CSize - Zakres; przypomina strukturę Windows SIZE.

class CPoint;
class CRect;

class CSize : public tagSIZE
{
public:

// Konstruktury
	// tworzy niezainicjalizowaną wielkość
	CSize() { /* wypełnioną losową */ }
	// tworzenie na podstawie dwóch zmiennych
	CSize(int initCX, int initCY) { cx = initCX; cy = initCY; }
	// tworzenie na podstawie innej wielkości
	CSize(SIZE initSize) { *(SIZE*)this = initSize; }
	// tworzenie na podstawie punktu
	CSize(POINT initPt) { *(POINT*)this = initPt; }
	// tworzenie na podstawie DWORD: cx = LOWORD(dw) cy = HIWORD(dw)
    CSize(DWORD dwSize) { cx = (short)LOWORD(dwSize); cy = (short)HIWORD(dwSize); }

// CSize

// Operacje
    BOOL operator==(SIZE size) const          { return (cx == size.cx && cy == size.cy); }
    BOOL operator!=(SIZE size) const          { return (cx != size.cx || cy != size.cy); }
    void operator+=(SIZE size)                { cx += size.cx; cy += size.cy; }
    void operator-=(SIZE size)                { cx -= size.cx; cy -= size.cy; }

// Operatory zwracające wartości CSize
    CSize operator+(SIZE size) const          { return CSize(cx + size.cx, cy + size.cy); }
    CSize operator-(SIZE size) const          { return CSize(cx - size.cx, cy - size.cy); }
    CSize operator-() const                   { return CSize(-cx, -cy); }

// Operatory zwracające wartości CPoint
    CPoint operator+(POINT point) const ;
    CPoint operator-(POINT point) const ;

// Operatory zwracające wartości CRect
    CRect operator+(const RECT* lpRect) const ;
    CRect operator-(const RECT* lpRect) const ;
};

/////////////////////////////////////////////////////////////////////////////
// CPoint - dwuwymiarowy punkt; przypomina strukturę Windows POINT.


class CPoint : public tagPOINT
{
public:
// Konstruktory

	// tworzy niezainicjalizowany punkt
	CPoint() { /* wypełniony losowo */ }
	// tworzenie na podstawie dwóch zmiennych
	CPoint(int initX, int initY) { x = initX; y = initY; }
	// tworzenie na podstawie innego punktu
	CPoint(POINT initPt) { *(POINT*)this = initPt; }
	// tworzenie na podstawie wielkości
	CPoint(SIZE initSize) { *(SIZE*)this = initSize; }
	// worzenie na podstawie DWORD: x = LOWORD(dw) y = HIWORD(dw)
	CPoint(DWORD dwPoint) {	x = (short)LOWORD(dwPoint);	y = (short)HIWORD(dwPoint);	}

// Operacje

// przekształcenia punktu
	void Offset(int xOffset, int yOffset) { x += xOffset; y += yOffset; }
	void Offset(POINT point) { x += point.x; y += point.y; }
	void Offset(SIZE size) { x += size.cx; y += size.cy; }

    BOOL operator==(POINT point) const  { return (x == point.x && y == point.y); }
    BOOL operator!=(POINT point) const  { return (x != point.x || y != point.y); }
    void operator+=(SIZE size)          { x += size.cx; y += size.cy; }
    void operator-=(SIZE size)          { x -= size.cx; y -= size.cy; }
    void operator+=(POINT point)        { x += point.x; y += point.y; }
    void operator-=(POINT point)        { x -= point.x; y -= point.y; }

// Operatory zwracające wartości CPoint
    CPoint operator+(SIZE size) const   { return CPoint(x + size.cx, y + size.cy); }
    CPoint operator-(SIZE size) const   { return CPoint(x - size.cx, y - size.cy); }
    CPoint operator-() const            { return CPoint(-x, -y); }
    CPoint operator+(POINT point) const { return CPoint(x + point.x, y + point.y); }

// Operatory zwracające wartości CSize
    CSize operator-(POINT point) const  { return CSize(x - point.x, y - point.y); }

// Operatory zwracające wartości CRect
    CRect operator+(const RECT* lpRect) const ;
    CRect operator-(const RECT* lpRect) const ;
};

/////////////////////////////////////////////////////////////////////////////
// CRect - dwuwymiarowy prostokąt; przypomina strukturę Windows RECT.

typedef const RECT* LPCRECT;    // wskaźnik do RECT tylko do odczytu

class CRect : public tagRECT
{
public:

// Konstruktory

	// niezainicjalizowany prostokąt
	CRect() { /* v */ }
	// na podstawie wierzchołków
	CRect(int l, int t, int r, int b) { left = l; top = t; right = r; bottom = b; }
	// copy constructor
	CRect(const RECT& srcRect) { ::CopyRect(this, &srcRect); }
	// na podstawie wskaźnika do innego prostokąta
	CRect(LPCRECT lpSrcRect) { ::CopyRect(this, lpSrcRect); }
	// na podstawie punktu i wielkości
	CRect(POINT point, SIZE size) { right = (left = point.x) + size.cx; bottom = (top = point.y) + size.cy; }
	// na podstawie dwóch punktów
	CRect(POINT topLeft, POINT bottomRight) { left = topLeft.x; top = topLeft.y; right = bottomRight.x; bottom = bottomRight.y; }

// Atrybuty (uzupełniające elementy składowe RECT)

	// zwraca szerokość
	int Width() const { return right - left; }
	// zwraca wysokość
	int Height() const { return bottom - top; }
	// zwraca wielkość
	CSize Size() const { return CSize(right - left, bottom - top); }
	// odwołanie do lewego górnego wierzchołka
	CPoint& TopLeft() { return *((CPoint*)this); }
	// odwołanie do prawego dolnego wierzchołka
	CPoint& BottomRight() { return *((CPoint*)this+1); }
	// odwołanie typu const do lewego górnego wierzchołka
	const CPoint& TopLeft() const { return *((CPoint*)this); }
	// odwołanie typu const do prawego dolnego wierzchołka
	const CPoint& BottomRight() const { return *((CPoint*)this+1); }
	// środek geometryczny prostokąta
	CPoint CenterPoint() const { return CPoint((left+right)/2, (top+bottom)/2); }
	// odwrócenie prostokąta
	void SwapLeftRight() { SwapLeftRight(LPRECT(this)); }
	static void SwapLeftRight(LPRECT lpRect) { LONG temp = lpRect->left; lpRect->left = lpRect->right; lpRect->right = temp; }

	// konwersja pomiędzy CRect i LPRECT/LPCRECT (nie ma potrzeby użycia &)
	operator LPRECT() { return this; }
	operator LPCRECT() const { return this; }

	// zwraca TRUE, jeśli prostokąt nie ma powierzchni
	BOOL IsRectEmpty() const { return ::IsRectEmpty(this); }
	// zwraca TRUE, jeśli prostokąt znajduje się w (0, 0) i nie ma powierzchni
	BOOL IsRectNull() const { return (left == 0 && right == 0 && top == 0 && bottom == 0); }
	// zwraca TRUE, jeśli punkt znajduje się wewnątrz prostokąta
	BOOL PtInRect(POINT point) const { return ::PtInRect(this, point); }


// Operacje

	// utworzenie prostokąta
	void SetRect(int x1, int y1, int x2, int y2) { ::SetRect(this, x1, y1, x2, y2); }
	void SetRect(POINT topLeft, POINT bottomRight) { ::SetRect(this, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); }
	// skasowanie prostokąta
	void SetRectEmpty() { ::SetRectEmpty(this); }
	// skopiowanie innego prostokąta
	void CopyRect(LPCRECT lpSrcRect) { ::CopyRect(this, lpSrcRect); }
	// TRUE, jeśli dwa prostokąty są identyczne
	BOOL EqualRect(LPCRECT lpRect) const { return ::EqualRect(this, lpRect); }

	// powiększenie szerokości i wysokości prostokąta
	void InflateRect(int x, int y) { ::InflateRect(this, x, y); }
	void InflateRect(SIZE size) { ::InflateRect(this, size.cx, size.cy); }
    void InflateRect(LPCRECT lpRect)
    {
	    left   -= lpRect->left;
	    top    -= lpRect->top;
	    right  += lpRect->right;
	    bottom += lpRect->bottom;
    }

    void InflateRect(int l, int t, int r, int b)
    {
	    left   -= l;
	    top    -= t;
	    right  += r;
	    bottom += b;
    }

	// zmniejszenie szerokości i wysokości prostokąta
	void DeflateRect(int x, int y) { ::InflateRect(this, -x, -y); }
	void DeflateRect(SIZE size) { ::InflateRect(this, -size.cx, -size.cy); }
    void DeflateRect(LPCRECT lpRect)
    {
	    left   += lpRect->left;
	    top    += lpRect->top;
	    right  -= lpRect->right;
	    bottom -= lpRect->bottom;
    }

    void DeflateRect(int l, int t, int r, int b)
    {
	    left   += l;
	    top    += t;
	    right  -= r;
	    bottom -= b;
    }


	// przeniesienie prostokąta
	void OffsetRect(int x, int y) { ::OffsetRect(this, x, y); }
	void OffsetRect(SIZE size) { ::OffsetRect(this, size.cx, size.cy); }
	void OffsetRect(POINT point) { ::OffsetRect(this, point.x, point.y); }
    void NormalizeRect()
    {
	    int nTemp;
        if (left > right) { nTemp = left; left = right;   right = nTemp; }
        if (top > bottom) { nTemp = top;   top = bottom; bottom = nTemp; }
    }

	// przecięcie dwóch prostokątów
	BOOL IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2) { return ::IntersectRect(this, lpRect1, lpRect2);}

	// wspólna część dwóch prostokątów
	BOOL UnionRect(LPCRECT lpRect1, LPCRECT lpRect2) { return ::UnionRect(this, lpRect1, lpRect2); }

// Dodatkowe operacje
    void operator=(const RECT& srcRect) { ::CopyRect(this, &srcRect); }
    BOOL operator==(const RECT& rect) const { return ::EqualRect(this, &rect); }
    BOOL operator!=(const RECT& rect) const { return !::EqualRect(this, &rect); }
    void operator+=(POINT point)            { ::OffsetRect(this, point.x, point.y); }
    void operator+=(SIZE size)              { ::OffsetRect(this, size.cx, size.cy); }
    void operator+=(LPCRECT lpRect)         { InflateRect(lpRect); }
    void operator-=(POINT point)            { ::OffsetRect(this, -point.x, -point.y); }
    void operator-=(SIZE size)              { ::OffsetRect(this, -size.cx, -size.cy); }
    void operator-=(LPCRECT lpRect)         { DeflateRect(lpRect); }
    void operator&=(const RECT& rect)       { ::IntersectRect(this, this, &rect); }
    void operator|=(const RECT& rect)       { ::UnionRect(this, this, &rect); }

// Operatory zwracające wartości CRect
    CRect MulDiv(int nMultiplier, int nDivisor) const
    {
	return CRect(
		::MulDiv(left, nMultiplier, nDivisor),
		::MulDiv(top, nMultiplier, nDivisor),
		::MulDiv(right, nMultiplier, nDivisor),
		::MulDiv(bottom, nMultiplier, nDivisor));
    }

    CRect operator+(POINT pt) const { CRect rect(*this); ::OffsetRect(&rect, pt.x, pt.y); return rect; }
    CRect operator-(POINT pt) const { CRect rect(*this); ::OffsetRect(&rect, -pt.x, -pt.y); return rect; }
    CRect operator+(SIZE size) const { CRect rect(*this); ::OffsetRect(&rect, size.cx, size.cy); return rect; }
    CRect operator-(SIZE size) const { CRect rect(*this); ::OffsetRect(&rect, -size.cx, -size.cy); return rect; }
    CRect operator+(LPCRECT lpRect) const { CRect rect(this); rect.InflateRect(lpRect); return rect; }
    CRect operator-(LPCRECT lpRect) const { CRect rect(this); rect.DeflateRect(lpRect); return rect; }
    CRect operator&(const RECT& rect2) const { CRect rect; ::IntersectRect(&rect, this, &rect2); return rect; }
    CRect operator|(const RECT& rect2) const { CRect rect; ::UnionRect(&rect, this, &rect2); return rect; }
};

// Operatory zwracające wartości CPoint
inline CPoint CSize::operator+(POINT point) const       { return CPoint(cx + point.x, cy + point.y); }
inline CPoint CSize::operator-(POINT point) const       { return CPoint(cx - point.x, cy - point.y); }

// Operatory zwracające wartości CRect
inline CRect CSize::operator+(const RECT* lpRect) const { return CRect(lpRect) + *this; }
inline CRect CSize::operator-(const RECT* lpRect) const { return CRect(lpRect) - *this; }

// Operatory zwracające wartości CRect
inline CRect CPoint::operator+(const RECT* lpRect) const { return CRect(lpRect) + *this; }
inline CRect CPoint::operator-(const RECT* lpRect) const { return CRect(lpRect) - *this; }

#endif
