// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program is distributed in the hope that it will be useful, but
//   WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#include <iostream>
using namespace std;

#include "../console/console.h"
using namespace Console;

#include "widgetmultiparent.h"

WidgetMultiParent::WidgetMultiParent(void) {
	focusedChild = 0;
}

WidgetMultiParent::WidgetMultiParent(TiXmlElement *xml) : Widget(xml) {
	focusedChild = 0;

	// Load all the children.
	TiXmlElement *child = xml->FirstChildElement();
	while(child) {
		
		Widget *childWidget = widgetCreateFromXMLElement(child);
		addChild(childWidget);
		
		child = child->NextSiblingElement();
	}

}

WidgetMultiParent::~WidgetMultiParent(void) {
	
	clearChildren();
}

void WidgetMultiParent::addChild(Widget *child) {

	// Search through and make sure it's not already in there.
	//for(unsigned int i = 0; i < children.size(); i++) {
	//}

    if(widgetIsAlreadySafeDeleted(child)) {
        out("error") << "Cannot add a child that is already getting deleted!" << endl;
        return;
	}
	
	if(widgetIsEventuallyMyParent(child)) {
		out("error") <<
			"Tried to add a child (" <<
			child->getName() <<
			") which is an eventual parent to this WidgetMultiParent (" <<
			getName() <<
			")." << endl;
			
		return;
	}
	
	if(child) {
		
		if(child->parent || child == widgetRoot) {
			out("error") <<
				"Tried to add a child (" <<
				child->getName() <<
				") to a WidgetMultiParent ("
				<< getName() <<
				") when the child already has a parent." << endl;
			
			return;
		}
		
		child->parent = this;
	}
	
	// Add it.
	children.push_back(child);
	
	markSizeDirty();

}

void WidgetMultiParent::removeChild(Widget *child) {
	for(unsigned int i = 0; i < children.size(); i++) {
		if(children[i] == child) {
			
			// Use disconnectSpecificChild() if you don't want to
			// delete the child.
			children[i]->parent = NULL;
			autoGenLuaWrapper_Widget_safeDelete(children[i]);
			
			// Remove it from the vector.
			
			// FIXME: Decide if we should leave the gap there or not.
			//children.erase(children.begin() + i);
			children[i] = NULL;
		}
	}
}

void WidgetMultiParent::disconnectSpecificChild(Widget *child) {
	for(unsigned int i = 0; i < children.size(); i++) {
		if(children[i] == child) {
			
			// Remove it from the vector.
			
			// FIXME: Decide if we should leave the gap there or not.
			//children.erase(children.begin() + i);
			children[i] = NULL;
			
			child->parent = NULL;
		}
	}
}

bool WidgetMultiParent::handleEvent(const WidgetEvent *event) {
	
	bool ret = Widget::handleEvent(event);
	
	if(event->type == WIDGETEVENT_FOCUSACCEPT || event->type == WIDGETEVENT_FOCUSACCEPT_BACK) {
		
		// Focus is coming into this widget from one of these directions.
		// Find any children interested in it.
		int dir   = event->type == WIDGETEVENT_FOCUSACCEPT ? 1 : -1;
		int start = event->type == WIDGETEVENT_FOCUSACCEPT ? 0 : children.size() - 1;
		
		for(int i = start; i < (int)children.size() && i >= 0; i += dir) {
			
			if(children[i]) {
				
				if(children[i]->handleEvent(event)) {
					
					// Found one. Set it as focused and return true.
					switchFocus(i);
					return true;
					
				} 
			}
		}
		
		return false;
	}
	
	if(event->type == WIDGETEVENT_FOCUSNEXT || event->type == WIDGETEVENT_FOCUSPREV) {
		
		// First, see if the currently focused child wants it.
		if(children.size() && children[focusedChild]) {
			if(children[focusedChild]->handleEvent(event)) {
				// Okay, something down the line used the event.
				return true;
			}
		}
		
		// If not, try children with a WIDGETEVENT_FOCUSACCEPT. (This
		// event was meant to tell this widget to go to the next child.
		int dir   = (event->type == WIDGETEVENT_FOCUSNEXT ? 1 : -1);
		int start = focusedChild + dir;
		WidgetEvent acceptEvent;
		memset(&acceptEvent, 0, sizeof(acceptEvent));
		acceptEvent.type = (event->type == WIDGETEVENT_FOCUSNEXT ? WIDGETEVENT_FOCUSACCEPT : WIDGETEVENT_FOCUSACCEPT_BACK);
		
		for(int i = start; i < (int)children.size() && i >= 0; i += dir) {
			if(children[i]) {
				if(children[i]->handleEvent(&acceptEvent)) {
					
					// One of the children accepted focus. Switch focus
					// to that one and return success.
					switchFocus(i);
					return true;
				} 
			}
		}		
		
		// If that doesn't work, return false. Let a parent deal with
		// it. (Or everyone just loses focus, or something?)
		return false;
	}
	
	if(event->type == WIDGETEVENT_FOCUSLEAVE || event->type == WIDGETEVENT_FOCUSENTER) {
		Widget *w = getFocusedChild();
		if(w) {
			w->handleEvent(event);
		}
	}
	
	return ret;
}

bool WidgetMultiParent::childNeedsMouseEvents(void) {
	
	// See if any child needs mouse events.
	for(unsigned int i = 0; i < children.size(); i++) {
		if(children[i] && children[i]->needsMouseEvents()) return true;
	}
	
	return false;
}

Widget *WidgetMultiParent::findChildByName(const std::string &name) {
	Widget *w = Widget::findChildByName(name);
	if(w)
		return w;
	
	// Go through all the children until something matches.
	for(unsigned int i = 0; i < children.size(); i++) {
		if(children[i]) {
			w = children[i]->findChildByName(name);
			if(w)
				return w;
		}
	}
	
	return NULL;
}

bool WidgetMultiParent::needsMouseEvents(void) {
	
	if(Widget::needsMouseEvents()) return true;
	
	return childNeedsMouseEvents();
}


void WidgetMultiParent::switchFocus(int newFocus) {
	
	WidgetEvent focusEvent;
	memset(&focusEvent, 0, sizeof(focusEvent));
	
	// Send message to the current focused Widget telling it that it's
	// losing focus.
	Widget *focusedChildWidget = getFocusedChild();
	if(focusedChildWidget) {
		focusEvent.type = WIDGETEVENT_FOCUSLEAVE;
		if(focusedChildWidget) focusedChildWidget->handleEvent(&focusEvent);
	}
	
	// Set new focus.
	focusedChild = newFocus;
	
	// Send message to the new focus telling it that it's gaining
	// focus.
	focusedChildWidget = getFocusedChild();
	if(focusedChildWidget) {
		focusEvent.type = WIDGETEVENT_FOCUSENTER;
		focusedChildWidget->handleEvent(&focusEvent);
	}
}

Widget *WidgetMultiParent::getFocusedChild(void) {
	
	// Quick sanity check on focused index.
	if(focusedChild < (int)children.size() && focusedChild >= 0) {
		return children[focusedChild];
	}
	return NULL;
}

void WidgetMultiParent::clearChildren() {
	
	for(unsigned int i = 0; i < children.size(); i++) {
		if(children[i]) {
			autoGenLuaWrapper_Widget_safeDelete(children[i]);
		}
	}
	children.clear();
}

int WidgetMultiParent::getNumChildren(void) {
	return (int)children.size();
}

Widget *WidgetMultiParent::getChildInSlot(int slot) {
	if(slot < 0 || slot >= (int)children.size()) {
		return NULL;
	}
	
	return children[slot];
}

