/*
 * \brief   Document structure elements
 * \date    2005-10-24
 * \author  Norman Feske <norman.feske@genode-labs.com>
 */

/*
 * Copyright (C) 2005-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 _ELEMENTS_H_
#define _ELEMENTS_H_

#include <scout/parent_element.h>
#include <scout/string.h>
#include <scout/fader.h>
#include <scout/platform.h>

namespace Scout {
	class Style;
	class Browser;
	class Token;
	class Link;
	class Link_token;
	class Launcher_config;
	class Launcher;
	class Launcher_link_token;
	class Block;
	class Center;
	class Png_image;
	class Chapter;
	class Document;
	class Spacer;
	class Verbatim;
	class Item;
	class Generic_icon;
	class Navbar;

	/**
	 * Link anchor
	 *
	 * An anchor marks a location within a document that can be addressed by a
	 * link.
	 */
	typedef Element Anchor;
}


/**
 * Textual style
 *
 * A style describes the font, color and accentuations of tokens.
 */
class Scout::Style
{
	public:

		enum {
			ATTR_BOLD = 0x1,
		};

		Font  *font;
		Color  color;
		int    attr;

		Style(Font *f, Color c, int a) : font(f), color(c), attr(a) { }
};


/**
 * String token
 *
 * A Token is a group of characters that are handled as an atomic text unit.
 * Line wrapping is performed at the granularity of tokens.
 */
class Scout::Token : public Element
{
	private:

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

	protected:

		const char  *_str;       /* start  of string   */
		int          _len;       /* length of string   */
		Style       *_style;     /* textual style      */
		Color        _col;       /* current text color */
		Color        _outline;   /* outline color      */

	public:

		/**
		 * Constructor
		 */
		Token(Style *style, const char *str, int len);

		/**
		 * Element interface
		 */
		void draw(Canvas_base &, Point) override;
		void refresh() { redraw_area(-1, 0, _size.w() + 1, _size.h()); }
};


/**
 * Link that references an anchor within the document
 */
class Scout::Link
{
	protected:

		Anchor *_dst;  /* link destination */

	public:

		/**
		 * Constructor
		 */
		explicit Link(Anchor *dst) : _dst(dst) { }

		/**
		 * Accessor function
		 */
		Anchor *dst() { return _dst; }
};


/**
 * Textual link
 */
class Scout::Link_token : public Token, private Link, public Event_handler,
                          private Fader
{
	private:

		enum { _MAX_ALPHA = 50 };

	protected:

		using Link::_dst;

	public:

		/**
		 * Constructor
		 */
		Link_token(Style *style, const char *str, int len, Anchor *dst)
		: Token(style, str, len), Link(dst)
		{
			_flags.takes_focus = 1;
			_curr_value        = 0;
			event_handler(this);
		}

		using Fader::step;
		using Fader::curr;
		
		/**
		 * Element interface
		 */
		void draw(Canvas_base &canvas, Point abs_position) override
		{
			_outline = Color(_style->color.r,
			                 _style->color.g,
			                 _style->color.b, _curr_value);

			Token::draw(canvas, abs_position);

			canvas.draw_box(_position.x() + abs_position.x(),
			                _position.y() + abs_position.y() + _size.h() - 1,
			                _size.w(), 1, Color(0,0,255));
		}

		void mfocus(int flag) override
		{
			/*
			 * highlight link of all siblings that point to the same link.
			 */
			if (_dst) {
				Element const *dst = _dst;
				for_each_sibling([dst, flag] (Element &e) {
					Link_token *l = dynamic_cast<Link_token *>(&e);
					if (l && l->has_destination(dst))
						l->highlight_link(flag);
				});
			}

			Token::mfocus(flag);
		}

		void highlight_link(bool flag)
		{
			if (flag && _curr_value != _MAX_ALPHA)
				fade_to(_MAX_ALPHA, 50);

			if (!flag && _curr_value != 0)
				fade_to(0, 2);
		}

		bool has_destination(Element const *e) { return e == _dst; }

		/**
		 * Event handler interface
		 */
		void handle_event(Event const &) override;

		/**
		 * Tick interface
		 */
		int on_tick() override
		{
			/* call on_tick function of the fader */
			if (Fader::on_tick() == 0) return 0;

			refresh();
			return 1;
		}
};


class Launchpad;


class Scout::Launcher : public Anchor
{
	public:

		typedef Genode::String<64> Name;

	private:

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

		Name                _prg_name;
		int                 _active    = 0;
		int                 _exec_once = 0;
		Launchpad          *_launchpad = nullptr;
		unsigned long const _caps;
		unsigned long       _quota;
		Launcher_config    *_config;

	public:

		static void init(Genode::Env &, Genode::Allocator &);

		/**
		 * Constructors
		 */
		Launcher(Name const &prg_name, int exec_once = 0,
		         unsigned long caps = 0, unsigned long quota = 0,
		         Launcher_config *config = 0)
		:
			_prg_name(prg_name), _active(1),
			_exec_once(exec_once), _caps(caps), _quota(quota), _config(config)
		{ }

		Launcher(Name const &prg_name, Launchpad *launchpad,
		         unsigned long caps, unsigned long quota,
		         Launcher_config *config = 0)
		:
			_prg_name(prg_name), _launchpad(launchpad),
			_caps(caps), _quota(quota), _config(config)
		{ }

		int active() const { return _active; }

		Name prg_name() const { return _prg_name; }

		void quota(unsigned long quota) { _quota = quota; }

		unsigned long quota() const { return _quota; }

		unsigned long caps() const { return _caps; }

		Launcher_config *config() { return _config; }

		/**
		 * Launch program
		 */
		void launch();
};


/**
 * Executable launcher link
 *
 * This is a special link that enables us to start external applications.
 */
class Scout::Launcher_link_token : public Link_token
{
	public:

		/**
		 * Constructor
		 */
		Launcher_link_token(Style *style, const char *str, int len, Launcher *l)
		: Link_token(style, str, len, l) { }

		/**
		 * Event handler interface
		 */
		void handle_event(Event const &) override;
};


/**
 * Text block
 *
 * A block is a group of tokens that form a paragraph. A block layouts its
 * tokens while using line wrapping.
 */
class Scout::Block : public Parent_element
{
	public:

		enum Alignment { LEFT, CENTER, RIGHT };
		enum Text_type { PLAIN, LINK, LAUNCHER };

	private:

		Alignment _align;             /* text alignment             */
		int       _second_indent;     /* indentation of second line */

		/**
		 * Append text to block
		 */
		void append_text(const char *str, Style *style, Text_type,
		                 Anchor *a, Launcher *l);

	public:

		/**
		 * Constructors
		 */
		explicit Block(int second_indent = 0)
		: _align(LEFT), _second_indent(second_indent) { }

		explicit Block(Alignment align)
		: _align(align), _second_indent(0) { }

		/**
		 * Define alignment of text
		 */
		void align(Alignment);

		/**
		 * Append a string of space-separated words
		 */
		void append_plaintext(const char *str, Style *style)
		{
			append_text(str, style, PLAIN, 0, 0);
		}

		/**
		 * Append a string of space-separated words a link
		 *
		 * \param dst  anchor that defines the link destination
		 */
		void append_linktext(const char *str, Style *style, Anchor *a)
		{
			append_text(str, style, LINK, a, 0);
		}

		/**
		 * Append a string of space-separated words a launcher-link
		 */
		void append_launchertext(const char *str, Style *style, Launcher *l)
		{
			append_text(str, style, LAUNCHER, 0, l);
		}

		/**
		 * Element interface
		 */
		void format_fixed_width(int w) override;
};


/**
 * Horizontally centered content
 */
class Scout::Center : public Parent_element
{
	public:

		/**
		 * Constructor
		 */
		explicit Center(Element *content = 0)
		{
			if (content) append(content);
		}

		/**
		 * Element interface
		 */
		void format_fixed_width(int w) override;
};


/**
 * PNG Image
 */
class Scout::Png_image : public Element
{
	private:

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

		void         *_png_data;
		Texture_base *_texture = nullptr;

	public:

		static void init(Genode::Allocator &);

		/**
		 * Constructor
		 */
		explicit Png_image(void *png_data) : _png_data(png_data) { }

		/**
		 * Accessor functions
		 */
		inline void *png_data() { return _png_data; }

		/**
		 * Element interface
		 */
		void fill_cache(Canvas_base &) override;
		void flush_cache(Canvas_base &) override;
		void draw(Canvas_base &, Point) override;
};


/**
 * Document
 */
class Scout::Document : public Parent_element
{
	private:

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

	public:

		Chapter    *toc   = nullptr;     /* table of contents */
		const char *title = "";          /* document title    */

		/**
		 * Constructor
		 */
		Document() { _flags.chapter = 1; }

		/**
		 * Element interface
		 */
		void format_fixed_width(int w) override
		{
			_min_size = Area(w, _format_children(0, w));
		}
};


/**
 * Chapter
 */
class Scout::Chapter : public Document { };


/**
 * Spacer
 *
 * A spacer is a place holder that consumes some screen space. It is used for
 * tweaking the layout of the document.
 */
struct Scout::Spacer : Element
{
	Spacer(int w, int h) { _min_size = _size = Area(w, h); }
};


/**
 * Verbatim text block
 *
 * A verbatim text block consists of a number of preformatted text lines.
 * The text is printed in a monospaced font and the whole verbatim area
 * has a shaded background.
 */
class Scout::Verbatim : public Parent_element
{
	public:

		Color bgcol;

		/**
		 * Constructor
		 */
		explicit Verbatim(Color bg) : bgcol(bg) { }

		/**
		 * Append verbatim text line
		 */
		void append_textline(const char *str, Style *style)
		{
			append(new Token(style, str, strlen(str)));
		}

		/**
		 * Element interface
		 */
		void draw(Canvas_base &, Point) override;
		void format_fixed_width(int) override;
};


/**
 * An iten consists of a item tag and a list of blocks
 */
class Scout::Item : public Parent_element
{
	private:

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

	public:

		Style      *_style;
		const char *_tag;
		int         _tag_ident;

		/**
		 * Constructor
		 */
		Item(Style *style, const char *str, int ident)
		: _style(style), _tag(str), _tag_ident(ident) { }

		/**
		 * Element interface
		 */
		void format_fixed_width(int w) override
		{
			_min_size = Area(w, _format_children(_tag_ident, w - _tag_ident));
		}

		void draw(Canvas_base &canvas, Point abs_position) override
		{
			canvas.draw_string(_position.x() + abs_position.x(),
			                   _position.y() + abs_position.y(),
			                   _style->font, _style->color, _tag, 255);
			Parent_element::draw(canvas, abs_position);
		}
};


/**
 * Document navigation bar
 */
class Scout::Navbar : public Parent_element, public Fader
{
	private:

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

		Block *_next_title = nullptr;
		Block *_prev_title = nullptr;

		Anchor *_next_anchor = nullptr;
		Anchor *_prev_anchor = nullptr;

	public:

		/**
		 * These pointers must be initialized such that they
		 * point to valid Icon widgets that are used as graphics
		 * for the navigation bar.
		 */
		static Generic_icon *next_icon;
		static Generic_icon *prev_icon;
		static Generic_icon *nbox_icon;
		static Generic_icon *pbox_icon;

		/**
		 * Constructor
		 */
		Navbar();

		/**
		 * Define link to next and previous chapter
		 */
		void next_link(const char *title, Anchor *dst);
		void prev_link(const char *title, Anchor *dst);

		/**
		 * Element interface
		 */
		void format_fixed_width(int)    override;
		void draw(Canvas_base &, Point) override;
		Element *find(Point)            override;

		/**
		 * Tick interface
		 */
		int on_tick() override;
};

#endif /* _ELEMENTS_H_ */
