/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: sgx/src/sgx/gui/sgxwidget.cpp

*/

#include "sgx/core/core.hpp"
#include "sgx/graphics/graphics.hpp"
#include "sgx/gui/gui.hpp"

sgxWidget::sgxWidget(const tINT32 x, const tINT32 y, const CGUIDesignScreen::tWidgetType type):
	m_Type(type),

	m_pHandler(NULL),
	m_pParent(NULL),
	m_bEnabled(TRUE),
	m_bVisible(TRUE),
	m_isOver(FALSE),
	m_pSelectCallback(NULL),
	m_State(FALSE),
	m_DirectedState(FALSE),
	m_pGroup(NULL),
	m_X(x), m_Y(y),
	m_iFlags(0),
	m_KeyboardShortcut(0),
	m_KeyboardShortcutExt(0)
{
}

CGUIDesignScreen::tWidgetType	
sgxWidget::getType() {
	return m_Type;
}

void sgxWidget::update(const tREAL32 telaps) {
	sgxVector<sgxWidget *>::iterator it = m_Children.begin();
	for(;it!=m_Children.end();++it) {
		(*it)->update(telaps);
	}
}

void	
sgxWidget::draw() const {
	draw(CSGXDrawSurfaceManager::get()->getDisplaySurface(), 0, 0);
}

tBOOL sgxWidget::draw(CSGXDrawSurface *pSurface, const tINT32 offsetX, const tINT32 offsetY) const {
	tBOOL rt = TRUE;

	if (!m_bVisible) {
		return FALSE;
	}

	sgxVector<sgxWidget *>::const_iterator cit = m_Children.begin();
	for(;cit!=m_Children.end();++cit) {
		if ((*cit)->m_bVisible) {
			rt &= (*cit)->draw(pSurface, offsetX, offsetY);
		}
	}

	return rt;
}


tBOOL	sgxWidget::enable(const tBOOL enabled) {
	tBOOL prev = enabled;
	m_bEnabled = enabled;
	return prev;
}

tBOOL	sgxWidget::disable() {
	tBOOL prev = m_bEnabled;
	m_bEnabled = FALSE;
	return prev;
}

tBOOL	sgxWidget::show(const tBOOL show) {
	tBOOL prev = m_bVisible;
	m_bVisible = show;
	return prev;
}

tBOOL	sgxWidget::hide() {
	tBOOL prev = m_bVisible;
	m_bVisible = FALSE;
	return prev;
}

tBOOL sgxWidget::isVisible() const {
	return m_bVisible;
}

void	sgxWidget::setCallback(CSGXCallback *pCallback, const tBOOL recursively) {
	m_pSelectCallback = pCallback;
	if (recursively) {
		sgxVector<sgxWidget *>::iterator it = m_Children.begin();
		for(;it!=m_Children.end();++it) {
			(*it)->setCallback(pCallback, recursively);
		}
	}
}


CSGXCallback *sgxWidget::getCallback() const {
	return m_pSelectCallback;
}

void 
sgxWidget::setText(const sgxString &s) {
	SGX_UNUSED(s);
}

void 
sgxWidget::getPosition(sgxPoint2i &pt) {
	pt.x = m_X;
	pt.y = m_Y;
}

void 
sgxWidget::setPosition(const sgxPoint2i &pt) {
	m_X = pt.x;
	m_Y = pt.y;
}

void 
sgxWidget::setPosition(const tINT32 x, const tINT32 y) {
	m_X = x;
	m_Y = y;
}

void 			
sgxWidget::setSize(const tINT32 w, const tINT32 h) {
SGX_UNUSED(w);
SGX_UNUSED(h);
}

tBOOL	sgxWidget::onPressed(const sgxPoint2i &position) {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetPressed(this, position)) {
			return FALSE;
		}
	}

	m_DirectedState = TRUE;
	if (m_iFlags & eTriggerOnDown) {
		onSelect(position);
	}
	return TRUE;
}


tBOOL	sgxWidget::onRelease(const sgxPoint2i &position) {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetRelease(this, position)) {
			return FALSE;
		}
	}

	if (m_DirectedState && !(m_iFlags & eTriggerOnDown)) {
		onSelect(position);
	}
	m_DirectedState = FALSE;
	return TRUE;
}


tBOOL	sgxWidget::onLeaveArea(const sgxPoint2i &position) {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetLeaveArea(this, position)) {
			return FALSE;
		}
	}

	m_DirectedState = FALSE;
	return TRUE;
}


tBOOL	sgxWidget::onReenterArea(const sgxPoint2i &position) {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetReenterArea(this, position)) {
			return FALSE;
		}
	}

	m_DirectedState = TRUE;
	return TRUE;
}


tBOOL	sgxWidget::onKeyDown(const tUINT32 key) {
	SGX_UNUSED(key);
	return TRUE;
}


tBOOL	sgxWidget::onKeyUp(const tUINT32 key) {
	SGX_UNUSED(key);
	return TRUE;
}


tBOOL	sgxWidget::onHoverEnter(const sgxPoint2i &position) {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetHoverEnter(this, position)) {
			return FALSE;
		}
	}

	m_isOver = TRUE;
	return TRUE;
}


tBOOL	sgxWidget::onHoverLeave(const sgxPoint2i &position) {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetHoverLeave(this, position)) {
			return FALSE;
		}
	}
	//
	if (isFocused()) {
		CSGXGUI::get()->clearFocus();
	}

	m_isOver = FALSE;
	return TRUE;
}


tBOOL	sgxWidget::onFocusGain() {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetFocusGain(this)) {
			return FALSE;
		}
	}

	return TRUE;
}


tBOOL	sgxWidget::onFocusLose() {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetFocusLose(this)) {
			return FALSE;
		}
	}

	m_isOver = FALSE;
	return TRUE;
}


tBOOL	sgxWidget::onActiveGain() {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetActiveGain(this)) {
			return FALSE;
		}
	}

	return TRUE;
}


tBOOL	sgxWidget::onActiveLose() {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetActiveLose(this)) {
			return FALSE;
		}
	}

	m_isOver = FALSE;
	return TRUE;
}



tBOOL	sgxWidget::onSelect(const sgxPoint2i &position) {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetSelect(this, position)) {
			return FALSE;
		}
	}

	if (m_pSelectCallback) {
		m_pSelectCallback->call((tMEM_SIZE)this);
	}
	CSGXGUI::get()->clearFocus();
	CSGXGUI::get()->clearActive();
	return TRUE;
}


tBOOL	sgxWidget::onActiveSelect() {
	if (m_pHandler) {
		if (!m_pHandler->onGUIWidgetSelectActive(this)) {
			return FALSE;
		}
	}

	if (m_pSelectCallback) {
		m_pSelectCallback->call((tMEM_SIZE)this);
	}
	CSGXGUI::get()->clearFocus();
	CSGXGUI::get()->clearActive();
	return TRUE;
}



tBOOL	sgxWidget::isFocused() const {
	return CSGXGUI::get()->getFocusWidget() == this;
}

tBOOL	sgxWidget::isActive() const {
	return CSGXGUI::get()->getActiveWidget() == this;
}

tBOOL	sgxWidget::isEnabled() const {
	return m_bEnabled;
}


tBOOL	sgxWidget::isPointInside(const sgxPoint2i &position) const {
	sgxRect2i rc;
	getArea(rc);
	return rc.isInside(position);
}

void	sgxWidget::getArea(sgxRect2i &rc) const {
	rc.left = m_X;
	rc.top = m_Y;
}

tUINT32	sgxWidget::getWidth() const {
	sgxRect2i rc;
	getArea(rc);
	return rc.right - rc.left;
}

tUINT32	sgxWidget::getHeight() const {
	sgxRect2i rc;
	getArea(rc);
	return rc.bottom - rc.top;
}


const sgxWidget *sgxWidget::getWidgetAt(const sgxPoint2i &position) const {
	return getWidgetAt(position);
}

sgxWidget *sgxWidget::getWidgetAt(const sgxPoint2i &position, const tSelectionSet fromSet) {
	if (!m_bEnabled) {
		return NULL;
	}
	//
	switch(fromSet) {
		case eFunctioningWidgets:
			switch(m_Type) {
				case CGUIDesignScreen::widgetImage:
				case CGUIDesignScreen::widgetText:
				case CGUIDesignScreen::widgetAnimatedImage:
					return NULL;
			}
			break;
	}
	//
	if (isPointInside(position)) {
		return this;
	}
	
	sgxPoint2i pos(position.x, position.y);
	pos.x -= m_X;
	pos.y -= m_Y;

	if (fromSet == sgxWidget::eAllFromTop) {
		sgxVector<sgxWidget *>::reverse_iterator it = m_Children.rbegin();
		for(;it!=m_Children.rend();++it) {
			if ((*it)->getWidgetAt(pos, fromSet)) {
				return *it;
			}
		}
	} else {
		sgxVector<sgxWidget *>::iterator it = m_Children.begin();
		for(;it!=m_Children.end();++it) {
			if ((*it)->getWidgetAt(pos, fromSet)) {
				return *it;
			}
		}
	}
	
	return NULL;
}

tUINT32			
sgxWidget::getWidgetListWithin(const sgxRect2i &rc, sgxVector<sgxWidget *> &widgetList) {
	tUINT32 count = widgetList.size();

	if (m_bEnabled) {
		sgxRect2i checkArea;
		sgxRect2i targetArea(rc);

		getArea(checkArea);
		if (targetArea.isInside(checkArea)) {
			widgetList.push_back(this);
		}
		//
		targetArea.left -= m_X;
		targetArea.top -= m_Y;
		targetArea.right -= m_X;
		targetArea.bottom -= m_Y;
		sgxVector<sgxWidget *>::iterator it = m_Children.begin();
		for(;it!=m_Children.end();++it) {
			(*it)->getWidgetListWithin(targetArea, widgetList);
		}
	}

	return widgetList.size() - count;
}

sgxWidget *sgxWidget::getWidgetOfUserData(const sgxString &str) {
	tUINT32 userdata = sgxGetCRC32(str);
	return getWidgetOfUserData(userdata);
}

sgxWidget *sgxWidget::getWidgetOfUserData(const tUINT32 userdata) {
	if (m_UserData == userdata) {
		return this;
	}

	sgxVector<sgxWidget *>::iterator it = m_Children.begin();
	for(;it!=m_Children.end();++it) {
		sgxWidget *p = (*it)->getWidgetOfUserData(userdata);
		if (p) {
			return p;
		}
	}

	return NULL;
}

sgxWidget *sgxWidget::getWidgetWithKeyShortcut(const tUINT32 key) {
	if (m_KeyboardShortcut == key) {
		return this;
	}

	sgxVector<sgxWidget *>::iterator it = m_Children.begin();
	for(;it!=m_Children.end();++it) {
		sgxWidget *p = (*it)->getWidgetWithKeyShortcut(key);
		if (p) {
			return p;
		}
	}

	return NULL;
}


sgxWidget *sgxWidget::addChildWidget(sgxWidget *pWidget) {
	m_Children.push_back(pWidget);
	pWidget->m_pParent = this;
	return pWidget;
}
			
tUINT32
sgxWidget::getChildWidgetCount() {
	return m_Children.size();
}

sgxWidget *		
sgxWidget::getChildWidget(const tUINT32 idx) {
	if (idx < m_Children.size()) {
		return m_Children[idx];
	}
	return NULL;
}

/**
Returns the idx'th child widget of the given type. 
*/
sgxWidget *		
sgxWidget::getChildWidget(const tUINT32 idx, CGUIDesignScreen::tWidgetType type) {
	tUINT32 mx = m_Children.size();
	tUINT32 count = idx;
	for(tUINT32 i=0;i<mx;++i) {
		if (m_Children[i]->m_Type == type) {
			if (count == 0) {
				return m_Children[i];
			}
			--count;
		}
	}
	return NULL;
}


tBOOL 			
sgxWidget::reorderWidget(sgxWidget *pTargetWidget, const tDepthPosition pos, sgxWidget *pMarkerWidget) {
	sgxWidget *pParent = pTargetWidget->m_pParent;
	sgxVector<sgxWidget *>::iterator it = find(pParent->m_Children.begin(), pParent->m_Children.end(), pMarkerWidget);

	if (it != pParent->m_Children.end()) {
		if (pos == eInFrontOf) {
			if (++it == pParent->m_Children.end()) {
				pParent->m_Children.push_back(pTargetWidget);
			} else {
				pParent->m_Children.insert(it, pTargetWidget);
			}
		} else {
			pParent->m_Children.insert(it, pTargetWidget);
		}

		it = find(pParent->m_Children.begin(), pParent->m_Children.end(), pTargetWidget);
		pParent->m_Children.erase(it);
		//
		return TRUE;
	}

	return FALSE;
}

void sgxWidget::clearChildWidgets() {
	m_Children.clear();
}

void sgxWidget::releaseChildWidgets() {
	sgxVector<sgxWidget *>::iterator it = m_Children.begin();
	for(;it!=m_Children.end();++it) {
		delete (*it);
	}
	
	clearChildWidgets();
}

sgxGUIWidgetCallbackHandler *sgxWidget::getHandler() {
	return m_pHandler;
}

void
sgxWidget::setHandler(sgxGUIWidgetCallbackHandler *pNewHandler, const tBOOL recursively) {
	m_pHandler = pNewHandler;

	if (recursively) {
		sgxVector<sgxWidget *>::iterator it = m_Children.begin();
		for(;it!=m_Children.end();++it) {
			(*it)->setHandler(pNewHandler, recursively);
		}
	}
}

sgxWidget *		
sgxWidget::getNextSibling(const tBOOL bWrap) {
	tUINT32 idx = 0;
	tBOOL bGrabNext = FALSE;

	do {
		sgxWidget *pW = m_pParent->getChildWidget(idx);
		if (pW == NULL && bWrap) {
			idx = (tUINT32)-1;	// so the +1 makes it 0
		} else if (pW == NULL) {
			return NULL;
		} else if (pW == this) {
			bGrabNext = TRUE;
		} else if (!pW->isEnabled() || !pW->isVisible()) {
			// NULL - skip it
		} else if (pW->getType() != CGUIDesignScreen::widgetButton && pW->getType() != CGUIDesignScreen::widgetImageButton) {
			// NULL - skip it
		} else if (bGrabNext) {
			return pW;
		}
		++idx;
	} while(!(idx == 0 && !bGrabNext));

	return NULL;
}

sgxWidget *		
sgxWidget::getPreviousSibling(const tBOOL bWrap) {
	tUINT32 size = m_pParent->getChildWidgetCount();
	tUINT32 idx = 0;

	// Check for empties! (and fix the idx in the process)
	if (size == 0) {
		return NULL;
	}

	sgxWidget *pLast = m_pParent->getChildWidget(size-1);
	do {
		sgxWidget *pW = m_pParent->getChildWidget(idx);
		if (pW == this) {
			return pLast;
		} else if (!pW->isEnabled() || !pW->isVisible()) {
			// NULL - skip it
		} else if (pW->getType() != CGUIDesignScreen::widgetButton && pW->getType() != CGUIDesignScreen::widgetImageButton) {
			// NULL - skip it
		}
		pLast = pW;
	} while(++idx != size);

	return NULL;
}

