/*
   ui/slider.cc
   This file is part of the Osirion project and is distributed under
   the terms of the GNU General Public License version 2
*/

#include "math/functions.h"
#include "ui/paint.h"
#include "ui/slider.h"

namespace ui {

Slider::Slider(Widget *parent, const float minimum, const float maximum) : Widget(parent)
{
	set_label("slider");
	slider_dragging = false;
	
	slider_minimum = minimum;
	slider_maximum = maximum;
	slider_value = slider_minimum;

	slider_minbutton = new ui::Button(this, "<");
	slider_minbutton->set_border(false);
	
	slider_decbutton = new ui::Button(this, "-");
	slider_decbutton->set_border(false);
	
	slider_incbutton = new ui::Button(this, "+");
	slider_incbutton->set_border(false);
	
	slider_maxbutton = new ui::Button(this, ">");
	slider_maxbutton->set_border(false);
	
	set_background(true);
	set_border(true);
	
	validate();
}

Slider::~Slider() {
}

void Slider::set_value(const float value)
{
	slider_value = value;
	validate();
}

void Slider::set_minimum(const float minimum)
{
	slider_minimum = minimum;
	validate();
}

void Slider::set_maximum(const float maximum)
{
	slider_maximum = maximum;
	validate();
}

void Slider::set_range(const float minimum, const float maximum)
{
	slider_minimum = minimum;
	slider_maximum = maximum;
	validate();
}

void Slider::validate()
{
	if (slider_minimum > slider_maximum) {
		math::swap(slider_minimum, slider_maximum);
	}

	math::clamp(slider_value, slider_minimum, slider_maximum);
}

void Slider::print(const size_t indent) const
{
	std::string marker("");
	con_print << aux::pad_left(marker, indent*2) << label() << " \"" << minimum() << "-" << maximum() << "\"" << std::endl;
}

bool Slider::on_emit(Widget *sender, const Event event, void *data)
{
	if (event == EventButtonClicked) {
		if (sender == slider_minbutton) {
			slider_value = slider_minimum;
			emit(EventSliderChanged, this);
			return true;
		} else if (sender == slider_decbutton) {
			if (slider_value > slider_minimum) {
				slider_value--;
				emit(EventSliderChanged, this);
			}
			return true;
		} else if (sender == slider_incbutton) {
			if (slider_value < slider_maximum) {
				slider_value++;
				emit(EventSliderChanged, this);
			}
			return true;
		} else if (sender == slider_maxbutton) {
			slider_value = slider_maximum;
			emit(EventSliderChanged, this);
			return true;
		}
	}
	return false;
}

bool Slider::on_keypress(const int key, const unsigned int modifier)
{
	switch (key) {
		
		case 512 + SDL_BUTTON_WHEELUP:
			if (slider_value < slider_maximum) {
				slider_value++;
				emit(EventSliderChanged, this);
			}
			return true;
			break;
		
		case 512 + SDL_BUTTON_WHEELDOWN:
			if (slider_value > slider_minimum) {
				slider_value--;
				emit(EventSliderChanged, this);
			}
			return true;
			break;
			
		case 512 + SDL_BUTTON_LEFT:
			if (slider_maximum > slider_minimum) {
				// TODO position hit test
				slider_dragging = true;
			}
			return true;
			break;
			
		default:
			break;
	}

	return false;
}

bool Slider::on_keyrelease(const int key, const unsigned int modifier)
{
	if (key == 512 + SDL_BUTTON_LEFT) {
		slider_dragging = false;
		return true;
	}

	return false;
}

void Slider::on_mousemove(const math::Vector2f &cursor) 
{
	if ((width() <= 0) || (height() <= 0)) {
		return;
	}
	
	if (slider_dragging && (slider_maximum > slider_minimum)) {
		float x = cursor.x();
		math::clamp(x,  2.0f * height(), width() - 2.0f * height());
		const float w = (width() - 4.0f * height());
		const float s = w / (slider_maximum - slider_minimum);
		const float p = x - 2.0f * height();
		const float newvalue = slider_minimum + round(p /s);
		if (slider_value != newvalue) {
			slider_value = newvalue;
			emit(EventSliderChanged, this);
		}
	}
}

void Slider::on_mouseover(const math::Vector2f &cursor)
{
	slider_dragging = false;
}

void Slider::show()
{
	Widget::show();
	resize();
}

void Slider::resize()
{
	// note: slider expects width > height
	slider_minbutton->set_size(height(), height());
	slider_minbutton->set_location(0, 0);
	
	slider_decbutton->set_size(height(), height());
	slider_decbutton->set_location(slider_minbutton->right(), 0);
	
	slider_maxbutton->set_size(height(), height());
	slider_maxbutton->set_location(width() - slider_maxbutton->width(), 0);
	
	slider_incbutton->set_size(height(), height());
	slider_incbutton->set_location(slider_maxbutton->left() - slider_incbutton->width() , 0);
}

void Slider::draw()
{
	if (slider_maximum > slider_minimum) {
		const float range = (slider_value - slider_minimum) / (slider_maximum - slider_minimum);
		const float x = (width() - 5.0f * height()) * range;
		if (slider_dragging) {
			Paint::set_color(palette()->highlight());
		} else {
			Paint::set_color(palette()->foreground());
		}
		Paint::draw_rectangle(math::Vector2f(global_location().x() + 2.0f * height() + x + 1, global_location().y() +1 ), math::Vector2f(height() - 2, height() - 2));
	}
	
	Widget::draw();
}

} // namespace ui