/*
 * \brief  Loadbar widget
 * \author Norman Feske
 * \date   2006-08-30
 */

/*
 * Copyright (C) 2006-2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

#ifndef _LOADBAR_H_
#define _LOADBAR_H_

#include "widgets.h"
#include "styles.h"
#include "fade_icon.h"

#define LOADBAR_RGBA  _binary_loadbar_rgba_start
#define REDBAR_RGBA   _binary_redbar_rgba_start
#define WHITEBAR_RGBA _binary_whitebar_rgba_start

extern unsigned char LOADBAR_RGBA[];
extern unsigned char REDBAR_RGBA[];
extern unsigned char WHITEBAR_RGBA[];

struct Loadbar_listener
{
	virtual ~Loadbar_listener() { }

	virtual void loadbar_changed(int mx) = 0;
};


class Loadbar_event_handler : public Scout::Event_handler
{
	private:

		/*
		 * Noncopyable
		 */
		Loadbar_event_handler(Loadbar_event_handler const &);
		Loadbar_event_handler &operator = (Loadbar_event_handler const &);

		Loadbar_listener *_listener;

	public:

		Loadbar_event_handler(Loadbar_listener *listener):
			_listener(listener) { }

		/**
		 * Event handler interface
		 */
		void handle_event(Scout::Event const &ev) override
		{
			static int key_cnt;
			using Scout::Event;

			if (ev.type == Event::PRESS)   key_cnt++;
			if (ev.type == Event::RELEASE) key_cnt--;

			if (ev.type == Event::PRESS || ev.type == Event::MOTION)
				if (_listener && key_cnt > 0)
					_listener->loadbar_changed(ev.mouse_position.x());
		}
};


template <typename PT>
class Loadbar : public Scout::Parent_element
{
	private:

		/*
		 * Noncopyable
		 */
		Loadbar(Loadbar const &);
		Loadbar &operator = (Loadbar const &);

		enum {
			_LW = 16,
			_LH = 16,
		};

		bool _active;

		Scout::Fade_icon<PT, _LW, _LH> _cover { };
		Scout::Fade_icon<PT, _LW, _LH> _bar   { };

		Loadbar_event_handler _ev_handler;

		int _value;
		int _max_value;

		const char *_txt;
		int _txt_w, _txt_h, _txt_len;
		Scout::Font *_font;

		void _update_bar_geometry(int w)
		{
			using namespace Scout;

			int max_w = w - _LW;
			int bar_w = (_value * max_w) / _max_value;
			bar_w += _LW;
			_bar.geometry(Rect(Point(_bar.position().x(), _bar.position().y()),
			                   Area(bar_w, _LH)));
		}

	public:

		Loadbar(Loadbar_listener *listener = 0, Scout::Font *font = 0):
			_active(listener ? true : false),
			_ev_handler(listener),
			_value(0), _max_value(100),
			_txt(""), _txt_w(0), _txt_h(0), _txt_len(0),
			_font(font)
		{
			using namespace Scout;

			_min_size = Area(_min_size.w(), _LH);
			_cover.rgba(LOADBAR_RGBA);
			_cover.alpha(100);
			_cover.focus_alpha(150);

			_bar.rgba(_active ? REDBAR_RGBA : LOADBAR_RGBA);
			_bar.alpha(_active ? 150 : 255);
			_bar.default_alpha(150);

			if (_active)
				event_handler(&_ev_handler);

			append(&_cover);
			append(&_bar);
		}

		int value_by_xpos(int xpos)
		{
			xpos -= _LW/2;
			int max_w = _size.w() - _LW;
			return Scout::max(Scout::min((_max_value * xpos) / max_w, _max_value), 0);
		}

		int value() { return _value; }

		void value(int value)
		{
			_value = Scout::max(Scout::min(value, _max_value), 0);
			_update_bar_geometry(_size.w());
		}

		int  max_value() { return _max_value; }

		void max_value(int max_value)
		{
			_max_value = max_value;
			_update_bar_geometry(_size.w());
		}

		void txt(const char *txt)
		{
			if (!_font) return;
			_txt     = txt;
			_txt_w   = _font->string_width(_txt, Scout::strlen(_txt)).decimal();
			_txt_h   = _font->bounding_box().h();
			_txt_len = Scout::strlen(_txt);
		}

		/**
		 * Element interface
		 */
		void format_fixed_width(int w) override
		{
			using namespace Scout;
			_cover.geometry(Rect(Point(0, 0), Area(w, _LH)));
			_update_bar_geometry(w);
			_min_size = Scout::Area(w, _min_size.h());
		}

		void draw(Scout::Canvas_base &canvas, Scout::Point abs_position) override
		{
			Parent_element::draw(canvas, abs_position);

			if (!_font) return;

			using namespace Scout;

			int txt_x = abs_position.x() + _position.x() + max((_size.w() - _txt_w)/2, 8UL);
			int txt_y = abs_position.y() + _position.y() + max((_size.h() - _txt_h)/2, 0UL) - 1;

			/* shrink clipping area to text area (limit too long label) */
			int cx1 = canvas.clip().x1(), cy1 = canvas.clip().y1();
			int cx2 = canvas.clip().x2(), cy2 = canvas.clip().y2();
			int nx1 = max(cx1, _position.x() + abs_position.x());
			int ny1 = max(cy1, _position.y() + abs_position.y());
			int nx2 = min(cx2, nx1 + (int)_size.w() - 8);
			int ny2 = min(cy2, ny1 + (int)_size.h());
			canvas.clip(Rect(Point(nx1, ny1), Area(nx2 - nx1 + 1, ny2 - ny1 + 1)));

			canvas.draw_string(txt_x , txt_y+1, _font, Color(0,0,0,150), _txt, strlen(_txt));
			canvas.draw_string(txt_x , txt_y, _font, Color(255,255,255,230), _txt, strlen(_txt));

			/* reset clipping */
			canvas.clip(Rect(Point(cx1, cy1), Area(cx2 - cx1 + 1, cy2 - cy1 + 1)));
		}

		void mfocus(int flag) override
		{
			if (!_active) return;

			_bar.mfocus(flag);
			_cover.mfocus(flag);
		}
};


template <typename PT>
class Kbyte_loadbar : public Loadbar<PT>
{
	private:

		typedef Genode::String<32> Label;

		Label _label { };

		struct Kbytes
		{
			int const _value;

			Kbytes(int value) : _value(value) { }

			void print(Genode::Output &out) const
			{
				if (_value >= 10*1024)
					Genode::print(out, _value/1024, " MByte");
				else
					Genode::print(out, _value, " KByte");
			}
		};

		void _update_label()
		{
			_label = Label(Kbytes(Loadbar<PT>::value()), " / ",
			               Kbytes(Loadbar<PT>::max_value()));

			Loadbar<PT>::txt(_label.string());
		}

	public:

		Kbyte_loadbar(Loadbar_listener *listener, Scout::Font *font = 0):
			Loadbar<PT>(listener, font)
		{
			_update_label();
		}

		void value(int val)
		{
			Loadbar<PT>::value(val);
			_update_label();
		}

		void max_value(int max_value)
		{
			Loadbar<PT>::max_value(max_value);
			_update_label();
		}
};

#endif
