#include <psemek/ui/button.hpp>

namespace psemek::ui
{

	label * button::label() const
	{
		return dynamic_cast<struct label *>(child().get());
	}

	image_view * button::icon() const
	{
		return dynamic_cast<struct image_view *>(child().get());
	}

	bool button::on_event(mouse_move const & e)
	{
		bool const over = shape().contains(math::cast<float>(e.position));

		switch (state_) {
		case state_t::normal:
			if (over)
			{
				state_ = state_t::mouseover;
				on_state_changed(state_t::normal);
			}
			break;
		case state_t::mouseover:
		case state_t::mousedown:
			if (!over)
			{
				auto old = state_;
				state_ = state_t::normal;
				on_state_changed(old);
			}
			break;
		}

		return false;
	}

	bool button::on_event(mouse_click const & e)
	{
		if (e.button != mouse_button::left) return false;

		switch (state_) {
		case state_t::normal:
			break;
		case state_t::mouseover:
			if (e.down)
			{
				state_ = state_t::mousedown;
				post_on_click();
				on_state_changed(state_t::mouseover);
				return true;
			}
			break;
		case state_t::mousedown:
			if (!e.down)
			{
				state_ = state_t::mouseover;
				post_on_release();
				on_state_changed(state_t::mousedown);
				return true;
			}
			break;
		}

		return false;
	}

	void button::update(float dt)
	{
		if (repeat_)
		{
			repeat_->timer += dt;

			switch (repeat_->state)
			{
			case repeat::state_t::idle:
				break;
			case repeat::state_t::wait:
				if (repeat_->timer >= repeat_->wait)
				{
					repeat_->timer -= repeat_->wait;
					repeat_->state = repeat::state_t::repeat;
					post_on_click();
				}
				break;
			case repeat::state_t::repeat:
				if (repeat_->timer >= repeat_->period)
				{
					repeat_->timer -= repeat_->period;
					repeat_->state = repeat::state_t::repeat;
					post_on_click();
				}
				break;
			}
		}
	}

	void button::set_enabled(bool value)
	{
		element::set_enabled(value);
		if (!value && repeat_)
			repeat_->state = repeat::state_t::idle;
	}

	void button::set_hidden(bool value)
	{
		element::set_hidden(value);
		if (value && repeat_)
			repeat_->state = repeat::state_t::idle;
	}

	void button::set_repeat(float wait, float period)
	{
		repeat_ = repeat{wait, period};
	}

	void button::set_no_repeat()
	{
		repeat_ = std::nullopt;
	}

	void button::on_state_changed(state_t old)
	{
		if (repeat_)
		{
			if (state() == state_t::mousedown)
			{
				repeat_->timer = 0.f;
				repeat_->state = repeat::state_t::wait;
			}
			else
			{
				repeat_->state = repeat::state_t::idle;
			}
		}

		if (state() == state_t::mousedown || old == state_t::mousedown)
		{
			post_reshape();
		}
	}

	void button::post_on_click()
	{
		if (on_click_callback_)
			post(on_click_callback_);
	}

	void button::post_on_release()
	{
		if (on_release_callback_)
			post(on_release_callback_);
	}

}
