#include "../util/nowarnings.h"
#include "ui/SliderPlus.h"
#include "ui/UIUtil.h"
#include "VGUI_MouseCode.h"

SliderPlus::SliderPlus(int inX, int inY,int inWidth, int inHeight, bool inIsVertical) : Panel(inX, inY, inWidth, inHeight)
{
	this->mDragging = false;
	this->mNobDragStartPos[0] = this->mNobDragStartPos[1] = 0;
	this->mDragStartPos[0] = this->mDragStartPos[1] = 0;
	this->mNobPos[0] = this->mNobPos[1] = 0;
	this->mRange[0] = 0;
	this->mRange[1] = 10;
	this->mValue = 0;
	this->mRangeWindow = 10;
	this->mIsVertical = inIsVertical;
	this->mLastMouseX = this->mLastMouseY = 0;

	// Add ourselves as our own input signal...kinda strange sounding but useful
	this->addInputSignal(this);
}

void SliderPlus::addIntChangeSignal(IntChangeSignal* inChangeSignal)
{
	// Add inChangeSignal to list
}

void SliderPlus::computeNobBox(int& outStartX, int& outStartY, int& outEndX, int& outEndY)
{
	int theWidth, theHeight;
	this->getSize(theWidth, theHeight);
	
	outStartX = this->mNobPos[0];
	outStartY = this->mNobPos[1];
	
	if(this->mIsVertical)
	{
		outEndX = outStartX + theWidth - 1;
		outEndY = outStartY + this->mRangeWindow;
	}
	else
	{
		outEndX = outStartX + this->mRangeWindow;
		outEndY = outStartY + theHeight - 1;
	}
}

void SliderPlus::fireIntChangeSignal()
{
	// Run through list of change signals, calling intChanged() on each one
}

void SliderPlus::getNobPos(int& outMin, int& outMax)
{
	// Return the upper left corner of the nob
	outMin = this->mNobPos[0];
	outMax = this->mNobPos[1];
}

void SliderPlus::getRange(int& outMinRange, int& outMaxRange)
{
	// Return the min and max range
	outMinRange = this->mRange[0];
	outMaxRange = this->mRange[1];
}

int	SliderPlus::getRangeWindow()
{
	return this->mRangeWindow;
}

int	SliderPlus::getValue()
{
	return this->mValue;
}

bool SliderPlus::hasFullRange()
{
	return false;
}

bool SliderPlus::isVertical()
{
	return this->mIsVertical;
}

void SliderPlus::paintBackground()
{
	int theStartX, theStartY;
	int theWidth, theHeight;
	int theEndX, theEndY;

	//this->getPos(theStartX, theStartY);
	theStartX = theStartY = 0;
	
	this->getSize(theWidth, theHeight);
	
	Color theColor;
	int theR, theG, theB, theA;
	
	this->getBgColor(theColor);
	theColor.getColor(theR, theG, theB, theA);
	
	// Draw rectangle the size of the slider in background color
	theEndX = theStartX + theWidth;
	theEndY = theStartY + theHeight;
	
	// Draw hollow box in bg color
	vguiSimpleBox(theStartX, theStartY, theEndX-1, theEndY-1, theR, theG, theB, theA);
	
	// Draw nob in foreground color, size of range window
	this->getFgColor(theColor);
	theColor.getColor(theR, theG, theB, theA);
	
	// Compute the nob position so we can draw a box for it, all numbers are in pixels relative to component
	this->computeNobBox(theStartX, theStartY, theEndX, theEndY);
	
	// Draw hollow box in fg color
	vguiSimpleBox(theStartX, theStartY, theEndX, theEndY, theR, theG, theB, theA);
}

void SliderPlus::cursorMoved(int inX, int inY, Panel* inPanel)
{
	// Are we dragging the nub?
	if(this->mDragging)
	{
		// Look at inX, inY and set our new nob pos
		this->mNobPos[0] = this->mNobPos[1] = 0;

		if(this->mIsVertical)
		{
			this->mNobPos[1] = inY;
		}
		else
		{
			this->mNobPos[0] = inX;
		}

		// Recompute the value from it
		this->recomputeValueFromNobPos();
	}
	
	this->mLastMouseX = inX;
	this->mLastMouseY = inY;
}

void SliderPlus::mousePressed(MouseCode inCode, Panel* inPanel)
{
	int theStartX, theStartY, theEndX, theEndY;
	this->computeNobBox(theStartX, theStartY, theEndX, theEndY);
	
	// Was LMB was pressed?
	if(inCode == MOUSE_LEFT)
	{
		// Pressed while over the nob?
		if((this->mLastMouseX >= theStartX) && (this->mLastMouseX <= theEndX) && (this->mLastMouseY >= theStartY) && (this->mLastMouseY <= theEndY))
		{
			// Remember this starting point and remember that we're dragging
			this->mDragStartPos[0] = this->mLastMouseX;
			this->mDragStartPos[1] = this->mLastMouseY;
			this->mDragging = true;
		}
		// Above or below nub?  
		else
		{
			// TODO?: Remember this so we can start adjusting by big increments.  For now, just adjust once.
			// Adjust value by major increment
			//this->recomputeNobPosFromValue();
		}
	}
	// Was RMB pressed above or below the nub?  
	else if(inCode == MOUSE_RIGHT)
	{
		// TODO?: Remember this so we can start adjusting by tiny increments. For now, just adjust once.
		// Adjust value by minor increment
		//this->recomputeNobPosFromValue();
	}
}

void SliderPlus::mouseReleased(MouseCode inCode, Panel* inPanel)
{
	// Forget that we are dragging or adjusting by big or small increments.
	if(inCode == MOUSE_LEFT)
	{
		this->mDragging = false;
	}
}

void SliderPlus::mouseWheeled(int delta, Panel* panel)
{
}

void SliderPlus::recomputeNobPosFromValue()
{
	// Look at current value and range and set the nob pos from it
	int theWidth, theHeight;
	this->getSize(theWidth, theHeight);

	float theNormalizedNobPos = (this->mValue - this->mRange[0])/(float)(this->mRange[1]);
	if(this->mIsVertical)
	{
		this->mNobPos[0] = 0;
		this->mNobPos[1] = (int)(theNormalizedNobPos*theHeight);
	}
	else
	{
		this->mNobPos[0] = (int)(theNormalizedNobPos*theWidth);
		this->mNobPos[1] = 0;
	}
}

void SliderPlus::recomputeValueFromNobPos()
{
	// Look at nob pos and set new value
	int theWidth, theHeight;
	this->getSize(theWidth, theHeight);
	
	int theRelevantValue = this->mNobPos[0];
	int theRelevantMaxPixels = theWidth;

	if(this->mIsVertical)
	{
		theRelevantValue = this->mNobPos[1];
		theRelevantMaxPixels = theHeight;
	}

	float theNormalizedValue = (theRelevantValue/(float)theRelevantMaxPixels);
	int theNewValue = this->mRange[0] + (int)(theNormalizedValue*this->mRange[1]);
	
	this->setValue(theNewValue);
}

void SliderPlus::setRange(int inMinRange, int inMaxRange)
{
	// Set the range ("value")
	this->mRange[0] = min(inMinRange, inMaxRange);
	this->mRange[1] = max(inMinRange, inMaxRange);
}

void SliderPlus::setRangeWindow(int inRangeWindow)
{
	// Set the range window (in pixels)
	this->mRangeWindow = inRangeWindow;
}

void SliderPlus::setSize(int inWidth, int inHeight)
{
	// Call parent setSize
	Panel::setSize(inWidth, inHeight);

	// Recompute something?
}

void SliderPlus::setValue(int inValue)
{
	// Only allow setting the value within our specified range
	int theNewValue = inValue;
	theNewValue = max(theNewValue, this->mRange[0]);
	theNewValue = min(theNewValue, this->mRange[1]);

	this->mValue = theNewValue;

	this->recomputeNobPosFromValue();
}