/*
	GWEN
	Copyright (c) 2010 Facepunch Studios
	See license in Gwen.h
*/

#include "Gwen/Utility.h"
#include "Gwen/Controls/ColorControls.h"

using namespace Gwen;
using namespace Gwen::Controls;

//Find a place to put these...
Color HSVToColor(float h, float s, float v)
{
	if (h < 0.0f) h += 360.0f;
	if (h > 360.0f) h -= 360.0f;

	s *= 255.0f;
	v *= 255.0f;

	float r, g, b;

	if (!h && !s)
	{
		r = g = b = v;
	}
	double min, max, delta, hue;

	max = v;
	delta = (max * s) / 255.0;
	min = max - delta;

	hue = h;
	if (h > 300 || h <= 60)
	{
		r = (int)max;
		if (h > 300)
		{
			g = (int)min;
			hue = (hue - 360.0) / 60.0;
			b = (int)((hue * delta - min) * -1);
		}
		else
		{
			b = (int)min;
			hue = hue / 60.0;
			g = (int)(hue * delta + min);
		}
	}
	else if (h > 60 && h < 180)
	{
		g = (int)max;
		if (h < 120)
		{
			b = (int)min;
			hue = (hue / 60.0 - 2.0) * delta;
			r = (int)(min - hue);
		}
		else
		{
			r = (int)min;
			hue = (hue / 60 - 2.0) * delta;
			b = (int)(min + hue);
		}
	}
	else
	{
		b = (int)max;
		if (h < 240)
		{
			r = (int)min;
			hue = (hue / 60.0 - 4.0) * delta;
			g = (int)(min - hue);
		}
		else
		{
			g = (int)min;
			hue = (hue / 60 - 4.0) * delta;
			r = (int)(min + hue);
		}
	}

	return Color(r, g, b, 255);
}

HSV RGBtoHSV(int r, int g, int b)
{
	double min, max, delta, temp;
	min = GwenUtil_Min(r, GwenUtil_Min(g, b));
	max = GwenUtil_Max(r, GwenUtil_Max(g, b));
	delta = max - min;

	HSV hsv;
	hsv.v = (int)max;
	if (!delta)
	{
		hsv.h = hsv.s = 0;
	}
	else
	{
		temp = delta / max;
		hsv.s = (int)(temp * 255);

		if (r == (int)max)
		{
			temp = (double)(g - b) / delta;
		}
		else if (g == (int)max)
		{
			temp = 2.0 + ((double)(b - r) / delta);
		}
		else
		{
			temp = 4.0 + ((double)(r - g) / delta);
		}
		temp *= 60;
		if (temp < 0)
		{
			temp += 360;
		}
		if (temp == 360)
		{
			temp = 0;
		}
		hsv.h = (int)temp;
	}

	hsv.s /= 255.0f;
	hsv.v /= 255.0f;

	return hsv;
}

GWEN_CONTROL_CONSTRUCTOR(ColorLerpBox)
{
	SetColor(Gwen::Color(255, 128, 0, 255));
	SetSize(128, 128);
	SetMouseInputEnabled(true);
	m_bDepressed = false;
}

//Find a place to put this? color member?
Gwen::Color LerpColor(Gwen::Color& toColor, Gwen::Color& fromColor, float amount)
{
	Gwen::Color colorDelta = toColor - fromColor;

	colorDelta.r *= amount;
	colorDelta.g *= amount;
	colorDelta.b *= amount;

	Gwen::Color newColor = fromColor + colorDelta;
	return newColor;
}

Gwen::Color ColorLerpBox::GetSelectedColor()
{
	return GetColorAtPos(cursorPos.x, cursorPos.y);
}

void ColorLerpBox::SetColor(Gwen::Color color, bool onlyHue)
{
	HSV hsv = RGBtoHSV(color.r, color.g, color.b);
	m_Hue = hsv.h;
	if (!onlyHue)
	{
		cursorPos.x = hsv.s * Width();
		cursorPos.y = (1 - hsv.v) * Height();
	}

	onSelectionChanged.Call(this);
}

void ColorLerpBox::OnMouseMoved(int x, int y, int /*deltaX*/, int /*deltaY*/)
{
	if (m_bDepressed)
	{
		cursorPos = CanvasPosToLocal(Gwen::Point(x, y));
		//Do we have clamp?
		if (cursorPos.x < 0)
			cursorPos.x = 0;
		if (cursorPos.x > Width())
			cursorPos.x = Width();

		if (cursorPos.y < 0)
			cursorPos.y = 0;
		if (cursorPos.y > Height())
			cursorPos.y = Height();

		onSelectionChanged.Call(this);
	}
}

void ColorLerpBox::OnMouseClickLeft(int x, int y, bool bDown)
{
	m_bDepressed = bDown;
	if (bDown)
		Gwen::MouseFocus = this;
	else
		Gwen::MouseFocus = NULL;

	OnMouseMoved(x, y, 0, 0);
}

Gwen::Color ColorLerpBox::GetColorAtPos(int x, int y)
{
	float xPercent = ((float)x / (float)Width());
	float yPercent = 1 - ((float)y / (float)Height());

	Gwen::Color result = HSVToColor(m_Hue, xPercent, yPercent);

	result.a = 255;

	return result;
}
void ColorLerpBox::Render(Gwen::Skin::Base* skin)
{
	//Is there any way to move this into skin? Not for now, no idea how we'll "actually" render these
	BaseClass::Render(skin);
	for (int x = 0; x < Width(); x++)
	{
		for (int y = 0; y < Height(); y++)
		{
			skin->GetRender()->SetDrawColor(GetColorAtPos(x, y));
			skin->GetRender()->DrawPixel(x, y);
		}
	}

	skin->GetRender()->SetDrawColor(Gwen::Color(0, 0, 0, 255));
	skin->GetRender()->DrawLinedRect(GetRenderBounds());

	Gwen::Color selected = GetSelectedColor();
	if ((selected.r + selected.g + selected.b) / 3 < 170)
		skin->GetRender()->SetDrawColor(Gwen::Color(255, 255, 255, 255));
	else
		skin->GetRender()->SetDrawColor(Gwen::Color(0, 0, 0, 255));

	Gwen::Rect testRect = Gwen::Rect(cursorPos.x - 3, cursorPos.y - 3, 6, 6);

	skin->GetRender()->DrawShavedCornerRect(testRect);
}

GWEN_CONTROL_CONSTRUCTOR(ColorSlider)
{
	SetSize(32, 128);
	SetMouseInputEnabled(true);
	m_bDepressed = false;
}

void ColorSlider::Render(Gwen::Skin::Base* skin)
{
	//Is there any way to move this into skin? Not for now, no idea how we'll "actually" render these
	int y = 0;
	for (y = 0; y < Height(); y++)
	{
		float yPercent = (float)y / (float)Height();
		skin->GetRender()->SetDrawColor(HSVToColor(yPercent * 360, 1, 1));
		skin->GetRender()->DrawFilledRect(Gwen::Rect(5, y, Width() - 10, 1));
	}

	int drawHeight = m_iSelectedDist - 3;

	//Draw our selectors
	skin->GetRender()->SetDrawColor(Gwen::Color(0, 0, 0, 255));
	skin->GetRender()->DrawFilledRect(Gwen::Rect(0, drawHeight + 2, Width(), 1));
	skin->GetRender()->DrawFilledRect(Gwen::Rect(0, drawHeight, 5, 5));
	skin->GetRender()->DrawFilledRect(Gwen::Rect(Width() - 5, drawHeight, 5, 5));
	skin->GetRender()->SetDrawColor(Gwen::Color(255, 255, 255, 255));
	skin->GetRender()->DrawFilledRect(Gwen::Rect(1, drawHeight + 1, 3, 3));
	skin->GetRender()->DrawFilledRect(Gwen::Rect(Width() - 4, drawHeight + 1, 3, 3));
}

void ColorSlider::OnMouseClickLeft(int x, int y, bool bDown)
{
	m_bDepressed = bDown;
	if (bDown)
		Gwen::MouseFocus = this;
	else
		Gwen::MouseFocus = NULL;

	OnMouseMoved(x, y, 0, 0);
}

Gwen::Color ColorSlider::GetColorAtHeight(int y)
{
	float yPercent = (float)y / (float)Height();
	return HSVToColor(yPercent * 360, 1, 1);
}
void ColorSlider::OnMouseMoved(int x, int y, int /*deltaX*/, int /*deltaY*/)
{
	if (m_bDepressed)
	{
		Gwen::Point cursorPos = CanvasPosToLocal(Gwen::Point(x, y));

		if (cursorPos.y < 0)
			cursorPos.y = 0;
		if (cursorPos.y > Height())
			cursorPos.y = Height();

		m_iSelectedDist = cursorPos.y;
		onSelectionChanged.Call(this);
	}
}

void ColorSlider::SetColor(Gwen::Color color)
{
	HSV hsv = RGBtoHSV(color.r, color.g, color.b);

	m_iSelectedDist = hsv.h / 360 * Height();

	onSelectionChanged.Call(this);
}

Gwen::Color ColorSlider::GetSelectedColor()
{
	return GetColorAtHeight(m_iSelectedDist);
}
