// XTPMarkupShape.cpp: implementation of the CXTPMarkupShape class.
//
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <math.h>

#include <Common/XTPVC80Helpers.h>

#include "XTPMarkupObject.h"
#include "XTPMarkupInputElement.h"
#include "XTPMarkupUIElement.h"
#include "XTPMarkupFrameworkElement.h"
#include "XTPMarkupShape.h"
#include "XTPMarkupDrawingContext.h"
#include "XTPMarkupBuilder.h"
#include "XTPMarkupThickness.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////////
// CXTPMarkupShape


CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pFillProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pStrokeProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pStrokeThicknessProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pStretchProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pStrokeDashArrayProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pStrokeStartLineCapProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pStrokeEndLineCapProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupShape::m_pStrokeLineJoinProperty = NULL;

IMPLEMENT_MARKUPCLASS(L"Shape", CXTPMarkupShape, CXTPMarkupFrameworkElement)

void CXTPMarkupShape::RegisterMarkupClass()
{
	m_pFillProperty = CXTPMarkupDependencyProperty::Register(L"Fill", MARKUP_TYPE(CXTPMarkupBrush), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsRender));

	m_pStrokeProperty = CXTPMarkupDependencyProperty::Register(L"Stroke", MARKUP_TYPE(CXTPMarkupBrush), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsRender));

	m_pStrokeThicknessProperty = CXTPMarkupDependencyProperty::Register(L"StrokeThickness", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsRender));

	m_pStretchProperty = CXTPMarkupDependencyProperty::Register(L"Stretch", MARKUP_TYPE(CXTPMarkupEnum), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(CXTPMarkupEnum::CreateValue(xtpMarkupStretchNone), &CXTPMarkupBuilder::ConvertStretch, CXTPMarkupPropertyMetadata::flagAffectsRender));

	m_pStrokeDashArrayProperty = CXTPMarkupDependencyProperty::Register(L"StrokeDashArray", MARKUP_TYPE(CXTPMarkupDoubleCollection), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsMeasure));

	m_pStrokeStartLineCapProperty = CXTPMarkupDependencyProperty::Register(L"StrokeStartLineCap", MARKUP_TYPE(CXTPMarkupEnum), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(CXTPMarkupEnum::CreateValue(xtpMarkupLineCapFlat), &CXTPMarkupShape::ConvertLineCap, CXTPMarkupPropertyMetadata::flagAffectsRender));

	m_pStrokeEndLineCapProperty = CXTPMarkupDependencyProperty::Register(L"StrokeEndLineCap", MARKUP_TYPE(CXTPMarkupEnum), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(CXTPMarkupEnum::CreateValue(xtpMarkupLineCapFlat), &CXTPMarkupShape::ConvertLineCap, CXTPMarkupPropertyMetadata::flagAffectsRender));

	m_pStrokeLineJoinProperty = CXTPMarkupDependencyProperty::Register(L"StrokeLineJoin", MARKUP_TYPE(CXTPMarkupEnum), MARKUP_TYPE(CXTPMarkupShape),
		new CXTPMarkupPropertyMetadata(CXTPMarkupEnum::CreateValue(xtpMarkupLineJoinMiter), &CXTPMarkupShape::ConvertLineJoin, CXTPMarkupPropertyMetadata::flagAffectsRender));
}

CXTPMarkupShape::CXTPMarkupShape()
{
}

CXTPMarkupShape::~CXTPMarkupShape()
{

}

CXTPMarkupBrush* CXTPMarkupShape::GetFill() const
{
	return MARKUP_STATICCAST(CXTPMarkupBrush, GetValue(m_pFillProperty));
}

void CXTPMarkupShape::SetFill(CXTPMarkupBrush* brush)
{
	SetValue(m_pFillProperty, brush);
}

CXTPMarkupBrush* CXTPMarkupShape::GetStroke() const
{
	return MARKUP_STATICCAST(CXTPMarkupBrush, GetValue(m_pStrokeProperty));
}

void CXTPMarkupShape::SetStroke(CXTPMarkupBrush* brush)
{
	SetValue(m_pStrokeProperty, brush);
}

void CXTPMarkupShape::GetStrokeStyle(CXTPMarkupStrokeStyle* pStrokeStyle) const
{
	pStrokeStyle->pStrokeBrush = GetStroke();
	pStrokeStyle->nStrokeThickness = pStrokeStyle->pStrokeBrush ? GetStrokeThickness() : 0;
	pStrokeStyle->pStrokeDashArray = GetStrokeDashArray();
	pStrokeStyle->nStrokeStartLineCap = GetStrokeStartLineCap();
	pStrokeStyle->nStrokeEndLineCap = GetStrokeEndLineCap();
	pStrokeStyle->nStrokeLineJoin = GetStrokeLineJoin();
}

CXTPMarkupObject* CXTPMarkupShape::ConvertLineCap(CXTPMarkupBuilder* /*pBuilder*/, CXTPMarkupObject* pObject)
{
	if (IsStringObject(pObject))
	{
		LPCWSTR lpszValue = *((CXTPMarkupString*)pObject);
		int nLength = ((CXTPMarkupString*)pObject)->GetLength();

		if (nLength == 4 && _wcsicmp(lpszValue, L"Flat") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupLineCapFlat);
		if (nLength == 6 && _wcsicmp(lpszValue, L"Square") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupLineCapSquare);
		if (nLength == 5 && _wcsicmp(lpszValue, L"Round") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupLineCapRound);
		if (nLength == 8 && _wcsicmp(lpszValue, L"Triangle") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupLineCapTriangle);

	}
	return NULL;
}

CXTPMarkupObject* CXTPMarkupShape::ConvertLineJoin(CXTPMarkupBuilder* /*pBuilder*/, CXTPMarkupObject* pObject)
{
	if (IsStringObject(pObject))
	{
		LPCWSTR lpszValue = *((CXTPMarkupString*)pObject);
		int nLength = ((CXTPMarkupString*)pObject)->GetLength();

		if (nLength == 5 && _wcsicmp(lpszValue, L"Miter") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupLineJoinMiter);
		if (nLength == 5 && _wcsicmp(lpszValue, L"Bevel") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupLineJoinBevel);
		if (nLength == 5 && _wcsicmp(lpszValue, L"Round") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupLineJoinRound);

	}
	return NULL;
}


//////////////////////////////////////////////////////////////////////////
// CXTPMarkupPointCollection

IMPLEMENT_MARKUPCLASS(NULL, CXTPMarkupPointCollection, CXTPMarkupObject)

void CXTPMarkupPointCollection::RegisterMarkupClass()
{

}

CXTPMarkupPointCollection::CXTPMarkupPointCollection()
{
	m_rcBounds.SetRectEmpty();
}

CXTPMarkupPointCollection::CXTPMarkupPointCollection(CPointArray& arr)
{
	m_rcBounds.SetRectEmpty();

	m_arrPoints.Copy(arr);

	int nCount = (int)m_arrPoints.GetSize();

	if (nCount > 0)
	{
		m_rcBounds.SetRect(INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);

		for (int i = 0; i < nCount; i++)
		{
			POINT& pt = m_arrPoints[i];
			if (pt.x > m_rcBounds.right) m_rcBounds.right = pt.x;
			if (pt.y > m_rcBounds.bottom) m_rcBounds.bottom = pt.y;
			if (pt.x < m_rcBounds.left) m_rcBounds.left = pt.x;
			if (pt.y < m_rcBounds.top) m_rcBounds.top = pt.y;
		}
	}
}


BOOL CXTPMarkupPointCollection::GetNextValue(LPCWSTR& lpszValue, int& nValue)
{
	while (*lpszValue == ' ') lpszValue++;
	LPCWSTR lpszNext = lpszValue;

	nValue = 0;
	int nSign = 1;
	if (*lpszNext == '-')
	{
		nSign = -1;
		lpszNext++;
	}

	if (*lpszNext == 0)
		return FALSE;

	while (*lpszNext != 0)
	{
		WCHAR c = *lpszNext;

		if (c == ' ' || c == ',')
		{
			if (lpszNext == lpszValue)
				return FALSE;
			lpszNext++;
			break;
		}

		if (c <'0' || c > '9')
			return FALSE;

		nValue = 10 * nValue + (c - '0');

		lpszNext++;
	}

	lpszValue = lpszNext;
	nValue *= nSign;

	return TRUE;
}

BOOL CXTPMarkupPointCollection::ConvertFromString(LPCWSTR lpszValue, CPointArray& arr)
{
	if (!lpszValue)
		return FALSE;

	while (*lpszValue != 0)
	{
		int x, y;
		if (!GetNextValue(lpszValue, x))
			return FALSE;

		if (!GetNextValue(lpszValue, y))
			return FALSE;

		while (*lpszValue == ' ') lpszValue++;

		POINT pt = {x, y};
		arr.Add(pt);
	}

	return TRUE;
}

CXTPMarkupObject* CXTPMarkupPointCollection::ConvertFrom(CXTPMarkupBuilder* /*pBuilder*/, CXTPMarkupObject* pObject) const
{
	if (IsStringObject(pObject))
	{
		CPointArray arr;
		if (ConvertFromString(*((CXTPMarkupString*)pObject), arr))
		{
			return new CXTPMarkupPointCollection(arr);
		}
	}

	return NULL;
}

//////////////////////////////////////////////////////////////////////////
// CXTPMarkupPolygon


CXTPMarkupDependencyProperty* CXTPMarkupPolygon::m_pPointsProperty = NULL;

IMPLEMENT_MARKUPCLASS(L"Polygon", CXTPMarkupPolygon, CXTPMarkupShape)

void CXTPMarkupPolygon::RegisterMarkupClass()
{
	m_pPointsProperty = CXTPMarkupDependencyProperty::Register(L"Points", MARKUP_TYPE(CXTPMarkupPointCollection), MARKUP_TYPE(CXTPMarkupPolygon),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsMeasure));

}

CXTPMarkupPolygon::CXTPMarkupPolygon()
{
}

CXTPMarkupPolygon::~CXTPMarkupPolygon()
{

}

void CXTPMarkupPolygon::SetPoints(CXTPMarkupPointCollection* pPoints)
{
	SetValue(m_pPointsProperty, pPoints);
}

CXTPMarkupPointCollection* CXTPMarkupPolygon::GetPoints() const
{
	return MARKUP_STATICCAST(CXTPMarkupPointCollection, GetValue(m_pPointsProperty));
}

CSize CXTPMarkupPolygon::MeasureOverride(CXTPMarkupDrawingContext* /*pDC*/, CSize szAvailableSize)
{
	CXTPMarkupPointCollection* pPoints = GetPoints();
	if (!pPoints)
		return CSize(0, 0);
	CRect rcBounds = pPoints->GetBounds();

	CSize szBound(max(rcBounds.Width(), rcBounds.right), max(rcBounds.Height(),rcBounds.bottom));

	if (GetStretch() != xtpMarkupStretchNone)
	{
		if (szAvailableSize.cx < INT_MAX)
			szBound.cx = szAvailableSize.cx;

		if (szAvailableSize.cy < INT_MAX)
			szBound.cy = szAvailableSize.cy;
	}
	return szBound;
}

void CXTPMarkupPolygon::OnRender(CXTPMarkupDrawingContext* pDC)
{
	CXTPMarkupStrokeStyle strokeStyle;
	GetStrokeStyle(&strokeStyle);

	CXTPMarkupBrush* pFillBrush = GetFill();

	if (!strokeStyle.pStrokeBrush && !pFillBrush)
		return;

	CXTPMarkupPointCollection* pPoints = GetPoints();
	if (!pPoints)
		return;

	const CXTPMarkupPointCollection::CPointArray& arr = pPoints->GetPoints();

	if (GetStretch() != xtpMarkupStretchNone)
	{
		CXTPMarkupPointCollection::CPointArray arrStretch;
		pPoints->Stretch(arrStretch, GetRenderSize());

		pDC->Polygon(arrStretch.GetData(), (int)arrStretch.GetSize(), &strokeStyle, pFillBrush);
	}
	else
	{
		pDC->Polygon(arr.GetData(), (int)arr.GetSize(), &strokeStyle, pFillBrush);
	}
}

//////////////////////////////////////////////////////////////////////////
// CXTPMarkupPolyline


CXTPMarkupDependencyProperty* CXTPMarkupPolyline::m_pPointsProperty = NULL;

IMPLEMENT_MARKUPCLASS(L"Polyline", CXTPMarkupPolyline, CXTPMarkupShape)

void CXTPMarkupPolyline::RegisterMarkupClass()
{
	m_pPointsProperty = CXTPMarkupDependencyProperty::Register(L"Points", MARKUP_TYPE(CXTPMarkupPointCollection), MARKUP_TYPE(CXTPMarkupPolyline),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsMeasure));
}

CXTPMarkupPolyline::CXTPMarkupPolyline()
{
}

CXTPMarkupPolyline::~CXTPMarkupPolyline()
{

}

void CXTPMarkupPolyline::SetPoints(CXTPMarkupPointCollection* pPoints)
{
	SetValue(m_pPointsProperty, pPoints);
}

CXTPMarkupPointCollection* CXTPMarkupPolyline::GetPoints() const
{
	return MARKUP_STATICCAST(CXTPMarkupPointCollection, GetValue(m_pPointsProperty));
}

CSize CXTPMarkupPolyline::MeasureOverride(CXTPMarkupDrawingContext* /*pDC*/, CSize szAvailableSize)
{
	CXTPMarkupPointCollection* pPoints = GetPoints();
	if (!pPoints)
		return CSize(0, 0);
	CRect rcBounds = pPoints->GetBounds();

	CSize szBound(max(rcBounds.Width(), rcBounds.right), max(rcBounds.Height(),rcBounds.bottom));

	if ((szBound.cx == 0 || szBound.cy == 0) && GetStroke())
	{
		int nStroke = (GetStrokeThickness() + 1) / 2;

		if (szBound.cy == 0) szBound.cy += nStroke;
		if (szBound.cx == 0) szBound.cx += nStroke;
	}

	if (GetStretch() != xtpMarkupStretchNone)
	{
		if (szAvailableSize.cx < INT_MAX)
			szBound.cx = szAvailableSize.cx;

		if (szAvailableSize.cy < INT_MAX)
			szBound.cy = szAvailableSize.cy;
	}
	return szBound;
}

void CXTPMarkupPointCollection::Stretch(CPointArray& arrStretch, CSize sz)
{
	const CPointArray& arr = GetPoints();
	arrStretch.Copy(arr);
	int nCount = (int)arrStretch.GetSize();

	CRect rc = GetBounds();
	if (rc.Width() == 0 && rc.Height() == 0)
		return;

	for (int i = 0; i < nCount; i++)
	{
		POINT& pt = arrStretch[i];
		if (rc.Width() != 0) pt.x = (pt.x - rc.left) * sz.cx / rc.Width();
		if (rc.Height() != 0) pt.y = (pt.y - rc.top) * sz.cy / rc.Height();
	}
}

void CXTPMarkupPolyline::OnRender(CXTPMarkupDrawingContext* pDC)
{
	CXTPMarkupStrokeStyle strokeStyle;
	GetStrokeStyle(&strokeStyle);

	if (strokeStyle.nStrokeThickness <= 0)
		return;

	CXTPMarkupPointCollection* pPoints = GetPoints();
	if (!pPoints)
		return;

	const CXTPMarkupPointCollection::CPointArray& arr = pPoints->GetPoints();

	if (GetStretch() != xtpMarkupStretchNone)
	{
		CXTPMarkupPointCollection::CPointArray arrStretch;
		pPoints->Stretch(arrStretch, GetRenderSize());

		pDC->Polyline(arrStretch.GetData(), (int)arrStretch.GetSize(), &strokeStyle);
	}
	else
	{
		pDC->Polyline(arr.GetData(), (int)arr.GetSize(), &strokeStyle);
	}
}




//////////////////////////////////////////////////////////////////////////
// CXTPMarkupRectangle

IMPLEMENT_MARKUPCLASS(L"Rectangle", CXTPMarkupRectangle, CXTPMarkupShape)

CXTPMarkupDependencyProperty* CXTPMarkupRectangle::m_pRadiusXProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupRectangle::m_pRadiusYProperty = NULL;

void CXTPMarkupRectangle::RegisterMarkupClass()
{
	m_pRadiusXProperty = CXTPMarkupDependencyProperty::Register(L"RadiusX", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupRectangle),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsRender));

	m_pRadiusYProperty = CXTPMarkupDependencyProperty::Register(L"RadiusY", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupRectangle),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsRender));

}

CXTPMarkupRectangle::CXTPMarkupRectangle()
{
}

CXTPMarkupRectangle::~CXTPMarkupRectangle()
{

}

CSize CXTPMarkupRectangle::MeasureOverride(CXTPMarkupDrawingContext* /*pDC*/, CSize szAvailableSize)
{
	CSize szBound(0, 0);

	if (GetStretch() != xtpMarkupStretchNone)
	{
		if (szAvailableSize.cx < INT_MAX)
			szBound.cx = szAvailableSize.cx;

		if (szAvailableSize.cy < INT_MAX)
			szBound.cy = szAvailableSize.cy;
	}

	return szBound;
}

void CXTPMarkupRectangle::OnRender(CXTPMarkupDrawingContext* pDC)
{
	CXTPMarkupBrush* pStrokeBrush = GetStroke();
	CXTPMarkupBrush* pFillBrush = GetFill();

	if (!pStrokeBrush && !pFillBrush)
		return;

	int nStroke = pStrokeBrush ? GetStrokeThickness() : 0;

	CRect rc(0, 0, GetRenderSize().cx, GetRenderSize().cy);
	if (rc.Width() == 0 || rc.Height() == 0)
		return;

	double nRadiusX = GetRadiusX(), nRadiusY = GetRadiusY();
	nRadiusX = min((double)(rc.Width() - nStroke) / 2, nRadiusX);
	nRadiusY = min((double)(rc.Height() - nStroke) / 2, nRadiusY);

	double* pnCorners = NULL;
	if (nRadiusX > 0 && nRadiusY > 0)
	{
		pnCorners = new double[8];
		pnCorners[0] = pnCorners[3] = pnCorners[4] = pnCorners[7] = (double)nRadiusY;
		pnCorners[1] = pnCorners[2] = pnCorners[5] = pnCorners[6] = (double)nRadiusX;
	}

	if (pFillBrush)
	{
		CRect rcFill(rc);
		rcFill.DeflateRect(nStroke / 2, nStroke / 2);

		pDC->FillRectangle(rcFill, pFillBrush, pnCorners);
	}

	if (nStroke > 0)
	{
		CXTPMarkupThickness strokeThickness(nStroke);
		pDC->DrawRectangle(rc, pStrokeBrush, &strokeThickness, pnCorners);
	}

	if (pnCorners) delete[] pnCorners;
}


//////////////////////////////////////////////////////////////////////////
// CXTPMarkupEllipse


IMPLEMENT_MARKUPCLASS(L"Ellipse", CXTPMarkupEllipse, CXTPMarkupShape)

void CXTPMarkupEllipse::RegisterMarkupClass()
{

}

CXTPMarkupEllipse::CXTPMarkupEllipse()
{
}

CXTPMarkupEllipse::~CXTPMarkupEllipse()
{

}

CSize CXTPMarkupEllipse::MeasureOverride(CXTPMarkupDrawingContext* /*pDC*/, CSize szAvailableSize)
{
	CSize szBound(0, 0);

	if (GetStretch() != xtpMarkupStretchNone)
	{
		if (szAvailableSize.cx < INT_MAX)
			szBound.cx = szAvailableSize.cx;

		if (szAvailableSize.cy < INT_MAX)
			szBound.cy = szAvailableSize.cy;
	}

	return szBound;
}

void CXTPMarkupEllipse::OnRender(CXTPMarkupDrawingContext* pDC)
{

	CXTPMarkupStrokeStyle strokeStyle;
	GetStrokeStyle(&strokeStyle);

	CXTPMarkupBrush* pFillBrush = GetFill();

	if (!strokeStyle.pStrokeBrush && !pFillBrush)
		return;

	pDC->Ellipse(CRect(0, 0, GetRenderSize().cx, GetRenderSize().cy), &strokeStyle, pFillBrush);
}

CXTPMarkupInputElement* CXTPMarkupEllipse::InputHitTestOverride(CPoint point) const
{
	if (m_szRenderSize.cx < 1 || m_szRenderSize.cy < 1)
		return (CXTPMarkupInputElement*)this;

   // Determine radii
	double a = m_szRenderSize.cx / 2;
	double b = m_szRenderSize.cy / 2;

	double x = point.x - a;
	double y = point.y - b;

	return ((x * x) / (a * a) + (y * y) / (b * b) <= 1) ? (CXTPMarkupInputElement*)this : NULL;
}


//////////////////////////////////////////////////////////////////////////
// CXTPMarkupLine


CXTPMarkupDependencyProperty* CXTPMarkupLine::m_pX1Property = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupLine::m_pY1Property = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupLine::m_pX2Property = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupLine::m_pY2Property = NULL;


IMPLEMENT_MARKUPCLASS(L"Line", CXTPMarkupLine, CXTPMarkupShape)

void CXTPMarkupLine::RegisterMarkupClass()
{
	m_pX1Property = CXTPMarkupDependencyProperty::Register(L"X1", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupLine),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsMeasure));

	m_pY1Property = CXTPMarkupDependencyProperty::Register(L"Y1", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupLine),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsMeasure));

	m_pX2Property = CXTPMarkupDependencyProperty::Register(L"X2", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupLine),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsMeasure));

	m_pY2Property = CXTPMarkupDependencyProperty::Register(L"Y2", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupLine),
		new CXTPMarkupPropertyMetadata(NULL, CXTPMarkupPropertyMetadata::flagAffectsMeasure));
}

CXTPMarkupLine::CXTPMarkupLine()
{
}

CXTPMarkupLine::~CXTPMarkupLine()
{

}

int CXTPMarkupLine::GetX1() const
{
	CXTPMarkupInt* pValue = MARKUP_STATICCAST(CXTPMarkupInt, GetValue(m_pX1Property));
	return pValue != NULL ? (int)*pValue : 0;
}

int CXTPMarkupLine::GetX2() const
{
	CXTPMarkupInt* pValue = MARKUP_STATICCAST(CXTPMarkupInt, GetValue(m_pX2Property));
	return pValue != NULL ? (int)*pValue : 0;
}

int CXTPMarkupLine::GetY1() const
{
	CXTPMarkupInt* pValue = MARKUP_STATICCAST(CXTPMarkupInt, GetValue(m_pY1Property));
	return pValue != NULL ? (int)*pValue : 0;
}

int CXTPMarkupLine::GetY2() const
{
	CXTPMarkupInt* pValue = MARKUP_STATICCAST(CXTPMarkupInt, GetValue(m_pY2Property));
	return pValue != NULL ? (int)*pValue : 0;
}

CSize CXTPMarkupLine::MeasureOverride(CXTPMarkupDrawingContext* /*pDC*/, CSize szAvailableSize)
{
	int x1 = GetX1();
	int x2 = GetX2();
	int y1 = GetY1();
	int y2 = GetY2();

	int x = max(x1, x2);
	int y = max(y1, y2);

	if ((x1 == x2 || y1 == y2) && GetStroke())
	{
		int nStroke = (GetStrokeThickness() + 1) / 2;

		if (x1 == x2) x += nStroke;
		if (y1 == y2) y += nStroke;
	}

	CSize szBound(x, y);

	if (GetStretch() != xtpMarkupStretchNone)
	{
		if (szAvailableSize.cx < INT_MAX)
			szBound.cx = szAvailableSize.cx;

		if (szAvailableSize.cy < INT_MAX)
			szBound.cy = szAvailableSize.cy;
	}
	return szBound;

}

void CXTPMarkupLine::GetPoints(int& x1, int& y1, int& x2, int& y2) const
{
	x1 = GetX1();
	x2 = GetX2();
	y1 = GetY1();
	y2 = GetY2();

	if (GetStretch() != xtpMarkupStretchNone)
	{
		int x = max(x1, x2);
		int y = max(y1, y2);

		CSize sz = GetRenderSize();
		if (x1 == x2)
			x1 = x2 = sz.cx / 2;
		else
		{
			x1 = (x1 == x ? sz.cx : 0);
			x2 = (x2 == x ? sz.cx : 0);
		}

		if (y1 == y2)
			y1 = y2 = sz.cy / 2;
		else
		{
			y1 = (y1 == y ? sz.cy : 0);
			y2 = (y2 == y ? sz.cy : 0);
		}
	}
}

void CXTPMarkupLine::OnRender(CXTPMarkupDrawingContext* pDC)
{
	CXTPMarkupStrokeStyle strokeStyle;
	GetStrokeStyle(&strokeStyle);

	if (strokeStyle.nStrokeThickness <= 0)
		return;

	int x1, x2, y1, y2;
	GetPoints(x1, y1, x2, y2);

	if (x1 == 0 && x2 == 0 && y1 == 0 && y2 == 0)
		return;

	pDC->DrawLine(x1, y1, x2, y2, &strokeStyle);
}

CXTPMarkupInputElement* CXTPMarkupLine::InputHitTestOverride(CPoint point) const
{
	if (!GetStroke())
		return NULL;

	int nStroke = GetStrokeThickness();
	if (nStroke == 0)
		return NULL;

	int x1, x2, y1, y2;
	GetPoints(x1, y1, x2, y2);

	double x = x2 - x1;
	double y = y2 - y1;

	double xp = point.x - x1;
	double yp = point.y - y1;

	double s = (double)nStroke / 2.0;

	if (xp + s < min(x, 0) || xp - s > max(x, 0) || yp + s < min(y, 0) || yp - s > max(y, 0))
		return NULL;

	if (x == 0)
		return fabs(xp) <= s ? (CXTPMarkupInputElement*)this : NULL;

	double yStroke = fabs(s / cos(atan(y / x)));

	double y0 = y * xp / (double)x;

	return fabs(y0 - yp) <= yStroke ? (CXTPMarkupInputElement*)this : NULL;
}