// XTPMarkupUIElement.cpp: implementation of the CXTPMarkupUIElement 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 "XTPMarkupObject.h"
#include "XTPMarkupInputElement.h"
#include "XTPMarkupUIElement.h"
#include "XTPMarkupDrawingContext.h"
#include "XTPMarkupContext.h"
#include "XTPMarkupBuilder.h"

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


IMPLEMENT_MARKUPCLASS(NULL, CXTPMarkupVisual, CXTPMarkupInputElement);

void CXTPMarkupVisual::RegisterMarkupClass()
{

}

CXTPMarkupVisual::CXTPMarkupVisual()
{
	m_ptVisualOffset = CPoint(0, 0);
	m_szRenderSize = 0;

	m_rcBoundRect.SetRectEmpty();
}

BOOL CXTPMarkupVisual::GetLayoutClip(CRect& /*rc*/) const
{
	return FALSE;
}

void CXTPMarkupVisual::RenderClipped(CXTPMarkupDrawingContext* drawingContext)
{
	int nVisualCount = GetVisualChildrenCount();
	for (int i = 0; i < nVisualCount; i++)
	{
		GetVisualChild(i)->RenderClipped(drawingContext);
	}
}

void CXTPMarkupVisual::Render(CXTPMarkupDrawingContext* drawingContext)
{
	CRect rcClipBox = drawingContext->GetClipBox();
	rcClipBox.OffsetRect(-m_ptVisualOffset);

	RECT rcIntersect;
	if (!::IntersectRect(&rcIntersect, GetBoundRect(), &rcClipBox))
	{
		RenderClipped(drawingContext);
		return;
	}

	CRect rcLayoutClip(0, 0, 0, 0);
	BOOL bClipLayout = FALSE;

	if (GetLayoutClip(rcLayoutClip))
	{
		if (rcLayoutClip.IsRectEmpty())
		{
			RenderClipped(drawingContext);
			return;
		}

		rcClipBox.IntersectRect(rcClipBox, rcLayoutClip);

		if (!::IntersectRect(&rcIntersect, GetBoundRect(), &rcClipBox))
		{
			RenderClipped(drawingContext);
			return;
		}

		bClipLayout = TRUE;
	}

	drawingContext->OffsetViewport(m_ptVisualOffset);

	HRGN hrgnClip = 0;

	if (bClipLayout)
	{
		hrgnClip = drawingContext->SaveClipRegion();

		drawingContext->IntersectClipRect(rcLayoutClip);
	}

	OnRender(drawingContext);

	int nVisualCount = GetVisualChildrenCount();
	for (int i = 0; i < nVisualCount; i++)
	{
		GetVisualChild(i)->Render(drawingContext);
	}

	if (IsKeyboardFocused())
	{
		OnRenderFocusVisual(drawingContext);

	}

	if (hrgnClip != NULL)
	{
		drawingContext->RestoreClipRegion(hrgnClip);
	}

	drawingContext->OffsetViewport(-m_ptVisualOffset);
}

int CXTPMarkupVisual::GetVisualChildrenCount() const
{
	return 0;
}

CXTPMarkupVisual* CXTPMarkupVisual::GetVisualChild(int /*nIndex*/) const
{
	ASSERT(FALSE);
	return NULL;
}

int CXTPMarkupVisual::GetLogicalChildrenCount() const
{
	return GetVisualChildrenCount();
}

CXTPMarkupObject* CXTPMarkupVisual::GetLogicalChild(int nIndex) const
{
	return GetVisualChild(nIndex);
}

CXTPMarkupInputElement* CXTPMarkupVisual::InputHitTest(CPoint point) const
{
	CPoint ptVisualPoint = point - m_ptVisualOffset;

	if (!m_rcBoundRect.PtInRect(ptVisualPoint))
		return NULL;

	CRect rcClip;
	if (GetLayoutClip(rcClip))
	{
		if (!rcClip.PtInRect(ptVisualPoint))
			return NULL;
	}

	int nVisualCount = GetVisualChildrenCount();
	for (int i = nVisualCount - 1; i >= 0; i--)
	{
		CXTPMarkupInputElement* pObject = GetVisualChild(i)->InputHitTest(ptVisualPoint);
		if (pObject)
			return pObject;
	}

	CRect rcRender(0, 0, m_szRenderSize.cx, m_szRenderSize.cy);
	if (!rcRender.PtInRect(ptVisualPoint))
		return NULL;

	return InputHitTestOverride(ptVisualPoint);
}

void CXTPMarkupVisual::UpdateBoundRect()
{
	m_rcBoundRect.SetRect(0, 0, m_szRenderSize.cx, m_szRenderSize.cy);

	int nVisualCount = GetVisualChildrenCount();
	for (int i = 0; i < nVisualCount; i++)
	{
		CXTPMarkupVisual* pVisual = GetVisualChild(i);
		pVisual->UpdateBoundRect();

		CRect rcBoundRect = pVisual->m_rcBoundRect;
		rcBoundRect.OffsetRect(pVisual->m_ptVisualOffset);

		m_rcBoundRect.UnionRect(m_rcBoundRect, rcBoundRect);
	}
}

CXTPMarkupInputElement* CXTPMarkupVisual::InputHitTestOverride(CPoint /*point*/) const
{
	return (CXTPMarkupInputElement*)this;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXTPMarkupDependencyProperty* CXTPMarkupUIElement::m_pClipToBoundsProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupUIElement::m_pVisibilityProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupUIElement::m_pFocusableProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupUIElement::m_pIsEnabledProperty = NULL;


IMPLEMENT_MARKUPCLASS(NULL, CXTPMarkupUIElement, CXTPMarkupVisual);

void CXTPMarkupUIElement::RegisterMarkupClass()
{
	m_pClipToBoundsProperty = CXTPMarkupDependencyProperty::Register(L"ClipToBounds", MARKUP_TYPE(CXTPMarkupBool), MARKUP_TYPE(CXTPMarkupUIElement));

	m_pVisibilityProperty = CXTPMarkupDependencyProperty::Register(L"Visibility", MARKUP_TYPE(CXTPMarkupEnum), MARKUP_TYPE(CXTPMarkupUIElement),
		new CXTPMarkupPropertyMetadata(NULL, &CXTPMarkupBuilder::ConvertVisibility, CXTPMarkupPropertyMetadata::flagAffectsMeasure));

	m_pFocusableProperty = CXTPMarkupDependencyProperty::Register(L"Focusable", MARKUP_TYPE(CXTPMarkupBool), MARKUP_TYPE(CXTPMarkupUIElement),
		new CXTPMarkupPropertyMetadata(CXTPMarkupBool::CreateFalseValue()));

	m_pIsEnabledProperty = CXTPMarkupDependencyProperty::Register(L"IsEnabled", MARKUP_TYPE(CXTPMarkupBool), MARKUP_TYPE(CXTPMarkupUIElement),
		new CXTPMarkupPropertyMetadata(CXTPMarkupBool::CreateTrueValue()));
}

CXTPMarkupUIElement::CXTPMarkupUIElement()
{
	m_szDesiredSize = 0;
	m_rcFinalRect.SetRectEmpty();

	m_bNeverMeasured = TRUE;
	m_bMeasureDirty = FALSE;

	m_bNeverArranged = TRUE;
	m_bArrangeDirty = FALSE;

	m_szPreviousAvailableSize = 0;
	m_bArrangeInProgress = FALSE;
	m_bMeasureInProgress = FALSE;
}

CXTPMarkupUIElement::~CXTPMarkupUIElement()
{

}

CXTPMarkupVisual* CXTPMarkupVisual::GetVisualParent() const
{
	CXTPMarkupObject* pUIElement = m_pLogicalParent;

	while (pUIElement && !pUIElement->IsKindOf(MARKUP_TYPE(CXTPMarkupVisual)))
	{
		pUIElement = pUIElement->GetLogicalParent();
	}

	return (CXTPMarkupVisual*)pUIElement;
}

void CXTPMarkupUIElement::InvalidateArrange()
{
	if (!m_bArrangeInProgress && !m_bNeverArranged)
	{
		m_bArrangeDirty = TRUE;

		Arrange(m_rcFinalRect);
		InvalidateVisual();
	}
}

void CXTPMarkupUIElement::InvalidateMeasure()
{
	CXTPMarkupDrawingContext dc(GetMarkupContext());
	InvalidateMeasureOverride(&dc);
}

void CXTPMarkupUIElement::InvalidateMeasureOverride(CXTPMarkupDrawingContext* pDC)
{
	if (!m_bMeasureInProgress && !m_bNeverMeasured)
	{
		m_bMeasureDirty = TRUE;
		m_bArrangeDirty = TRUE;

		CSize sz = GetDesiredSize();

		Measure(pDC, m_szPreviousAvailableSize);

		if (sz != GetDesiredSize())
		{
			m_bMeasureDirty = TRUE;
			m_bArrangeDirty = TRUE;

			CXTPMarkupUIElement* pParent = MARKUP_DYNAMICCAST(CXTPMarkupUIElement, GetVisualParent());
			if (pParent)
			{
				pParent->InvalidateMeasureOverride(pDC);
			}
			else if (m_pMarkupContext)
			{
				m_pMarkupContext->OnInvalidateArrange(this);
			}
		}
		else
		{
			Arrange(m_rcFinalRect);
			InvalidateVisual();
		}
	}
}

void CXTPMarkupUIElement::InvalidateVisual()
{
	if (m_pMarkupContext) m_pMarkupContext->OnInvalidateVisual(this);
}

void CXTPMarkupUIElement::Arrange(CRect rcFinalRect)
{
	if (GetVisibility() == xtpMarkupVisibilityCollapsed)
	{
		m_rcFinalRect = rcFinalRect;
		m_bArrangeDirty = FALSE;
		m_bNeverArranged = FALSE;
		return;
	}

	if (m_bNeverMeasured)
	{
		CXTPMarkupDrawingContext dc(GetMarkupContext());
		Measure(&dc, rcFinalRect.Size());
	}

	if ((m_bArrangeDirty || m_bNeverArranged) || (rcFinalRect != m_rcFinalRect))
	{
		m_bNeverArranged = FALSE;
		m_bArrangeInProgress = TRUE;
		ArrangeCore(rcFinalRect);
		m_bArrangeInProgress = FALSE;

		UpdateBoundRect();
	}

	m_rcFinalRect = rcFinalRect;
	m_bArrangeDirty = FALSE;
}

void CXTPMarkupUIElement::ArrangeCore(CRect rcFinalRect)
{
	m_szRenderSize = rcFinalRect.Size();
}

void CXTPMarkupUIElement::Measure(CXTPMarkupDrawingContext* pDC, CSize szAvailableSize)
{
	if (GetVisibility() == xtpMarkupVisibilityCollapsed)
	{
		m_bMeasureDirty = TRUE;
		m_bNeverMeasured = FALSE;
		m_szPreviousAvailableSize = szAvailableSize;
		m_szDesiredSize = CSize(0, 0);
		return;
	}

	if ((!m_bMeasureDirty && !m_bNeverMeasured) && (szAvailableSize == m_szPreviousAvailableSize) && !pDC->IsPrinting())
	{
		return;
	}

	m_bNeverMeasured = FALSE;

	m_bArrangeDirty = TRUE;

	m_bMeasureInProgress = TRUE;
	CSize size = MeasureCore(pDC, szAvailableSize);
	m_bMeasureInProgress = FALSE;

	m_szDesiredSize = size;
	m_szPreviousAvailableSize = szAvailableSize;
	m_bMeasureDirty = pDC->IsPrinting() ? TRUE : FALSE;
}

CSize CXTPMarkupUIElement::MeasureCore(CXTPMarkupDrawingContext* /*pDC*/, CSize /*szAvailableSize*/)
{
	return CSize(0, 0);
}

BOOL CXTPMarkupUIElement::GetLayoutClip(CRect& rc) const
{
	if (GetVisibility() != xtpMarkupVisibilityVisible)
	{
		rc.SetRectEmpty();
		return TRUE;
	}
	return FALSE;
}