/*
 * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
 * Copyright(C) 2019 the Claws Mail Team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write tothe Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#include "claws-features.h"
#endif

#include "container_linux.h"
#include "container_linux_images.h"

#include <cmath>
#include "lh_prefs.h"
#include "utils.h"

#ifndef M_PI
#       define M_PI    3.14159265358979323846
#endif

container_linux::container_linux()
{
	m_temp_surface	= cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2);
	m_temp_cr		= cairo_create(m_temp_surface);
	g_rec_mutex_init(&m_images_lock);
}

container_linux::~container_linux()
{
	clear_images();
	cairo_surface_destroy(m_temp_surface);
	cairo_destroy(m_temp_cr);
	g_rec_mutex_clear(&m_images_lock);
}

litehtml::uint_ptr container_linux::create_font( const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm )
{
    PangoFontDescription *desc = pango_font_description_from_string (faceName);
    pango_font_description_set_absolute_size(desc, size * PANGO_SCALE);
    if(italic == litehtml::font_style_italic)
    {
        pango_font_description_set_style(desc, PANGO_STYLE_ITALIC);
    } else
    {
        pango_font_description_set_style(desc, PANGO_STYLE_NORMAL);
    }
    PangoWeight fnt_weight;
    if(weight >= 0 && weight < 150)			fnt_weight = PANGO_WEIGHT_THIN;
    else if(weight >= 150 && weight < 250)	fnt_weight = PANGO_WEIGHT_ULTRALIGHT;
    else if(weight >= 250 && weight < 350)	fnt_weight = PANGO_WEIGHT_LIGHT;
    else if(weight >= 350 && weight < 450)	fnt_weight = PANGO_WEIGHT_NORMAL;
    else if(weight >= 450 && weight < 550)	fnt_weight = PANGO_WEIGHT_MEDIUM;
    else if(weight >= 550 && weight < 650)	fnt_weight = PANGO_WEIGHT_SEMIBOLD;
    else if(weight >= 650 && weight < 750)	fnt_weight = PANGO_WEIGHT_BOLD;
    else if(weight >= 750 && weight < 850)	fnt_weight = PANGO_WEIGHT_ULTRABOLD;
    else fnt_weight = PANGO_WEIGHT_HEAVY;

    pango_font_description_set_weight(desc, fnt_weight);

	cairo_font* ret = nullptr;

	if(fm)
	{
		cairo_save(m_temp_cr);
        PangoLayout *layout = pango_cairo_create_layout(m_temp_cr);
        PangoContext *context = pango_layout_get_context(layout);
        PangoLanguage *language = pango_language_get_default();
        pango_layout_set_font_description(layout, desc);
        PangoFontMetrics *metrics = pango_context_get_metrics(context, desc, language);

        fm->ascent = PANGO_PIXELS((double)pango_font_metrics_get_ascent(metrics));
        fm->descent = PANGO_PIXELS((double)pango_font_metrics_get_descent(metrics));
        fm->height = fm->ascent + fm->descent;
        fm->x_height = fm->height;

        pango_layout_set_text(layout, "x", 1);

        int x_width, x_height;
        pango_layout_get_pixel_size(layout, &x_width, &x_height);

		fm->x_height	= x_height;

		cairo_restore(m_temp_cr);

        g_object_unref(layout);
        pango_font_metrics_unref(metrics);

		ret = new cairo_font;
		ret->font		= desc;
		ret->size		= size;
		ret->strikeout 	= (decoration & litehtml::font_decoration_linethrough) != 0;
		ret->underline	= (decoration & litehtml::font_decoration_underline) != 0;
        ret->ascent     = fm->ascent;
        ret->descent    = fm->descent;

        ret->underline_thickness = pango_font_metrics_get_underline_thickness(metrics);
        ret->underline_position = -pango_font_metrics_get_underline_position(metrics);
        pango_quantize_line_geometry(&ret->underline_thickness, &ret->underline_position);
        ret->underline_thickness = PANGO_PIXELS(ret->underline_thickness);
        ret->underline_position = -1;//PANGO_PIXELS(ret->underline_position);

        ret->strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness(metrics);
        ret->strikethrough_position = pango_font_metrics_get_strikethrough_position(metrics);
        pango_quantize_line_geometry(&ret->strikethrough_thickness, &ret->strikethrough_position);
        ret->strikethrough_thickness = PANGO_PIXELS(ret->strikethrough_thickness);
        ret->strikethrough_position = PANGO_PIXELS(ret->strikethrough_position);
	}

	return (litehtml::uint_ptr) ret;
}

void container_linux::delete_font( litehtml::uint_ptr hFont )
{
	auto* fnt = (cairo_font*) hFont;
	if(fnt)
	{
        pango_font_description_free(fnt->font);
		delete fnt;
	}
}

int container_linux::text_width( const char* text, litehtml::uint_ptr hFont )
{
	auto* fnt = (cairo_font*) hFont;

	cairo_save(m_temp_cr);

    PangoLayout *layout = pango_cairo_create_layout(m_temp_cr);
    pango_layout_set_font_description(layout, fnt->font);

    pango_layout_set_text(layout, text, -1);
    pango_cairo_update_layout (m_temp_cr, layout);

    int x_width, x_height;
    pango_layout_get_pixel_size(layout, &x_width, &x_height);

	cairo_restore(m_temp_cr);

    g_object_unref(layout);

	return (int) x_width;
}

void container_linux::draw_text( litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos )
{
	auto* fnt = (cairo_font*) hFont;
	auto* cr = (cairo_t*) hdc;
	cairo_save(cr);

	apply_clip(cr);

	set_color(cr, color);

    PangoLayout *layout = pango_cairo_create_layout(cr);
    pango_layout_set_font_description (layout, fnt->font);
    pango_layout_set_text (layout, text, -1);

    int baseline = PANGO_PIXELS(pango_layout_get_baseline(layout));

    PangoRectangle ink_rect, logical_rect;
    pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect);

    int text_baseline = pos.height - fnt->descent;

    int x = pos.left() + logical_rect.x;
    int y = pos.top() + logical_rect.y + text_baseline - baseline;

	cairo_move_to(cr, x, y);
    pango_cairo_update_layout (cr, layout);
    pango_cairo_show_layout (cr, layout);

	int tw = 0;

	if(fnt->underline || fnt->strikeout)
	{
		tw = text_width(text, hFont);
	}

	if(fnt->underline)
	{
		cairo_set_line_width(cr, fnt->underline_thickness);
		cairo_move_to(cr, x, pos.top() + text_baseline - fnt->underline_position + 0.5);
		cairo_line_to(cr, x + tw, pos.top() + text_baseline - fnt->underline_position + 0.5);
		cairo_stroke(cr);
	}
	if(fnt->strikeout)
	{
		cairo_set_line_width(cr, fnt->strikethrough_thickness);
		cairo_move_to(cr, x, pos.top() + text_baseline - fnt->strikethrough_position - 0.5);
		cairo_line_to(cr, x + tw, pos.top() + text_baseline - fnt->strikethrough_position - 0.5);
		cairo_stroke(cr);
	}

	cairo_restore(cr);

    g_object_unref(layout);
}

int container_linux::pt_to_px( int pt ) const
{
	GdkScreen* screen = gdk_screen_get_default();
	double dpi = gdk_screen_get_resolution(screen);

	return (int) ((double) pt * dpi / 72.0);
}

/*
int container_linux::get_default_font_size() const
{
	return pt_to_px(12);
}
*/

void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker )
{
	if(!marker.image.empty())
	{
		/*litehtml::string url;
		make_url(marker.image.c_str(), marker.baseurl, url);

		lock_images_cache();
		images_map::iterator img_i = m_images.find(url.c_str());
		if(img_i != m_images.end())
		{
			if(img_i->second)
			{
				draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
			}
		}
		unlock_images_cache();*/
	} else
	{
		switch(marker.marker_type)
		{
		case litehtml::list_style_type_circle:
			{
				draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 1);
			}
			break;
		case litehtml::list_style_type_disc:
			{
				fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color);
			}
			break;
		case litehtml::list_style_type_square:
			if(hdc)
			{
				auto* cr = (cairo_t*) hdc;
				cairo_save(cr);

				cairo_new_path(cr);
				cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);

				set_color(cr, marker.color);
				cairo_fill(cr);
				cairo_restore(cr);
			}
			break;
		default:
			/*do nothing*/
			break;
		}
	}
}

void container_linux::load_image( const char* src, const char* baseurl, bool redraw_on_ready )
{
	litehtml::string url;
	make_url(src, baseurl, url);
	bool request = false;
	struct timeval last;

	gettimeofday(&last, NULL);

	lock_images_cache();

	auto i = m_images.find(url);
	if(i == m_images.end()) {
		/* Attached images can be loaded into cache right here. */
		if (!strncmp(src, "cid:", 4)) {
			GdkPixbuf *pixbuf = get_local_image(src);

			if (pixbuf != NULL)
				m_images.insert(std::make_pair(src, std::make_pair(pixbuf, last)));

			unlock_images_cache();
			return;
		} else {
			if (!lh_prefs_get()->enable_remote_content) {
				debug_print("blocking download of image from '%s'\n", src);
				unlock_images_cache();
				return;
			}

			request = true;
			m_images.insert(std::make_pair(url, std::make_pair((GdkPixbuf *)NULL, last)));
		}
	} else {
		debug_print("found image cache entry: %p '%s'\n", i->second.first, url.c_str());
		i->second.second = last;
	}

	unlock_images_cache();

	if (request) {
		struct FetchCtx *ctx;

		debug_print("allowing download of image from '%s'\n", src);

		ctx = g_new(struct FetchCtx, 1);
		ctx->url = g_strdup(url.c_str());
		ctx->container = this;

		GTask *task = g_task_new(NULL, NULL, get_image_callback, ctx);
		g_task_set_task_data(task, ctx, NULL);
		g_task_run_in_thread(task, get_image_threaded);
	}
}

void container_linux::get_image_size( const char* src, const char* baseurl, litehtml::size& sz )
{
	litehtml::string url;
	make_url(src, baseurl, url);

	lock_images_cache();

	auto img = m_images.find(url);
	if(img != m_images.end())
	{
		if(img->second.first)
		{
			sz.width = gdk_pixbuf_get_width(img->second.first);
			sz.height = gdk_pixbuf_get_height(img->second.first);
		} else
		{
			sz.width    = 0;
			sz.height   = 0;
		}
	} else
	{
		sz.width        = 0;
		sz.height       = 0;
	}

	unlock_images_cache();
}

void container_linux::draw_background( litehtml::uint_ptr hdc, const std::vector<litehtml::background_paint>& bgvec )
{
	auto* cr = (cairo_t*) hdc;
	cairo_save(cr);
	apply_clip(cr);

	const auto& bg = bgvec.back();

	rounded_rectangle(cr, bg.border_box, bg.border_radius);
	cairo_clip(cr);

	cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
	cairo_clip(cr);

	if(bg.color.alpha)
	{
		set_color(cr, bg.color);
		cairo_paint(cr);
	}

	for (int i = (int)bgvec.size() - 1; i >= 0; i--)
	{
		const auto& bg = bgvec[i];

		if(bg.image_size.height == 0 || bg.image_size.width == 0) continue;

		cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
		cairo_clip(cr);

		std::string url;
		make_url(bg.image.c_str(), bg.baseurl.c_str(), url);

		lock_images_cache();
		auto img_i = m_images.find(url);
		if(img_i != m_images.end() && img_i->second.first)
		{
			GdkPixbuf *bgbmp = img_i->second.first;

			GdkPixbuf *new_img = NULL;
			if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
			{
				new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
				bgbmp = new_img;
			}

			cairo_surface_t* img = surface_from_pixbuf(bgbmp);
			cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
			cairo_matrix_t flib_m;
			cairo_matrix_init_identity(&flib_m);
			cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
			cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
			cairo_pattern_set_matrix (pattern, &flib_m);

			switch(bg.repeat)
			{
			case litehtml::background_repeat_no_repeat:
				draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
				break;

			case litehtml::background_repeat_repeat_x:
				cairo_set_source(cr, pattern);
				cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
				cairo_fill(cr);
				break;

			case litehtml::background_repeat_repeat_y:
				cairo_set_source(cr, pattern);
				cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
				cairo_fill(cr);
				break;

			case litehtml::background_repeat_repeat:
				cairo_set_source(cr, pattern);
				cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
				cairo_fill(cr);
				break;
			}

			cairo_pattern_destroy(pattern);
			cairo_surface_destroy(img);
			if(new_img)
			{
				g_object_unref(new_img);
			}
		}
		unlock_images_cache();
	}

	cairo_restore(cr);
}

void container_linux::make_url(const char* url,	const char* basepath, litehtml::string& out)
{
	out = url;
}

void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
{
	if(rx > 0 && ry > 0)
	{

		cairo_save(cr);

		cairo_translate(cr, x, y);
		cairo_scale(cr, 1, ry / rx);
		cairo_translate(cr, -x, -y);

		if(neg)
		{
			cairo_arc_negative(cr, x, y, rx, a1, a2);
		} else
		{
			cairo_arc(cr, x, y, rx, a1, a2);
		}

		cairo_restore(cr);
	} else
	{
		cairo_move_to(cr, x, y);
	}
}

void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
{
	auto* cr = (cairo_t*) hdc;
	cairo_save(cr);
	apply_clip(cr);

	cairo_new_path(cr);

	int bdr_top		= 0;
	int bdr_bottom	= 0;
	int bdr_left	= 0;
	int bdr_right	= 0;

	if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
	{
		bdr_top = (int) borders.top.width;
	}
	if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
	{
		bdr_bottom = (int) borders.bottom.width;
	}
	if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
	{
		bdr_left = (int) borders.left.width;
	}
	if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
	{
		bdr_right = (int) borders.right.width;
	}

	// draw right border
	if(bdr_right)
	{
		set_color(cr, borders.right.color);

		if(borders.radius.top_right_x && borders.radius.top_right_y)
		{
			double end_angle	= 2 * M_PI;
			double start_angle	= end_angle - M_PI / 2.0  / ((double) bdr_top / (double) bdr_right + 1);

			add_path_arc(cr,
				         draw_pos.right() - borders.radius.top_right_x,
						 draw_pos.top() + borders.radius.top_right_y,
						 borders.radius.top_right_x - bdr_right,
						 borders.radius.top_right_y - bdr_right + (bdr_right - bdr_top),
						 end_angle,
						 start_angle, true);

			add_path_arc(cr,
						 draw_pos.right() - borders.radius.top_right_x,
						 draw_pos.top() + borders.radius.top_right_y,
						 borders.radius.top_right_x,
						 borders.radius.top_right_y,
						 start_angle,
						 end_angle, false);
		} else
		{
			cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
			cairo_line_to(cr, draw_pos.right(), draw_pos.top());
		}

		if(borders.radius.bottom_right_x && borders.radius.bottom_right_y)
		{
			cairo_line_to(cr, draw_pos.right(),	draw_pos.bottom() - borders.radius.bottom_right_y);

			double start_angle	= 0;
			double end_angle	= start_angle + M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_right + 1);

			add_path_arc(cr,
						 draw_pos.right() - borders.radius.bottom_right_x,
						 draw_pos.bottom() - borders.radius.bottom_right_y,
						 borders.radius.bottom_right_x,
						 borders.radius.bottom_right_y,
						 start_angle,
						 end_angle, false);

			add_path_arc(cr,
						 draw_pos.right() - borders.radius.bottom_right_x,
						 draw_pos.bottom() - borders.radius.bottom_right_y,
						 borders.radius.bottom_right_x - bdr_right,
						 borders.radius.bottom_right_y - bdr_right + (bdr_right - bdr_bottom),
						 end_angle,
						 start_angle, true);
		} else
		{
			cairo_line_to(cr, draw_pos.right(),	draw_pos.bottom());
			cairo_line_to(cr, draw_pos.right() - bdr_right,	draw_pos.bottom() - bdr_bottom);
		}

		cairo_fill(cr);
	}

	// draw bottom border
	if(bdr_bottom)
	{
		set_color(cr, borders.bottom.color);

		if(borders.radius.bottom_left_x && borders.radius.bottom_left_y)
		{
			double start_angle	= M_PI / 2.0;
			double end_angle	= start_angle + M_PI / 2.0  / ((double) bdr_left / (double) bdr_bottom + 1);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.bottom_left_x,
						 draw_pos.bottom() - borders.radius.bottom_left_y,
						 borders.radius.bottom_left_x - bdr_bottom + (bdr_bottom - bdr_left),
						 borders.radius.bottom_left_y - bdr_bottom,
						 start_angle,
						 end_angle, false);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.bottom_left_x,
						 draw_pos.bottom() - borders.radius.bottom_left_y,
						 borders.radius.bottom_left_x,
						 borders.radius.bottom_left_y,
						 end_angle,
						 start_angle, true);
		} else
		{
			cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
			cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
		}

		if(borders.radius.bottom_right_x && borders.radius.bottom_right_y)
		{
			cairo_line_to(cr, draw_pos.right() - borders.radius.bottom_right_x,	draw_pos.bottom());

			double end_angle	= M_PI / 2.0;
			double start_angle	= end_angle - M_PI / 2.0  / ((double) bdr_right / (double) bdr_bottom + 1);

			add_path_arc(cr,
						 draw_pos.right() - borders.radius.bottom_right_x,
						 draw_pos.bottom() - borders.radius.bottom_right_y,
						 borders.radius.bottom_right_x,
						 borders.radius.bottom_right_y,
						 end_angle,
						 start_angle, true);

			add_path_arc(cr,
						 draw_pos.right() - borders.radius.bottom_right_x,
						 draw_pos.bottom() - borders.radius.bottom_right_y,
						 borders.radius.bottom_right_x - bdr_bottom + (bdr_bottom - bdr_right),
						 borders.radius.bottom_right_y - bdr_bottom,
						 start_angle,
						 end_angle, false);
		} else
		{
			cairo_line_to(cr, draw_pos.right() - bdr_right,	draw_pos.bottom() - bdr_bottom);
			cairo_line_to(cr, draw_pos.right(),	draw_pos.bottom());
		}

		cairo_fill(cr);
	}

	// draw top border
	if(bdr_top)
	{
		set_color(cr, borders.top.color);

		if(borders.radius.top_left_x && borders.radius.top_left_y)
		{
			double end_angle	= M_PI * 3.0 / 2.0;
			double start_angle	= end_angle - M_PI / 2.0  / ((double) bdr_left / (double) bdr_top + 1);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.top_left_x,
						 draw_pos.top() + borders.radius.top_left_y,
						 borders.radius.top_left_x,
						 borders.radius.top_left_y,
						 end_angle,
						 start_angle, true);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.top_left_x,
						 draw_pos.top() + borders.radius.top_left_y,
						 borders.radius.top_left_x - bdr_top + (bdr_top - bdr_left),
						 borders.radius.top_left_y - bdr_top,
						 start_angle,
						 end_angle, false);
		} else
		{
			cairo_move_to(cr, draw_pos.left(), draw_pos.top());
			cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
		}

		if(borders.radius.top_right_x && borders.radius.top_right_y)
		{
			cairo_line_to(cr, draw_pos.right() - borders.radius.top_right_x,	draw_pos.top() + bdr_top);

			double start_angle	= M_PI * 3.0 / 2.0;
			double end_angle	= start_angle + M_PI / 2.0  / ((double) bdr_right / (double) bdr_top + 1);

			add_path_arc(cr,
						 draw_pos.right() - borders.radius.top_right_x,
						 draw_pos.top() + borders.radius.top_right_y,
						 borders.radius.top_right_x - bdr_top + (bdr_top - bdr_right),
						 borders.radius.top_right_y - bdr_top,
						 start_angle,
						 end_angle, false);

			add_path_arc(cr,
						 draw_pos.right() - borders.radius.top_right_x,
						 draw_pos.top() + borders.radius.top_right_y,
						 borders.radius.top_right_x,
						 borders.radius.top_right_y,
						 end_angle,
						 start_angle, true);
		} else
		{
			cairo_line_to(cr, draw_pos.right() - bdr_right,	draw_pos.top() + bdr_top);
			cairo_line_to(cr, draw_pos.right(),	draw_pos.top());
		}

		cairo_fill(cr);
	}

	// draw left border
	if(bdr_left)
	{
		set_color(cr, borders.left.color);

		if(borders.radius.top_left_x && borders.radius.top_left_y)
		{
			double start_angle	= M_PI;
			double end_angle	= start_angle + M_PI / 2.0  / ((double) bdr_top / (double) bdr_left + 1);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.top_left_x,
						 draw_pos.top() + borders.radius.top_left_y,
						 borders.radius.top_left_x - bdr_left,
						 borders.radius.top_left_y - bdr_left + (bdr_left - bdr_top),
						 start_angle,
						 end_angle, false);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.top_left_x,
						 draw_pos.top() + borders.radius.top_left_y,
						 borders.radius.top_left_x,
						 borders.radius.top_left_y,
						 end_angle,
						 start_angle, true);
		} else
		{
			cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
			cairo_line_to(cr, draw_pos.left(), draw_pos.top());
		}

		if(borders.radius.bottom_left_x && borders.radius.bottom_left_y)
		{
			cairo_line_to(cr, draw_pos.left(),	draw_pos.bottom() - borders.radius.bottom_left_y);

			double end_angle	= M_PI;
			double start_angle	= end_angle - M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_left + 1);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.bottom_left_x,
						 draw_pos.bottom() - borders.radius.bottom_left_y,
						 borders.radius.bottom_left_x,
						 borders.radius.bottom_left_y,
						 end_angle,
						 start_angle, true);

			add_path_arc(cr,
						 draw_pos.left() + borders.radius.bottom_left_x,
						 draw_pos.bottom() - borders.radius.bottom_left_y,
						 borders.radius.bottom_left_x - bdr_left,
						 borders.radius.bottom_left_y - bdr_left + (bdr_left - bdr_bottom),
						 start_angle,
						 end_angle, false);
		} else
		{
			cairo_line_to(cr, draw_pos.left(),	draw_pos.bottom());
			cairo_line_to(cr, draw_pos.left() + bdr_left,	draw_pos.bottom() - bdr_bottom);
		}

		cairo_fill(cr);
	}
	cairo_restore(cr);
}

void container_linux::transform_text(litehtml::string& text, litehtml::text_transform tt)
{

}

void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius )
{
	m_clips.emplace_back(pos, bdr_radius);
}

void container_linux::del_clip()
{
	if(!m_clips.empty())
	{
		m_clips.pop_back();
	}
}

void container_linux::apply_clip( cairo_t* cr )
{
	for(const auto& clip_box : m_clips)
	{
		rounded_rectangle(cr, clip_box.box, clip_box.radius);
		cairo_clip(cr);
	}
}

void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
{
	if(!cr || !width || !height) return;
	cairo_save(cr);

	apply_clip(cr);

	cairo_new_path(cr);

	cairo_translate (cr, x + width / 2.0, y + height / 2.0);
	cairo_scale (cr, width / 2.0, height / 2.0);
	cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);

	set_color(cr, color);
	cairo_set_line_width(cr, line_width);
	cairo_stroke(cr);

	cairo_restore(cr);
}

void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
{
	if(!cr || !width || !height) return;
	cairo_save(cr);

	apply_clip(cr);

	cairo_new_path(cr);

	cairo_translate (cr, x + width / 2.0, y + height / 2.0);
	cairo_scale (cr, width / 2.0, height / 2.0);
	cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);

	set_color(cr, color);
	cairo_fill(cr);

	cairo_restore(cr);
}

void container_linux::clear_images()
{
	lock_images_cache();

	for(auto i = m_images.begin(); i != m_images.end(); ++i) {
		if (i->second.first) {
			g_object_unref(i->second.first);
		}
	}

	m_images.clear();

	unlock_images_cache();
}

gint container_linux::clear_images(gsize desired_size)
{
	gsize size = 0;
	gint num = 0;

	lock_images_cache();

	/* First, remove all local images - the ones with "cid:" URL. */
	for (auto i = m_images.begin(); i != m_images.end(); ) {
		if (!strncmp(i->first.c_str(), "cid:", 4)) {
			g_object_unref(i->second.first);
			i = m_images.erase(i);
			num++;
		} else {
			++i;
		}
	}

	/* Second, build an LRU list */
	auto lru_comp_func = [](const lru_entry& l1, const lru_entry& l2) {
		return timercmp(&l1.second, &l2.second, <);
	};
	std::set<lru_entry, decltype(lru_comp_func)> lru(lru_comp_func);

	for (auto i = m_images.begin(); i != m_images.end(); ++i) {
		lru.insert(std::make_pair(i->first, i->second.second));
	}

	/*
	for (auto l = lru.begin(); l != lru.end(); l++) {
		debug_print("lru dump: %d %d %s\n", l->second.tv_sec, l->second.tv_usec, l->first.c_str());
	}
	*/

	/* Last, walk the LRU list and remove the oldest entries that push it over
	 * the desired size limit */
	for (auto l = lru.rbegin(); l != lru.rend(); ++l) {
		gsize cursize;

		auto i = m_images.find(l->first);

		if(i == m_images.end()) {
			g_warning("failed to find '%s' in m_images", l->first.c_str());
			continue;
		}

		if(i->second.first == NULL) {
			/* This should mean that the download is still in progress */
			debug_print("warning - trying to prune a null pixbuf for %s\n", i->first.c_str());
			continue;
		}

		cursize = gdk_pixbuf_get_byte_length(i->second.first);
		/*
		debug_print("clear_images: desired_size %d - size %d - cursize %d - %d %d %s\n",
			desired_size, size, cursize, l->second.tv_sec, l->second.tv_usec, l->first.c_str());
		*/
		if (size + cursize > desired_size) {
			debug_print("pruning %s from image cache\n", i->first.c_str());
			g_object_unref(i->second.first);
			m_images.erase(i);
			num++;
		} else {
			size += cursize;
		}
	}

	unlock_images_cache();

	return num;
}

/*
const char* container_linux::get_default_font_name() const
{
	return "Times New Roman";
}
*/

std::shared_ptr<litehtml::element>	container_linux::create_element(const char *tag_name,
																	  const litehtml::string_map &attributes,
																	  const std::shared_ptr<litehtml::document> &doc)
{
	return nullptr;
}

void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
{
	cairo_new_path(cr);
	if(radius.top_left_x && radius.top_left_y)
	{
		add_path_arc(cr,
			 pos.left() + radius.top_left_x,
			 pos.top() + radius.top_left_y,
			 radius.top_left_x,
			 radius.top_left_y,
			 M_PI,
			 M_PI * 3.0 / 2.0, false);
	} else
	{
		cairo_move_to(cr, pos.left(), pos.top());
	}

	cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());

	if(radius.top_right_x && radius.top_right_y)
	{
		add_path_arc(cr,
			 pos.right() - radius.top_right_x,
			 pos.top() + radius.top_right_y,
			 radius.top_right_x,
			 radius.top_right_y,
			 M_PI * 3.0 / 2.0,
			 2.0 * M_PI, false);
	}

	cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);

	if(radius.bottom_right_x && radius.bottom_right_y)
	{
		add_path_arc(cr,
			 pos.right() - radius.bottom_right_x,
			 pos.bottom() - radius.bottom_right_y,
			 radius.bottom_right_x,
			 radius.bottom_right_y,
			 0,
			 M_PI / 2.0, false);
	}

	cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());

	if(radius.bottom_left_x && radius.bottom_left_y)
	{
		add_path_arc(cr,
			 pos.left() + radius.bottom_left_x,
			 pos.bottom() - radius.bottom_left_y,
			 radius.bottom_left_x,
			 radius.bottom_left_y,
			 M_PI / 2.0,
			 M_PI, false);
	}
}

void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x,	int y, int cx, int cy)
{
	cairo_save(cr);

	{
		cairo_matrix_t flib_m;
		cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);

		if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
		{
			GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
			gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
			cairo_paint(cr);
		} else
		{
			gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
			cairo_paint(cr);
		}
	}

	cairo_restore(cr);
}

cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
{
	cairo_surface_t* ret;

	if(gdk_pixbuf_get_has_alpha(bmp))
	{
		ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
	} else
	{
		ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
	}

//	Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
//	Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
//	Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
	cairo_t *ctx = cairo_create(ret);
	cairo_paint(ctx);
	cairo_destroy(ctx);

	return ret;
}

void container_linux::get_media_features(litehtml::media_features& media) const
{
	litehtml::position client;
    get_client_rect(client);
	media.type			= litehtml::media_type_screen;
	media.width			= client.width;
	media.height		= client.height;
	media.device_width	= gdk_screen_width();
	media.device_height	= gdk_screen_height();
	media.color			= 8;
	media.monochrome	= 0;
	media.color_index	= 256;
	media.resolution	= 96;
}

void container_linux::get_language(litehtml::string& language, litehtml::string& culture) const
{
	language = "en";
	culture = "";
}

void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)
{

}
