/*
 *  ppui/Slider.cpp
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Milkytracker 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 Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "Slider.h"
#include "GraphicsAbstract.h"
#include "Button.h"
#include "Event.h"
#include "Screen.h"

void PPSlider::initButtons()
{
	PPPoint buttonDownPos, buttonBarPos;
	PPSize buttonBarSize;

	if (horizontal)
	{
		this->size.height = SLIDERBUTTONHEIGHT;
		this->size.width = oneDimSize;
	
		buttonDownPos = PPPoint(location.x + oneDimSize - SLIDERBUTTONWIDTH, location.y);
	
		buttonBarPos = PPPoint(location.x + SLIDERBUTTONWIDTH, location.y);
	
		buttonBarSize = PPSize((oneDimSize - SLIDERBUTTONWIDTH*2), SLIDERBUTTONHEIGHT);  
	}
	else
	{
		this->size.width = SLIDERBUTTONWIDTH;
		this->size.height = oneDimSize;
		
		buttonDownPos = PPPoint(location.x, location.y + oneDimSize - SLIDERBUTTONHEIGHT);
		
		buttonBarPos = PPPoint(location.x, location.y + SLIDERBUTTONHEIGHT);
		
		buttonBarSize = PPSize(SLIDERBUTTONWIDTH, (oneDimSize - SLIDERBUTTONHEIGHT*2));  
	}

	backgroundButton = new PPButton(0, parentScreen, NULL, /*_Point(location.x + (horizontal?0:1), location.y + (horizontal?1:0))*/location, this->size, false);
	backgroundButton->setColor(backgroundColor);
	backgroundButton->setInvertShading(true);

	buttonUp = new PPButton(0, parentScreen, this, location, PPSize(SLIDERBUTTONWIDTH,SLIDERBUTTONHEIGHT), false);
	buttonUp->setText(!buttonSwap ? "-" : "+");

	buttonDown = new PPButton(0, parentScreen, this, buttonDownPos , PPSize(SLIDERBUTTONWIDTH,SLIDERBUTTONHEIGHT), false);
	buttonDown->setText(!buttonSwap ? "+" : "-");

	buttonBar = new PPButton(0, parentScreen, this, buttonBarPos, buttonBarSize, false, false);

	setBarSize(currentBarSize, false);
	setBarPosition(currentBarPosition, false);
}

PPSlider::PPSlider(pp_int32 id, PPScreen* parentScreen, EventListenerInterface* eventListener, 
				   const PPPoint& location, pp_int32 size, 
				   bool horizontal, bool buttonSwap) :
	PPControl(id, parentScreen, eventListener, location, PPSize(0,0)),
	oneDimSize(size),
	horizontal(horizontal),
	buttonSwap(buttonSwap),
	controlCaughtByLMouseButton(false), controlCaughtByRMouseButton(false)
{
	// default color
	backgroundColor.r = 64;
	backgroundColor.g = 64;
	backgroundColor.b = 64;

	currentBarSize = 65536;
	currentBarPosition = 0;

	initButtons();
	
	caughtControl = NULL;

	minValue = 0;
	maxValue = 255;
	currentValue = 0;
}

PPSlider::~PPSlider()
{
	delete backgroundButton;	
	delete buttonUp;
	delete buttonDown;
	delete buttonBar;
}

void PPSlider::paint(PPGraphicsAbstract* g)
{
	if (!isVisible())
		return;

	g->setRect(PPRect(location.x, location.y, location.x + size.width, location.y + size.height));

	/*g->setColor(backgroundColor);

	g->fill();*/

	backgroundButton->paint(g);

	buttonDown->paint(g);

	buttonUp->paint(g);

	buttonBar->paint(g);

}

void PPSlider::setBarSize(pp_int32 size, bool repaint /* = true */)
{
	if (size < 0) size = 0;
	if (size > 65536) size = 65536;

	currentBarSize = size;
	
	pp_int32 newSize = (((horizontal?(this->size.width-SLIDERBUTTONWIDTH*2):(this->size.height-SLIDERBUTTONHEIGHT*2))) * size)>>16;

	if (horizontal)
	{
		buttonBar->setSize(PPSize(newSize, this->size.height));		
	}
	else
	{
		buttonBar->setSize(PPSize(this->size.width, newSize));
	}

	if (repaint)
		parentScreen->paintControl(this);

}

void PPSlider::setLocation(const PPPoint& location)
{
	PPControl::setLocation(location);
	
	delete backgroundButton;	
	delete buttonUp;
	delete buttonDown;
	delete buttonBar;

	initButtons();
}

void PPSlider::setBarPosition(pp_int32 pos, bool repaint /* = false */)
{
	if (pos < 0) pos = 0;
	if (pos > 65536) pos = 65536;

	currentBarPosition = pos;

	pp_int32 size = (horizontal?buttonBar->getSize().width:buttonBar->getSize().height);

	pp_int32 entireSize = (horizontal?(this->size.width-SLIDERBUTTONWIDTH*2):(this->size.height-SLIDERBUTTONHEIGHT*2));

	pp_int32 maxPos = entireSize-size;

	pp_int32 newPos = (maxPos*pos)>>16;	

	PPPoint p = location;

	if (horizontal)
		p.x+=newPos + SLIDERBUTTONWIDTH;
	else
		p.y+=newPos + SLIDERBUTTONHEIGHT;

	buttonBar->setLocation(p);

	if (repaint)
		parentScreen->paintControl(this);
}

pp_int32 PPSlider::dispatchEvent(PPEvent* event)
{
	//if (!visible)
	//	return 0;

	switch (event->getID())
	{
		/*case eMouseWheelMoved:
		{

			TMouseWheelEventParams* params = (TMouseWheelEventParams*)event->getDataPtr();
			
			if (params->delta > 0)
			{
				PPEvent e(eLMouseUp);
				handleEvent(reinterpret_cast<PPObject*>(buttonUp), &e);
			}
			else if (params->delta < 0)
			{
				PPEvent e(eLMouseUp);
				handleEvent(reinterpret_cast<PPObject*>(buttonDown), &e);
			}
			
			event->cancel();
			
			break;
		}*/

		case eLMouseDown:
		{
			PPPoint* p = (PPPoint*)event->getDataPtr();

			if (buttonUp->hit(*p))
			{
				caughtControl = buttonUp;
				caughtControl->dispatchEvent(event);
				controlCaughtByLMouseButton = true;
			}
			else if (buttonDown->hit(*p))
			{
				caughtControl = buttonDown;
				caughtControl->dispatchEvent(event);				
				controlCaughtByLMouseButton = true;
			}
			else if (buttonBar->hit(*p))
			{
				caughtControl = buttonBar;
				// -------------------------
				buttonBar->setPressed(true);
				parentScreen->paintControl(buttonBar);
				// -------------------------
				caughtMouseLocation = *p;
				caughtControlLocation = buttonBar->getLocation();
				caughtControl->dispatchEvent(event);				
				controlCaughtByLMouseButton = true;
			}
			else if (backgroundButton->hit(*p))
			{
				handleEvent(reinterpret_cast<PPObject*>(backgroundButton), event);				
			}

			break;
		}

		case eLMouseUp:
			controlCaughtByLMouseButton = false;

			if (caughtControl == NULL)
				break;

			if (controlCaughtByRMouseButton)
			{
				caughtControl->dispatchEvent(event);
				break;
			}

			// -------------------------
			if (reinterpret_cast<PPButton*>(caughtControl) == buttonBar)
			{
				buttonBar->setPressed(false);
				parentScreen->paintControl(buttonBar);
			}
			// -------------------------

			caughtControl->dispatchEvent(event);
			caughtControl = NULL;
			break;

		case eLMouseRepeat:
		{
			if (caughtControl)
			{
				caughtControl->dispatchEvent(event);
				break;
			}
			else
			{
				PPPoint* p = (PPPoint*)event->getDataPtr();
				/*if (backgroundButton->hit(*p))
				{
					handleEvent(reinterpret_cast<PPObject*>(backgroundButton), event);				
				}
				PPPoint* p = (PPPoint*)event->getDataPtr();*/
				if (backgroundButton->hit(*p) && !buttonBar->hit(*p))
				{
					handleEvent(reinterpret_cast<PPObject*>(backgroundButton), event);				
				}
				else if (buttonBar->hit(*p))
				{
					// -------------------------
					buttonBar->setPressed(true);
					parentScreen->paintControl(buttonBar);
					// -------------------------

					caughtControl = buttonBar;
					caughtMouseLocation = *p;
					caughtControlLocation = buttonBar->getLocation();
					caughtControl->dispatchEvent(event);				
				}

			}
			break;
		}

		case eRMouseDown:
		{
			PPPoint* p = (PPPoint*)event->getDataPtr();

			if (buttonUp->hit(*p))
			{
				caughtControl = buttonUp;
				caughtControl->dispatchEvent(event);
				controlCaughtByRMouseButton = true;
			}
			else if (buttonDown->hit(*p))
			{
				caughtControl = buttonDown;
				caughtControl->dispatchEvent(event);				
				controlCaughtByRMouseButton = true;
			}
			else if (buttonBar->hit(*p))
			{
				caughtControl = buttonBar;
				// -------------------------
				buttonBar->setPressed(true);
				parentScreen->paintControl(buttonBar);
				// -------------------------
				caughtMouseLocation = *p;
				caughtControlLocation = buttonBar->getLocation();
				caughtControl->dispatchEvent(event);				
				controlCaughtByRMouseButton = true;
			}
			else if (backgroundButton->hit(*p))
			{
				handleEvent(reinterpret_cast<PPObject*>(backgroundButton), event);				
			}

			break;
		}

		case eRMouseUp:
			controlCaughtByRMouseButton = false;

			if (caughtControl == NULL)
				break;

			if (controlCaughtByLMouseButton)
			{
				caughtControl->dispatchEvent(event);
				break;
			}

			// -------------------------
			if (reinterpret_cast<PPButton*>(caughtControl) == buttonBar)
			{
				buttonBar->setPressed(false);
				parentScreen->paintControl(buttonBar);
			}
			// -------------------------

			caughtControl->dispatchEvent(event);
			caughtControl = NULL;
			break;

		case eRMouseRepeat:
		{
			if (caughtControl)
			{
				caughtControl->dispatchEvent(event);
				break;
			}
			else
			{
				/*PPPoint* p = (PPPoint*)event->getDataPtr();
				if (backgroundButton->hit(*p))
				{
					handleEvent(reinterpret_cast<PPObject*>(backgroundButton), event);				
				}*/
				PPPoint* p = (PPPoint*)event->getDataPtr();
				if (backgroundButton->hit(*p) && !buttonBar->hit(*p))
				{
					handleEvent(reinterpret_cast<PPObject*>(backgroundButton), event);				
				}
				else if (buttonBar->hit(*p))
				{
					// -------------------------
					buttonBar->setPressed(true);
					parentScreen->paintControl(buttonBar);
					// -------------------------

					caughtControl = buttonBar;
					caughtMouseLocation = *p;
					caughtControlLocation = buttonBar->getLocation();
					caughtControl->dispatchEvent(event);				
				}

			}
			break;
		}

		//case eLMouseDrag:
		default:
			if (caughtControl == NULL)
				break;

			caughtControl->dispatchEvent(event);
			break;

	}

	return 0;
}

pp_int32 PPSlider::handleEvent(PPObject* sender, PPEvent* event)
{

	if (
		(event->getID() == eLMouseUp &&
		sender == reinterpret_cast<PPObject*>(buttonUp)) ||
		(event->getID() == eLMouseRepeat &&
		sender == reinterpret_cast<PPObject*>(buttonUp)) ||
		(event->getID() == eRMouseUp &&
		sender == reinterpret_cast<PPObject*>(buttonUp)) ||
		(event->getID() == eRMouseRepeat &&
		sender == reinterpret_cast<PPObject*>(buttonUp))
		)
	{
		// Call parent event listener
		//PPEvent e(eBarScrollUp);
		//return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
		
		if (currentValue > minValue)
			currentValue--;
	
		float f = (float)(currentValue - minValue)/(maxValue - minValue);

		setBarPosition((pp_int32)(f*65536.0f));
		
		parentScreen->paintControl(this);

		PPEvent e(eValueChanged);

		return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
	}
	if (
		(event->getID() == eLMouseUp &&
		sender == reinterpret_cast<PPObject*>(buttonDown)) ||
		(event->getID() == eLMouseRepeat &&
		sender == reinterpret_cast<PPObject*>(buttonDown)) ||
		(event->getID() == eRMouseUp &&
		sender == reinterpret_cast<PPObject*>(buttonDown)) ||
		(event->getID() == eRMouseRepeat &&
		sender == reinterpret_cast<PPObject*>(buttonDown))
		)
	{
		// Call parent event listener
		//PPEvent e(eBarScrollDown);
		//return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);

		if (currentValue < maxValue)
			currentValue++;

		float f = (float)(currentValue - minValue)/(maxValue - minValue);

		setBarPosition((pp_int32)(f*65536.0f));

		parentScreen->paintControl(this);
		
		PPEvent e(eValueChanged);

		return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
	}
	else if (/*event->getID() == eLMouseDown &&*/
			 sender == reinterpret_cast<PPObject*>(backgroundButton))
	{
		
		if (horizontal)
		{
			PPPoint* p = (PPPoint*)event->getDataPtr();
			
			if (p->x < buttonBar->getLocation().x)
			{
				pp_int32 bsize = buttonBar->getSize().width;
				pp_int32 entireSize = size.width - SLIDERBUTTONWIDTH*2;

				float f = (float)entireSize/(float)(entireSize - bsize);

				currentBarPosition-=(pp_int32)(currentBarSize*f);
				if (currentBarPosition < 0) currentBarPosition = 0;
				setBarPosition(currentBarPosition);
				
				// Call parent event listener
				//PPEvent e(eBarPosChanged);
				//return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
			}
			else if (p->x > buttonBar->getLocation().x + buttonBar->getSize().width)
			{
				pp_int32 bsize = buttonBar->getSize().width;
				pp_int32 entireSize = size.width - SLIDERBUTTONWIDTH*2;

				float f = (float)entireSize/(float)(entireSize - bsize);

				currentBarPosition+=(pp_int32)(currentBarSize*f);
				if (currentBarPosition > 65536) currentBarPosition = 65536;
				setBarPosition(currentBarPosition);
				
				// Call parent event listener
				//PPEvent e(eBarPosChanged);
				//return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
			}
		}
		else 
		{
			PPPoint* p = (PPPoint*)event->getDataPtr();
			
			if (p->y < buttonBar->getLocation().y)
			{
				pp_int32 bsize = buttonBar->getSize().height;
				pp_int32 entireSize = size.height - SLIDERBUTTONHEIGHT*2;

				float f = (float)entireSize/(float)(entireSize - bsize);

				currentBarPosition-=(pp_int32)(currentBarSize*f);
				if (currentBarPosition < 0) currentBarPosition = 0;
				setBarPosition(currentBarPosition);
				
				// Call parent event listener
				//PPEvent e(eBarPosChanged);
				//return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
			}
			else if (p->y > buttonBar->getLocation().y + buttonBar->getSize().height)
			{
				pp_int32 bsize = buttonBar->getSize().height;
				pp_int32 entireSize = size.height - SLIDERBUTTONHEIGHT*2;

				float f = (float)entireSize/(float)(entireSize - bsize);

				currentBarPosition+=(pp_int32)(currentBarSize*f);
				if (currentBarPosition > 65536) currentBarPosition = 65536;
				setBarPosition(currentBarPosition);
				
				// Call parent event listener
				//PPEvent e(eBarPosChanged);
				//return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
			}
		}
				

	}
	else if ((event->getID() == eLMouseDrag && 
			 sender == reinterpret_cast<PPObject*>(buttonBar)) ||
			 event->getID() == eRMouseDrag && 
			 sender == reinterpret_cast<PPObject*>(buttonBar))
	{
		PPPoint* p = (PPPoint*)event->getDataPtr();
	
		pp_int32 pos = 0,dx,nx;

		if (horizontal)
		{

			dx = (p->x - caughtMouseLocation.x);
			
			/*if (dx < 0)
			{
				__asm
				{
					int 3
				}
			}*/
			
			nx = caughtControlLocation.x + dx;
			//if (nx < location.x + SLIDERBUTTONSIZE)
			//	nx = location.x + SLIDERBUTTONSIZE;
			//if (nx > location.x + size.width - SLIDERBUTTONSIZE*2 - buttonBar->getSize().width)
			//	nx = location.x + size.width - SLIDERBUTTONSIZE*2 - buttonBar->getSize().width;
			
			nx-=(location.x + SLIDERBUTTONWIDTH);
			
			pp_int32 d = (size.width - buttonBar->getSize().width - SLIDERBUTTONWIDTH*2);
			
			if (d != 0)
				pos = nx*65536 / d;			
		}
		else
		{
			dx = (p->y - caughtMouseLocation.y);
			
			/*if (dx < 0)
				__asm
			{
				{
					int 3
				}
			}*/
			
			nx = caughtControlLocation.y + dx;
			//if (nx < location.x + SLIDERBUTTONSIZE)
			//	nx = location.x + SLIDERBUTTONSIZE;
			//if (nx > location.x + size.width - SLIDERBUTTONSIZE*2 - buttonBar->getSize().width)
			//	nx = location.x + size.width - SLIDERBUTTONSIZE*2 - buttonBar->getSize().width;
			
			nx-=(location.y + SLIDERBUTTONHEIGHT);
			
			pp_int32 d = (size.height - buttonBar->getSize().height - SLIDERBUTTONHEIGHT*2);
			
			if (d != 0)
				pos = nx*65536 / d;
		}
		
		setBarPosition(pos);
		// Call parent event listener
		//PPEvent e(eBarPosChanged);
		//return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
	}
	else
	{
		return 0;
	}

	float f = getBarPosition() / 65536.0f;

	currentValue = (pp_int32)((f * (maxValue - minValue)) + minValue);

	parentScreen->paintControl(this);

	PPEvent e(eValueChanged);

	return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);

}

void PPSlider::setSize(pp_uint32 size)
{
	PPPoint buttonDownPos, buttonBarPos;
	PPSize buttonBarSize;

	if (horizontal)
	{
		this->size.height = SLIDERBUTTONHEIGHT;
		this->size.width = size;
	
		buttonDownPos = PPPoint(location.x + size - SLIDERBUTTONWIDTH, location.y);
	
		buttonBarPos = PPPoint(location.x + SLIDERBUTTONWIDTH, location.y);
	
		buttonBarSize = PPSize((size - SLIDERBUTTONWIDTH*2), SLIDERBUTTONHEIGHT);  
	}
	else
	{
		this->size.width = SLIDERBUTTONWIDTH;
		this->size.height = size;
		
		buttonDownPos = PPPoint(location.x, location.y + size - SLIDERBUTTONHEIGHT);
		
		buttonBarPos = PPPoint(location.x, location.y + SLIDERBUTTONHEIGHT);
		
		buttonBarSize = PPSize(SLIDERBUTTONWIDTH, (size - SLIDERBUTTONHEIGHT*2));  
	}

	backgroundButton->setLocation(location);
	backgroundButton->setSize(this->size);

	buttonUp->setLocation(location);

	buttonDown->setLocation(buttonDownPos);

}
