// ---------------------------------------------------------------------------
// 
//   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 "../hacks/winhacks.h"

#include <iostream>
using namespace std;

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

#include "GL/gl.h"

#include "widgetslider.h"
#include "widgetscrollable.h"

WidgetScrollable::WidgetScrollable(void) {
	scrollVertically = true;
	scrollHorizontally = false;
	offsetX = 0;
	offsetY = 0;
	dragScrolling = false;
}

WidgetScrollable::WidgetScrollable(TiXmlElement *xml) : WidgetSingleParent(xml) {
	offsetX = 0;
	offsetY = 0;
	dragScrolling = false;
	
	// Scrolling bools. Getting bools out of TinyXML is weird.
	int scrollVertInt = 1;
	int scrollHorizInt = 0;
	xml->QueryIntAttribute("scrollVertically", &scrollVertInt);
	xml->QueryIntAttribute("scrollHorizontally", &scrollHorizInt);
	scrollVertically = (bool)scrollVertInt;
	scrollHorizontally = (bool)scrollHorizInt;	
	
	// Initial offsets.
	xml->QueryFloatAttribute("offsetX", &offsetX);
	xml->QueryFloatAttribute("offsetY", &offsetY);
	
}

WidgetScrollable::~WidgetScrollable(void) {
	// WidgetSingleParent handles child cleanup.
}


float WidgetScrollable::getMinHeight(void) {
	float realMinHeight = 0;
	float widgetMinHeight = WidgetSingleParent::getMinHeight();
	if(child && !scrollVertically) realMinHeight = child->getMinHeight();
	return realMinHeight > widgetMinHeight ? realMinHeight : widgetMinHeight;
}

float WidgetScrollable::getMinWidth(void) {
	float realMinWidth = 0;
	float widgetMinWidth = WidgetSingleParent::getMinWidth();
	if(child && !scrollHorizontally) realMinWidth = child->getMinWidth();
	return realMinWidth > widgetMinWidth ? realMinWidth : widgetMinWidth;
}

void WidgetScrollable::setWidth(float width) {
	// The child's minWidth will kick in and stop this, or it will
	// stretch the child to fill the display.
	if(child) child->setWidth(width);
	
	WidgetSingleParent::setWidth(width);
	
	updateScrollbars();
}

void WidgetScrollable::setHeight(float height) {
	// The child's minHeight will kick in and stop this, or it will
	// stretch the child to fill the display.
	if(child) child->setHeight(height);
	
	WidgetSingleParent::setHeight(height);
	
	updateScrollbars();
}

bool WidgetScrollable::getScrollVertically(void) {
	return scrollVertically;
}

bool WidgetScrollable::getScrollHorizontally(void) {
	return scrollHorizontally;
}

void WidgetScrollable::setScrollVertically(bool b) {
	scrollVertically = b;
}

void WidgetScrollable::setScrollHorizontally(bool b) {
	scrollHorizontally = b;
}


void WidgetScrollable::draw(void) {
	Widget::draw();
	
	updateScrollbars();
	
	if(child) {
		// Push MY clip area...
		pushClipArea(0, 0, width, height);
		
		glPushMatrix();
		
		// AND the child's.
		pushClipArea(0, 0, child->getWidth(), child->getHeight());
		
		glTranslatef(-offsetX, -offsetY, 0.0);
		
		child->draw();
		
		glPopMatrix();
		
		popClipArea();
		popClipArea();
	}
}

bool WidgetScrollable::handleEvent(const WidgetEvent *event) {
	
	WidgetSingleParent::handleEvent(event);
	
	WidgetEvent shiftedEvent = *event;
	
	if(event->type == WIDGETEVENT_MOUSEMOTION ||
		event->type == WIDGETEVENT_MOUSEDOWN ||
		event->type == WIDGETEVENT_MOUSEUP) {
		
		bool ret = false;
		
		shiftedEvent.mouse.x += offsetX;
		shiftedEvent.mouse.y += offsetY;
	
		debugDrawOffsetX -= offsetX;
		debugDrawOffsetY -= offsetY;
		if(child) {
			// No collision detection to see if we're actually over the
			// child widget. Just the converted coordinates.
			
			// EDIT: ^^^ Why the fuck did I do this? ^^^ 
			//   Need collision check.
			
			if((event->type == WIDGETEVENT_MOUSEDOWN && mouseWasOverMe) || event->type != WIDGETEVENT_MOUSEDOWN) {
				ret = child->handleEvent(&shiftedEvent);
			}
		}
		debugDrawOffsetX += offsetX;
		debugDrawOffsetY += offsetY;
	
		if(!ret) {
			
			// Nothing ate the mouse event. If it's a scroll event,
			// then scroll in our vertical scrollbar.

			WidgetSlider *vbar = dynamic_cast<WidgetSlider*>(widgetGetByUID(verticalScrollbarUID));
			WidgetSlider *hbar = dynamic_cast<WidgetSlider*>(widgetGetByUID(horizontalScrollbarUID));
			
			if(event->type == WIDGETEVENT_MOUSEDOWN && (event->mouse.button == 3 || event->mouse.button == 4)) {

				if(vbar) {
					return vbar->handleEvent(event);
				}
			}

			if(event->type == WIDGETEVENT_MOUSEMOTION && dragScrolling) {
				
				/*if(vbar) {
					vbar->handleEvent(&shiftedEvent);
				}
				
				if(hbar) {
					hbar->handleEvent(&shiftedEvent);
				}*/
				
				Vector2D shiftAmount = Vector2D(event->mouse.x, event->mouse.y) - dragScrollStart;
				
				//cout << shiftAmount << endl;
				
				float vPos = dragScrollStartValues.y - shiftAmount.y;
				
				if(vbar) vbar->setValue(vPos);
				
				float hPos = dragScrollStartValues.x - shiftAmount.x;
				
				if(hbar) hbar->setValue(hPos);
				
				return true;
			}

			if(event->mouse.button == 1 && (event->type == WIDGETEVENT_MOUSEDOWN || event->type == WIDGETEVENT_MOUSEUP)) {
				
				if(vbar || hbar) {
					
					/*if(event->type == WIDGETEVENT_MOUSEDOWN && !dragScrolling) {
						cout << "DRAG START" << endl;
						dragScrolling = true;
						
						if(vbar) vbar->overrideDragStart(event->mouse.x, event->mouse.y);
						if(hbar) vbar->overrideDragStart(event->mouse.x, event->mouse.y);
					}
					
					if(event->type == WIDGETEVENT_MOUSEUP && dragScrolling) {
						cout << "DRAG END" << endl;
						dragScrolling = false;

						if(vbar) vbar->overrideDragEnd();
						if(hbar) vbar->overrideDragEnd();
					}*/
					
					if(event->type == WIDGETEVENT_MOUSEDOWN && !dragScrolling && mouseWasOverMe) {
						dragScrolling = true;
						dragScrollStart = Vector2D(event->mouse.x, event->mouse.y);
						dragScrollStartValues = Vector2D(hbar ? hbar->getValue() : 0, vbar ? vbar->getValue() : 0);
						return true;
					}

					if(event->type == WIDGETEVENT_MOUSEUP && dragScrolling) {
						dragScrolling = false;
						return true;
					}
					
					// If we've made it here, then it's probably a
					// WIDGETEVENT_MOUSEDOWN where mouseWasOverMe is
					// false. We don't eat that event.
				}
			}
			
		}
	
		return false;
	}
	
	if(event->type == WIDGETEVENT_KEYDOWN) {
		
		bool ret = child->handleEvent(event);
		
		// Pageup/pagedown and home/end, if not handled by a child, get
		// passed to the vertical scrollbar.
		if(!ret) {		
			if(	event->keyboard.keyCode == WIDGET_KEY_DOWN ||
				event->keyboard.keyCode == WIDGET_KEY_UP ||
				event->keyboard.keyCode == WIDGET_KEY_PAGEDOWN ||
				event->keyboard.keyCode == WIDGET_KEY_PAGEUP ||
				event->keyboard.keyCode == WIDGET_KEY_HOME ||
				event->keyboard.keyCode == WIDGET_KEY_END) {
					
				WidgetSlider *vbar = dynamic_cast<WidgetSlider*>(widgetGetByUID(verticalScrollbarUID));
				if(vbar) {
					return vbar->handleEvent(event);
				}
			}
		}
		
		return ret;
	}
	
	// I don't recognize this event. Pass it along.
	if(child) return child->handleEvent(&shiftedEvent);
	
	return false;
}

float WidgetScrollable::getMaxOffsetX(void) {
	if(child) {
		return child->getWidth() - width;
	}
	return 0;
}

float WidgetScrollable::getMaxOffsetY(void) {
	if(child) {
		return child->getHeight() - height;
	}
	return 0;
}

void WidgetScrollable::setOffsetX(float offset) {
	if(offset > getMaxOffsetX()) offset = getMaxOffsetX();
	if(offset < 0) offset = 0;

	offsetX = offset;

#ifdef WIDGET_SNAP_TO_PIXELS
    offsetX = int(offset + 0.5);
#endif
}

void WidgetScrollable::setOffsetY(float offset) {
	if(offset > getMaxOffsetY()) offset = getMaxOffsetY();
	if(offset < 0) offset = 0;

	offsetY = offset;

#ifdef WIDGET_SNAP_TO_PIXELS
    offsetY = int(offset + 0.5);
#endif
}

float WidgetScrollable::getOffsetX(void) {
	return offsetX;
}

float WidgetScrollable::getOffsetY(void) {
	return offsetY;
}

void WidgetScrollable::setVerticalScrollBar(Widget *bar) {
	
	if(!dynamic_cast<WidgetSlider*>(bar)) {

		if(bar) {

			// Can't set this.
			// TODO: Use a generic number value type instead of a slider?
			out("error") << "Trying to set something besides a slider as a verticalscrollbar." << endl;

		} else {

			// Oh, we're just clearing it.
			verticalScrollbarUID = "";

		}
		return;
	}
	
	verticalScrollbarUID = bar->getUID();
	
	updateScrollbars();
}

void WidgetScrollable::setHorizontalScrollBar(Widget *bar) {
	
	if(!dynamic_cast<WidgetSlider*>(bar)) {

		if(bar) {

			// Can't set this.
			// TODO: Use a generic number value type instead of a slider?
			out("error") << "Trying to set something besides a slider as a horizontal scrollbar." << endl;

		} else {
			
			// Oh, we're just clearing it.
			horizontalScrollbarUID = "";
		}
		return;
	}
	
	horizontalScrollbarUID = bar->getUID();
	
	updateScrollbars();
}

Widget *WidgetScrollable::getVerticalScrollBar(void) {
	return widgetGetByUID(verticalScrollbarUID);
}

Widget *WidgetScrollable::getHorizontalScrollBar(void) {
	return widgetGetByUID(horizontalScrollbarUID);
}

void WidgetScrollable::updateScrollbars(void) {
	
	// Get some sane values for child sizes.
	float childHeight = 0;
	float childWidth  = 0;
	
	if(child) {
		childHeight = child->getHeight();
		childWidth  = child->getWidth();
	}
	
	if(childHeight < height) childHeight = height;
	if(childWidth < width) childWidth = width;
	
	// Vertical.
	if(scrollVertically) {
		WidgetSlider *vbar = dynamic_cast<WidgetSlider*>(widgetGetByUID(verticalScrollbarUID));
		if(vbar) {
			vbar->setMaxValue(childHeight - height);
			vbar->setBigStep(height);
			offsetY = vbar->getValue();

#ifdef WIDGET_SNAP_TO_PIXELS
            offsetY = int(offsetY + 0.5);
#endif
		}
	}
	
	// Horizontal.
	if(scrollHorizontally) {
		WidgetSlider *hbar = dynamic_cast<WidgetSlider*>(widgetGetByUID(horizontalScrollbarUID));
		if(hbar) {
			hbar->setMaxValue(childWidth - width);
			hbar->setBigStep(width);
			offsetX = hbar->getValue();

#ifdef WIDGET_SNAP_TO_PIXELS
            offsetX = int(offsetX + 0.5);
#endif
		}
	}
	
}

// TODO: All Lua callbacks.

