#include "Painter.h"
#include "Window.h"
#include "Widget.h"


#include <assert.h>
#define DEBUG_WIDGET  0
#if DEBUG_WIDGET
#  include <stdio.h>
#  include <typeinfo>
#endif


bool c_useRetina = true;
float c_retinaScale = 2.0f;


BEGIN_NAMESPACE

	
Widget::Widget(Object* a_parent, const char* a_name)
  : Widget(ObjectPtrCast<Widget>(a_parent), a_name)
{
}


Widget::Widget(const char* a_name, bool a_root)
  : BaseT(nullptr, a_name)
{
  assert(a_root);
  m_layoutDirection = LP_Horizontal;
  m_windowX = m_windowY = 0;
  m_watchingMouse = false;
  m_delayDelete = false;
  m_hasUnmanagedDescendant = false;
  m_x = m_y = 0;
  m_goalWidth = m_goalHeight = 1;
  layoutMargin = 5;
  layoutSpacing = 5;
  backgroundBrush = 0xF0F0F0;
  flags = WF_None;
  scale = 1.0f;
  m_combinedScale = 1.0f;
}


Widget::Widget(Widget* a_parent, const char* a_name, LayoutPolicy a_layoutDirection, int a_x, int a_y, int a_width, int a_height)
	: BaseT(a_parent ? a_parent : new Window(a_name, true), a_name), m_layoutDirection(a_layoutDirection)
{
  // TODO: when automatically creating a parent window when parent is 0, we need
  // a way to automatically destroy things - currently destruction is not well tested.
  // Need to try create and destroy a bunch of widgets and see if the windows can be removed.
  // May require adding a flag to say if we automatically created the parent window or not.
  // Or perhaps using shared_ptr and then if the window has no children it destroys itself?
  layoutMargin = 5;
  layoutSpacing = 5;
  backgroundBrush = 0xF0F0F0;
  m_windowX = m_windowY = 0;
  m_watchingMouse = false;
  m_delayDelete = false;
  m_hasUnmanagedDescendant = false;
  scale = 1.0f;
  Widget* widgetParent = parent<Widget>();
  m_combinedScale = (widgetParent) ? widgetParent->m_combinedScale : 1.0f;
  connect(scale.valueChanged, this, &Widget::updateScale);
  setGeometry(a_x, a_y, a_width, a_height);
  flags = WF_None;
  if (m_layoutDirection == LP_Unmanaged)
    setParentHasUnmanagedChild();
}


Widget::~Widget()
{
}


void Widget::updateScale(float ignored)
{
  Widget* widgetParent = parent<Widget>();
  m_combinedScale = (widgetParent) ? scale.value() * widgetParent->m_combinedScale : scale.value();

  std::list<Widget*> widgetChildren = children<Widget>();
  for (Widget* child : widgetChildren)
  {
    child->updateScale(0.0f);
  }
}


void Widget::setParentHasUnmanagedChild()
{
  Widget* widgetParent = parent<Widget>();
  if (widgetParent)
    widgetParent->setParentHasUnmanagedChild();
  m_hasUnmanagedDescendant = true;
}


void Widget::childAdded(Object* a_object)
{
  Widget* widgetParent = parent<Widget>();
	if (widgetParent)
		widgetParent->updateLayout();
	else
		updateLayout();
	repaint();
}


void Widget::updateLayout()
{
  std::list<Widget*> widgetChildren = children<Widget>();
  
	// recalculate layout
	int childCount = (int)widgetChildren.size();
	if (childCount)
	{
		SizeOptions childTotalSize;
		Vector<SizeOptions> childSizes;
		childSizes.resize(childCount);
		memset(&childTotalSize, 0, sizeof(childTotalSize));

		int i = 0;
		for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it)
		{
      (*it)->scaledSizeOptions(childSizes[i]);

      childTotalSize.m_minimum.m_width    += childSizes[i].m_minimum.m_width;
      childTotalSize.m_minimum.m_height   += childSizes[i].m_minimum.m_height;

      childTotalSize.m_preferred.m_width  += childSizes[i].m_preferred.m_width;
      childTotalSize.m_preferred.m_height += childSizes[i].m_preferred.m_height;

      childTotalSize.m_maximum.m_width    += childSizes[i].m_maximum.m_width;
      childTotalSize.m_maximum.m_height   += childSizes[i].m_maximum.m_height;

      i++;
		}

		int x = 0, y = 0, wid = width(), heig = height();
		int extraSpaceForMarginsAndSpacing = (childCount - 1) * layoutSpacing() + layoutMargin() * 2;

    // TODO: refactor the horz/vert cases to one set of code as mostly the same
		if (m_layoutDirection == LP_Horizontal)
		{
			// horz
			if ( wid > childTotalSize.m_maximum.m_width )
			{
				// TODO: need to do padding horizontally
				wid = childTotalSize.m_maximum.m_width;
			}
			if ( wid < childTotalSize.m_minimum.m_width )
			{
				// TODO: can't meet the constraint
				wid = childTotalSize.m_minimum.m_width;
			}
			if ( wid < extraSpaceForMarginsAndSpacing )
			{
				// TODO: can't meet the constraint
				wid = extraSpaceForMarginsAndSpacing;
			}

			int w = wid - extraSpaceForMarginsAndSpacing;
			int h = heig - layoutMargin() * 2;
			int remainder = w - childTotalSize.m_preferred.m_width;
			Vector<int> proposedChildWidths;
			Vector<bool> childWidthDone;
			int childWidthDoneCount = 0;
			proposedChildWidths.resize(childCount);
			childWidthDone.resize(childCount);
			for (int i = 0; i < childCount; i++)
			{
				proposedChildWidths[i] = childSizes[i].m_preferred.m_width;
				childWidthDone[i] = false;
			}
			while (abs(remainder) > (childCount-childWidthDoneCount))
			{
				int distributedRemainder = remainder / (childCount-childWidthDoneCount);

				for (int i = 0; i < childCount; i++)
				{
					if (!childWidthDone[i]) {
						if (remainder < 0) {
							if (proposedChildWidths[i] + distributedRemainder < childSizes[i].m_minimum.m_width)
							{
								proposedChildWidths[i] = childSizes[i].m_minimum.m_width;
								childWidthDone[i] = true;
								childWidthDoneCount++;
							} else {
								proposedChildWidths[i] += distributedRemainder;
							}
						} else {
							if (proposedChildWidths[i] + distributedRemainder > childSizes[i].m_maximum.m_width)
							{
								proposedChildWidths[i] = childSizes[i].m_maximum.m_width;
								childWidthDone[i] = true;
								childWidthDoneCount++;
							} else {
								proposedChildWidths[i] += distributedRemainder;
							}
						}
					}
				}

				int newTotalProposedWidth = 0;
				for (int i = 0; i < childCount; i++)
					newTotalProposedWidth += proposedChildWidths[i];
				remainder = w - newTotalProposedWidth;

				if (childWidthDoneCount >= childCount)
					break;
			}

			int i = 0;
			y = x = layoutMargin();
			for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it, x += proposedChildWidths[i] + layoutSpacing(), i++)
				(*it)->setGeometry(x, y, proposedChildWidths[i], h);

		}
    else // if (m_layoutDirection == LP_Vertical)
    {
			// vert
			if ( heig > childTotalSize.m_maximum.m_height )
			{
				// TODO: need to do padding horizontally
				heig = childTotalSize.m_maximum.m_height;
			}
			if ( heig < childTotalSize.m_minimum.m_height )
			{
				// TODO: can't meet the constraint
				heig = childTotalSize.m_minimum.m_height;
			}
			if ( heig < extraSpaceForMarginsAndSpacing )
			{
				// TODO: can't meet the constraint
				heig = extraSpaceForMarginsAndSpacing;
			}

			int h = heig - extraSpaceForMarginsAndSpacing;
			int w = wid - layoutMargin() * 2;
			int remainder = h - childTotalSize.m_preferred.m_height;
			Vector<int> proposedChildHeight;
			Vector<bool> childHeightDone;
			int childHeightDoneCount = 0;
			proposedChildHeight.resize(childCount);
			childHeightDone.resize(childCount);
			for (int i = 0; i < childCount; i++)
			{
				proposedChildHeight[i] = childSizes[i].m_preferred.m_height;
				childHeightDone[i] = false;
			}
			while (abs(remainder) > (childCount-childHeightDoneCount))
			{
				int distributedRemainder = remainder / (childCount-childHeightDoneCount);

				for (int i = 0; i < childCount; i++)
				{
					if (!childHeightDone[i]) {
						if (remainder < 0) {
							if (proposedChildHeight[i] + distributedRemainder < childSizes[i].m_minimum.m_height)
							{
								proposedChildHeight[i] = childSizes[i].m_minimum.m_height;
								childHeightDone[i] = true;
								childHeightDoneCount++;
							} else {
								proposedChildHeight[i] += distributedRemainder;
							}
						} else {
							if (proposedChildHeight[i] + distributedRemainder > childSizes[i].m_maximum.m_height)
							{
								proposedChildHeight[i] = childSizes[i].m_maximum.m_height;
								childHeightDone[i] = true;
								childHeightDoneCount++;
							} else {
								proposedChildHeight[i] += distributedRemainder;
							}
						}
					}
				}

				int newTotalProposedHeight = 0;
				for (int i = 0; i < childCount; i++)
					newTotalProposedHeight += proposedChildHeight[i];
				remainder = h - newTotalProposedHeight;

				if (childHeightDoneCount >= childCount)
					break;
			}

			int i = 0;
			y = x = layoutMargin();
			for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it, y += proposedChildHeight[i] + layoutSpacing(), i++)
				(*it)->setGeometry(x, y, w, proposedChildHeight[i]);
		}
	}
}


void Widget::sizeOptions(SizeOptions& a_sizeOptions)
{
	a_sizeOptions.m_minimum.m_width    = 0;
	a_sizeOptions.m_minimum.m_height   = 0;
	a_sizeOptions.m_preferred.m_width  = 256;
	a_sizeOptions.m_preferred.m_height = 256;
	a_sizeOptions.m_maximum.m_width    = 65536;
	a_sizeOptions.m_maximum.m_height   = 65536;
	//return;

  std::list<Widget*> widgetChildren = children<Widget>();
	int childCount = (int)widgetChildren.size();
	if (!childCount)
	{
		// TODO
		// Bottom level widgets that have no children ought to implement sizeOptions
		return;
	}

	SizeOptions childSizes;
	if (m_layoutDirection == LP_Horizontal)
	{
		// horiz
		a_sizeOptions.m_minimum.m_height = 0;
		a_sizeOptions.m_preferred.m_height = 0;
		a_sizeOptions.m_maximum.m_height = 65536;

		a_sizeOptions.m_minimum.m_width = 0;
		a_sizeOptions.m_preferred.m_width = 0;
		a_sizeOptions.m_maximum.m_width = 0;

		for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it)
		{
			(*it)->scaledSizeOptions(childSizes);

			a_sizeOptions.m_minimum.m_width   += childSizes.m_minimum.m_width;
			a_sizeOptions.m_preferred.m_width += childSizes.m_preferred.m_width;
			a_sizeOptions.m_maximum.m_width   += childSizes.m_maximum.m_width;

			a_sizeOptions.m_minimum.m_height    = std::max(a_sizeOptions.m_minimum.m_height, childSizes.m_minimum.m_height);
			a_sizeOptions.m_preferred.m_height += childSizes.m_preferred.m_height;
			a_sizeOptions.m_maximum.m_height    = std::min(a_sizeOptions.m_maximum.m_height, childSizes.m_maximum.m_height);
		}

		a_sizeOptions.m_preferred.m_height /= childCount; // average of preferred heights

		int extraSpaceForMarginsAndSpacing = layoutMargin() * 2;
		a_sizeOptions.m_minimum.m_height   += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_preferred.m_height += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_maximum.m_height   += extraSpaceForMarginsAndSpacing;

		extraSpaceForMarginsAndSpacing += (childCount - 1) * layoutSpacing();
		a_sizeOptions.m_minimum.m_width   += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_preferred.m_width += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_maximum.m_width   += extraSpaceForMarginsAndSpacing;

	}
  else // if (m_layoutDirection == LP_Vertical)
  {
		// vert
		a_sizeOptions.m_minimum.m_width = 0;
		a_sizeOptions.m_preferred.m_width = 0;
		a_sizeOptions.m_maximum.m_width = 65536;

		a_sizeOptions.m_minimum.m_height = 0;
		a_sizeOptions.m_preferred.m_height = 0;
		a_sizeOptions.m_maximum.m_height = 0;

		for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it)
		{
			(*it)->scaledSizeOptions(childSizes);

			a_sizeOptions.m_minimum.m_height   += childSizes.m_minimum.m_height;
			a_sizeOptions.m_preferred.m_height += childSizes.m_preferred.m_height;
			a_sizeOptions.m_maximum.m_height   += childSizes.m_maximum.m_height;

			a_sizeOptions.m_minimum.m_width    = std::max(a_sizeOptions.m_minimum.m_width, childSizes.m_minimum.m_width);
			a_sizeOptions.m_preferred.m_width += childSizes.m_preferred.m_width;
			a_sizeOptions.m_maximum.m_width    = std::min(a_sizeOptions.m_maximum.m_width, childSizes.m_maximum.m_width);
		}

		a_sizeOptions.m_preferred.m_width /= childCount; // average of preferred heights

		int extraSpaceForMarginsAndSpacing = layoutMargin() * 2;
		a_sizeOptions.m_minimum.m_width   += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_preferred.m_width += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_maximum.m_width   += extraSpaceForMarginsAndSpacing;

		extraSpaceForMarginsAndSpacing += (childCount - 1) * layoutSpacing();
		a_sizeOptions.m_minimum.m_height   += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_preferred.m_height += extraSpaceForMarginsAndSpacing;
		a_sizeOptions.m_maximum.m_height   += extraSpaceForMarginsAndSpacing;
	}

	// ensure min is less than max and clamp the preferred to the min and max
	if ( a_sizeOptions.m_maximum.m_height < a_sizeOptions.m_minimum.m_height )
		a_sizeOptions.m_maximum.m_height = a_sizeOptions.m_minimum.m_height = (a_sizeOptions.m_maximum.m_height + a_sizeOptions.m_minimum.m_height) / 2;
	if ( a_sizeOptions.m_maximum.m_width < a_sizeOptions.m_minimum.m_width )
		a_sizeOptions.m_maximum.m_width = a_sizeOptions.m_minimum.m_width = (a_sizeOptions.m_maximum.m_width + a_sizeOptions.m_minimum.m_width) / 2;
	if ( a_sizeOptions.m_preferred.m_height < a_sizeOptions.m_minimum.m_height )
		a_sizeOptions.m_preferred.m_height = a_sizeOptions.m_minimum.m_height;
	if ( a_sizeOptions.m_preferred.m_width < a_sizeOptions.m_minimum.m_width )
		a_sizeOptions.m_preferred.m_width = a_sizeOptions.m_minimum.m_width;
	if ( a_sizeOptions.m_preferred.m_height > a_sizeOptions.m_maximum.m_height )
		a_sizeOptions.m_preferred.m_height = a_sizeOptions.m_maximum.m_height;
	if ( a_sizeOptions.m_preferred.m_width > a_sizeOptions.m_maximum.m_width )
		a_sizeOptions.m_preferred.m_width = a_sizeOptions.m_maximum.m_width;
 
  // We are the topLevel widget before the window widget
  Widget* widgetParent = parent<Widget>();
  if (widgetParent && !widgetParent->parent<Widget>())
  {
    Rectangle rect = widgetParent->rectangle();
    a_sizeOptions.m_maximum = rect.m_size;
  }
}


void Widget::setGeometry(int a_x, int a_y, int a_width, int a_height)
{
	m_windowX = m_x = a_x;
	m_windowY = m_y = a_y;
	m_goalWidth = a_width;// / 2.0;// * m_combinedScale;// / 2.0;
	m_goalHeight = a_height;// / 2.0;// * m_combinedScale;// / 2.0;
  Widget* widgetParent = parent<Widget>();
	if (widgetParent)
	{
    if (c_useRetina)
    {
      m_goalWidth *= 2;
      m_goalHeight *= 2;
    }
    //m_goalWidth = (a_width / c_retinaScale) * 2.0;
    //m_goalHeight = (a_height / c_retinaScale) * 2.0;
		m_windowX = widgetParent->m_windowX + a_x;
		m_windowY = widgetParent->m_windowY + a_y;
		updateTarget(); // update target buffer
	}

  // TODO: if a child geometry changes, does that bubble up to the parent?
  // If so, layout updates need to ascend up to the root parent, and then do a full re-layout all the way down

  // TODO: also need to be careful. doing updateLayout will call setGeometry on widgets which will call updateLayout etc.
  // Also in the constructor, it calls setGeometry and it also does this before the widget it added to the parent's children
  // Adding a child widget causes a full re-layout from the root down

  // TODO: setGeometry on the root (ie: window) should re-size the underlying window. possibly need a virtual function for this
	updateLayout();
}


TimerId Widget::startTimer(int a_milliSeconds)
{
  Widget* widgetParent = parent<Widget>();
	if (widgetParent)
		return widgetParent->startTimer(a_milliSeconds);
	return 0;
}


void Widget::killTimer(TimerId a_timerId)
{
  Widget* widgetParent = parent<Widget>();
	if (widgetParent)
		widgetParent->killTimer(a_timerId);
}


void Widget::eraseBackground(int i)
{
	Painter p(this);
	p.reset();
/* 
	p.setBrush(backgroundBrush());
#if DEBUG_WIDGET
	printf("draw rect: %i %i %i %i\n", 0, 0, width(), height());
#endif
	p.drawRectangle(0, 0, width(), height());
*/
}


int Widget::width()
{
  if (c_useRetina)
	  return targetBuffer().m_width / 2;
	return targetBuffer().m_width;// * 2;
}


int Widget::height()
{
  if (c_useRetina)
	  return targetBuffer().m_height / 2;
	return targetBuffer().m_height;// * 2;
}


void Widget::sizeEvent(SizeEvent& a_event)
{
}


void Widget::paintEvent(PaintEvent& a_event)
{
	eraseBackground();
}


void Widget::glPaint()
{
}


void Widget::timerEvent(TimerEvent& a_event)
{
}


void Widget::keyEvent(KeyEvent& a_event)
{
}


void Widget::mouseEvent(MouseEvent& a_event)
{
}


void Widget::mouseEnterEvent(MouseEvent& a_event)
{
}


void Widget::mouseLeaveEvent(MouseEvent& a_event)
{
}


void Widget::wheelEvent(WheelEvent& a_event)
{
}


void Widget::updateTarget()
{
  Widget* widgetParent = parent<Widget>();
 
    /*
  if (widgetParent)
  {
    PixelBuffer& parentTarget = widgetParent->targetBuffer();
    m_goalWidth = clamp(m_goalWidth, 0, parentTarget.m_width);
    m_goalHeight = clamp(m_goalHeight, 0, parentTarget.m_height);
    if (parentTarget.m_isRetina)
    {
      m_target.m_width = clamp(parentTarget.m_width - m_x*2 - 1, 0, m_goalWidth);
      m_target.m_height = clamp(parentTarget.m_height - m_y*2 - 1, 0, m_goalHeight);
    }
    else
    {
      m_target.m_width = clamp(parentTarget.m_width - m_x - 1, 0, m_goalWidth);
      m_target.m_height = clamp(parentTarget.m_height - m_y - 1, 0, m_goalHeight);
    }
  }
  */

  if (m_layoutDirection == LP_Unmanaged)
  {
    m_target.m_width = m_goalWidth;   // FIXME: still needs to be clamped though
    m_target.m_height = m_goalHeight; // ditto
    
    //m_target.m_width = clamp(m_goalWidth, 0, m_target.m_width);
    //m_target.m_height = clamp(m_goalHeight, 0, m_target.m_height);
  }
  else if (widgetParent)
  {
    PixelBuffer& parentTarget = widgetParent->targetBuffer();
    m_target.m_width = clamp(parentTarget.m_width - m_x - 1, 0, m_goalWidth);
    m_target.m_height = clamp(parentTarget.m_height - m_y - 1, 0, m_goalHeight);

    
//    m_goalWidth = clamp(m_goalWidth, 0, parentTarget.m_width - m_x - 1);
//    m_goalHeight = clamp(m_goalHeight, 0, parentTarget.m_height - m_y - 1);
    //m_target.m_width = clamp(m_goalWidth, 0, parentTarget.m_width - m_x - 1);
    //m_target.m_height = clamp(m_goalHeight, 0, parentTarget.m_height - m_y - 1);
  }

  int maxWidth = m_target.m_width - m_x - 1;
  int maxHeight = m_target.m_height - m_y - 1;

  if (widgetParent)
  {
    PixelBuffer& parentTarget = widgetParent->targetBuffer();
    m_target.m_pixels = parentTarget.m_pixels + m_y * (parentTarget.m_strideBytes/4) + m_x;
    m_target.m_strideBytes = parentTarget.m_strideBytes;
    m_target.m_format = parentTarget.m_format;
    m_target.m_isRetina = parentTarget.m_isRetina;
    
    maxWidth = parentTarget.m_width - m_x - 1;
    maxHeight = parentTarget.m_height - m_y - 1;

#if USE_RETINA
    if (c_useRetina)
    {
      maxWidth = parentTarget.m_width - m_x*2 - 1;
      maxHeight = parentTarget.m_height - m_y*2 - 1;
      
      //m_target.m_pixels = parentTarget.m_pixels + int(m_y * c_retinaScale) * (parentTarget.m_strideBytes/4) + int(m_x * c_retinaScale);
      m_target.m_pixels = parentTarget.m_pixels + int(m_y * 2.0) * (parentTarget.m_strideBytes/4) + int(m_x * 2.0);
    }
#endif

  }
  else
  {
    m_target.m_isRetina = false;
    m_target.m_pixels = m_target.m_pixels + m_y * (m_target.m_strideBytes/4) + m_x;
  }

  if (m_layoutDirection != LP_Unmanaged)
  {
    m_target.m_width = clamp(m_target.m_width, 0, maxWidth);
    m_target.m_height = clamp(m_target.m_height, 0, maxHeight);
  }

}


void Widget::repaint()
{
	Rectangle rectangle = { { { 0, 0 } }, { { width(), height() } } };
	update(rectangle);
}


void Widget::update(Rectangle& a_rectangle)
{
  Widget* widgetParent = parent<Widget>();
	if (widgetParent)
	{
		a_rectangle.m_x += m_x;
		a_rectangle.m_y += m_y;
		widgetParent->update(a_rectangle); // This recursively calculates m_windowX and m_windowY
		// When this gets up to the top level widget it will be a window object with overloaded update function
	}
}


void Widget::event(Event& a_event)
{
	a_event.m_commonEvent.reject();

	switch (a_event.m_type)
	{
		case Event::ET_TimerEvent:
			timerEvent(a_event.m_timerEvent);
			break;
		case Event::ET_PaintEvent:
      if (flags() == WF_EraseBackground)
        eraseBackground();
      paintEvent(a_event.m_paintEvent);
			break;
		case Event::ET_SizeEvent:
			if (parent<Widget>())
				updateTarget();
			sizeEvent(a_event.m_sizeEvent);
			break;
		default:
			break;
	}

  std::list<Widget*> widgetChildren = children<Widget>();

/*
  // Code if not dealing with unmanaged layouts
  for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it)
  {
#if DEBUG_WIDGET
    printf("event, child class: %s\n", typeid(*(*it)).name());
#endif
    (*it)->event(a_event);
    if (a_event.m_commonEvent.m_accepted)
      return;
  }
*/

  // Code to deal with event propogation with unmanaged layouts
  // The order depends on event type. paint events go dirst to managed, then the unmanaged
  // layout widgets are drawn last. For input events, the managed widgets are processed first.
	if (a_event.m_type == Event::ET_PaintEvent)
  {
    std::vector<Widget*> unmanagedWidgets;
    for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it)
    {
      if ((*it)->m_hasUnmanagedDescendant)
      {
        unmanagedWidgets.push_back(*it);
      }
      else
      {
        (*it)->event(a_event);
        if (a_event.m_commonEvent.m_accepted)
          return;
      }
    }
    for (Widget* w : unmanagedWidgets)
    {
      w->event(a_event);
      if (a_event.m_commonEvent.m_accepted)
        return;
    }
  }
  else
  {
    std::vector<Widget*> managedWidgets;
    for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it)
    {
      if ((*it)->m_hasUnmanagedDescendant)
      {
        (*it)->event(a_event);
        if (a_event.m_commonEvent.m_accepted)
          return;
      }
      else
      {
        managedWidgets.push_back(*it);
      }
    }
    for (Widget* w : managedWidgets)
    {
      w->event(a_event);
      if (a_event.m_commonEvent.m_accepted)
        return;
    }
  }


	bool mouseInside = false;
	switch (a_event.m_type)
	{
		case Event::ET_MouseEvent:
			mouseInside = ((a_event.m_mouseEvent.m_position.m_x > m_windowX)
				&& (a_event.m_mouseEvent.m_position.m_y > m_windowY)
				&& (a_event.m_mouseEvent.m_position.m_x < (m_windowX + width()))
				&& (a_event.m_mouseEvent.m_position.m_y < (m_windowY + height())));
			if (m_mouseEntered && !mouseInside)
			{
				mouseLeaveEvent(a_event.m_mouseEvent);
			}
			if (!m_mouseEntered && mouseInside)
			{
				mouseEnterEvent(a_event.m_mouseEvent);
			}
			m_mouseEntered = mouseInside;
			if (mouseInside	|| m_watchingMouse )
			{
        MouseEvent pushEvent = a_event.m_mouseEvent;
				a_event.m_mouseEvent.m_x = (pushEvent.m_x - m_windowX);
				a_event.m_mouseEvent.m_y = (pushEvent.m_y - m_windowY);
				a_event.m_mouseEvent.m_oldX = (pushEvent.m_oldX - m_windowX);
				a_event.m_mouseEvent.m_oldY = (pushEvent.m_oldY - m_windowY);
				//a_event.m_mouseEvent.m_x = (pushEvent.m_x - m_windowX) * c_retinaScale;
				//a_event.m_mouseEvent.m_y = (pushEvent.m_y - m_windowY) * c_retinaScale;
				//a_event.m_mouseEvent.m_oldX = (pushEvent.m_oldX - m_windowX) * c_retinaScale;
				//a_event.m_mouseEvent.m_oldY = (pushEvent.m_oldY - m_windowY) * c_retinaScale;
				//a_event.m_mouseEvent.m_x = (pushEvent.m_x * 2.0f / c_retinaScale) - m_windowX;
				//a_event.m_mouseEvent.m_y = (pushEvent.m_y * 2.0f / c_retinaScale) - m_windowY;
				//a_event.m_mouseEvent.m_oldX = (pushEvent.m_oldX * 2.0f / c_retinaScale) - m_windowX;
				//a_event.m_mouseEvent.m_oldY = (pushEvent.m_oldY * 2.0f / c_retinaScale) - m_windowY;
				if (!a_event.m_mouseEvent.m_oldButtons && a_event.m_mouseEvent.m_buttons)
					m_watchingMouse = true;
				if (!a_event.m_mouseEvent.m_buttons)
					m_watchingMouse = false;
				mouseEvent(a_event.m_mouseEvent);
        a_event.m_mouseEvent = pushEvent; // pop
			}
			break;
		case Event::ET_WheelEvent:
			wheelEvent(a_event.m_wheelEvent);
			break;
		case Event::ET_KeyEvent:
			keyEvent(a_event.m_keyEvent);
			break;
		default:
			break;
	}

  if (!parent())
  {
    deleteDelayDeleteWidgets();
  }
}


void Widget::delayDelete()
{
  m_delayDelete = true;
}


void Widget::collectDelayDeleteWidgets(std::vector<Widget*>& a_widgetsToBeDeleted) const
{
  std::list<Widget*> widgetChildren = children<Widget>();
  for (std::list<Widget*>::const_iterator it = widgetChildren.begin(); it != widgetChildren.end(); ++it)
  {
    (*it)->collectDelayDeleteWidgets(a_widgetsToBeDeleted);
    if ((*it)->m_delayDelete)
    {
      a_widgetsToBeDeleted.emplace_back(*it);
    }
  }
}


void Widget::deleteDelayDeleteWidgets()
{
  std::vector<Widget*> widgetsToBeDeleted;
  collectDelayDeleteWidgets(widgetsToBeDeleted);
  for (auto w : widgetsToBeDeleted)
  {
    delete w;
  }
}


void Widget::scaledSizeOptions(SizeOptions& a_sizeOptions)
{
  sizeOptions(a_sizeOptions);
  a_sizeOptions.m_minimum.m_width *= m_combinedScale;
  a_sizeOptions.m_minimum.m_height *= m_combinedScale;
  a_sizeOptions.m_preferred.m_width *= m_combinedScale;
  a_sizeOptions.m_preferred.m_height *= m_combinedScale;
  a_sizeOptions.m_maximum.m_width *= m_combinedScale;
  a_sizeOptions.m_maximum.m_height *= m_combinedScale;
}


END_NAMESPACE

