/* test.c
 * This file is part of Amsam photo cropping program
 * Copyright 2021, 2023, 2024, 2025, 2026 Nandakumar Edamana
 * Distributed under GNU General Public License, version 3
 */ 

#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>

#include "test.h"


#include <pthread.h>

void thread_destruct(thread *this)
{
}

void thread_construct(thread *this)
{
}

pthread_mutex_t * new_pthread_mutex_t_must()
{
	pthread_mutex_t *mutexnbl = NULL;
	mutexnbl = malloc(sizeof(pthread_mutex_t));
	if (mutexnbl) {
		pthread_mutex_t *mutexnn;
		mutexnn = (pthread_mutex_t *) mutexnbl;
		return mutexnn;
	}

	perror(NULL);
	exit(EXIT_FAILURE);
}

void thread_mutex_construct(thread_mutex *this)
{
	this->mutex = new_pthread_mutex_t_must();

	pthread_mutex_init(this->mutex, NULL);
}

void thread_mutex_destruct(thread_mutex *this)
{
}

thread * thread_start(void * (*twfun)(void *usrdata), void *usrdata)
{
	pthread_t tid;

	int retval = 0;

	retval = pthread_create(&tid, NULL, twfun, usrdata);
	if (retval != 0) {
		return NULL;
	}

	thread *th = (thread *) malloc(sizeof(thread));
	if (th == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	thread_construct(th);
	th->tid = tid;

	return th;
}

thread * thread_start_or_serial(void * (*twfun)(void *usrdata), void *usrdata)
{
	thread *th = NULL;

	th = thread_start(twfun, usrdata);

	if (NULL == th) {
		twfun(usrdata);
	}

	return th;
}

void thread_join(thread *thandle)
{
	if (NULL != thandle) {
		pthread_join(thandle->tid, NULL);
	}
}

void thread_mutex_lock(thread_mutex *thandle)
{
	pthread_mutex_lock(thandle->mutex);
}

void thread_mutex_trylock(thread_mutex *thandle)
{
	pthread_mutex_trylock(thandle->mutex);
}

void thread_mutex_unlock(thread_mutex *thandle)
{
	pthread_mutex_unlock(thandle->mutex);
}

int thread_cancel(thread *thandle)
{
	return pthread_cancel(thandle->tid);
}

void procedural_gui_testing_context_construct(ProceduralGuiTestingContext *this, myapp *app, ProceduralContext *pctx)
{
	this->app = app;
	this->pctx = pctx;
}

void procedural_gui_testing_context_destruct(ProceduralGuiTestingContext *this)
{
}

void animate_procedural_context(ProceduralGuiTestingContext *tctx, int interval)
{
}

_Bool on_gui_testing_context_tick(ProceduralGuiTestingContext *tctx)
{
	_Bool changed = procedural_context_execute_next(tctx->pctx);

	if (changed) {
		myapp_reflect_brush_status(tctx->app);
		myapp_drawframe_noskip(tctx->app);
	}

	return (_Bool) changed;
}

void exmcmx_args_construct(ExmcmxArgs *this, void *fnc, GuiTestingContext *tctx)
{
	this->fnc = fnc;
	this->tctx = tctx;
}

void exmcmx_args_destruct(ExmcmxArgs *this)
{
}

_Bool mchelper_exmcmx(ExmcmxArgs *args)
{
	g_main_context_invoke(NULL, args->fnc, args->tctx->app);

	thread_mutex_unlock(args->tctx->mx);
	return (_Bool) G_SOURCE_REMOVE;
}

void gui_testing_context_exmcmx(GuiTestingContext *this, void *fnc)
{
	ExmcmxArgs *args = (ExmcmxArgs *) malloc(sizeof(ExmcmxArgs));
	if (args == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	exmcmx_args_construct(args, fnc, this);
	thread_mutex_lock(this->mx);
	g_main_context_invoke(NULL, (void *) mchelper_exmcmx, args);
	thread_mutex_lock(this->mx);
	thread_mutex_unlock(this->mx);
	if (args) {
		exmcmx_args_destruct(args);
		free(args);
	}
}

void gui_testing_context_construct(GuiTestingContext *this, myapp *app)
{
	thread_mutex *_tmp_1 = (thread_mutex *) malloc(sizeof(thread_mutex));
	if (_tmp_1 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	thread_mutex_construct(_tmp_1);
	this->mx = _tmp_1;
	this->app = app;
}

void gui_testing_context_destruct(GuiTestingContext *this)
{
	if (this->mx) {
		thread_mutex_destruct(this->mx);
		free(this->mx);
	}
}

void uidelay()
{
	sleep(2u);
}

void uidelay_minimal()
{
	usleep(33u * 1000u);
}

int helper_select_next_prebrush(GuiTestingContext *tctx, int i)
{
	assert(i >= 0);

	switch (i) {
	case 0:
	{
		gui_testing_context_exmcmx(tctx, mctest_select_ballpoint_brush);
		break;
	}

	case 1:
	{
		gui_testing_context_exmcmx(tctx, mctest_select_charcoal_brush);
		break;
	}

	case 2:
	{
		gui_testing_context_exmcmx(tctx, mctest_select_cryaon_brush);
		break;
	}

	case 3:
	{
		gui_testing_context_exmcmx(tctx, mctest_select_pencil_brush);
		break;
	}

	case 4:
	{
		gui_testing_context_exmcmx(tctx, mctest_select_round_brush);
		break;
	}

	case 5:
	{
		gui_testing_context_exmcmx(tctx, mctest_select_round_pressure_size_brush);
		break;
	}

	case 6:
	{
		gui_testing_context_exmcmx(tctx, mctest_select_soft_round_brush);
		return -1;
		break;
	}
	}

	return i + 1;
}

_Bool mctest_increase_fill_size(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_layerops_0(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_layerops_10(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_layerops_20(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_layerops_30(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_ballpoint_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_brush_tool(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_charcoal_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_cryaon_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_floodfill_tool(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_pencil_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_round_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_randsquare_brush_type(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_round_pressure_size_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_select_soft_round_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

_Bool mctest_assert_round_brush(myapp *app)
{
	return (_Bool) G_SOURCE_REMOVE;
}

void test_stroke_context_construct(TestStrokeContext *this, myapp *app, int maxstp, int xoffset)
{
	thread_mutex *_tmp_1 = (thread_mutex *) malloc(sizeof(thread_mutex));
	if (_tmp_1 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	thread_mutex_construct(_tmp_1);
	this->mx = _tmp_1;
	this->stp = 0;
	this->app = app;
	this->maxstp = maxstp;
	this->xoffset = xoffset;
}

void test_stroke_context_destruct(TestStrokeContext *this)
{
	if (this->mx) {
		thread_mutex_destruct(this->mx);
		free(this->mx);
	}
}

_Bool mctest_draw(TestStrokeContext *ctx)
{
	return (_Bool) G_SOURCE_REMOVE;
}

void test_draw(myapp *app, int xoffset, _Bool animate)
{
	TestStrokeContext *ctx = (TestStrokeContext *) malloc(sizeof(TestStrokeContext));
	if (ctx == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	test_stroke_context_construct(ctx, app, 20, xoffset);

	for (int i = 0; i <= ctx->maxstp; i += 1) {
		thread_mutex_lock(ctx->mx);
		g_main_context_invoke(NULL, (void *) mctest_draw, ctx);

		if (animate) {
			uidelay_minimal();
		}
	}

	thread_mutex_lock(ctx->mx);
	if (ctx) {
		test_stroke_context_destruct(ctx);
		free(ctx);
	}
}

void mchelper_draw_dot_perc(myapp *app, double x, double y, double pressure)
{
	mchelper_draw_dot_perc_nongui(app->proxydoc->prevdoc, x, y, pressure);
}

void mchelper_draw_line_perc(myapp *app, double x1, double y1, double p1, double x2, double y2, double p2)
{
	mchelper_draw_line_perc_nongui(app->proxydoc->prevdoc, x1, y1, p1, x2, y2, p2);
}

void mchelper_test_active_layer_name(const char *expected, myapp *app)
{
}

void mchelper_test_bg_fg_mode(_Bool expected, myapp *app)
{
}

void mchelper_set_fill_size_perc(myapp *app, double siz)
{
	mchelper_set_fill_size_perc_nongui(app->proxydoc->prevdoc, siz);
}

void mchelper_set_stroke_color(myapp *app, float r, float g, float b)
{
	mchelper_set_stroke_color_nongui(app->proxydoc->prevdoc, r, g, b);
}

void mctest_draw_sunset(ProceduralGuiTestingContext *tctx)
{
	double suny = 0.41;
	double sunsiz = 0.1;

	SelectBrushTool *_tmp_1 = (SelectBrushTool *) malloc(sizeof(SelectBrushTool));
	if (_tmp_1 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	select_brush_tool_construct(_tmp_1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_1);

	DrawVgradient *_tmp_2 = (DrawVgradient *) malloc(sizeof(DrawVgradient));
	if (_tmp_2 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_vgradient_construct(_tmp_2, 0, 1, 0.1, 1, 0.2, 0, 1, false);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_2);

	SelectSoftRoundBrushPreset *_tmp_3 = (SelectSoftRoundBrushPreset *) malloc(sizeof(SelectSoftRoundBrushPreset));
	if (_tmp_3 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	select_soft_round_brush_preset_construct(_tmp_3);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_3);

	SetBrushPressureForSize *_tmp_4 = (SetBrushPressureForSize *) malloc(sizeof(SetBrushPressureForSize));
	if (_tmp_4 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_brush_pressure_for_size_construct(_tmp_4, true);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_4);
	SetFillSizePerc *_tmp_5 = (SetFillSizePerc *) malloc(sizeof(SetFillSizePerc));
	if (_tmp_5 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_fill_size_perc_construct(_tmp_5, 0.2);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_5);
	SetStrokeColorSrgb *_tmp_6 = (SetStrokeColorSrgb *) malloc(sizeof(SetStrokeColorSrgb));
	if (_tmp_6 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_stroke_color_srgb_construct(_tmp_6, 0.3, 0, 0);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_6);
	DrawLinePerc *_tmp_7 = (DrawLinePerc *) malloc(sizeof(DrawLinePerc));
	if (_tmp_7 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_line_perc_construct(_tmp_7, 0, suny, 1, 0.45, suny, 0);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_7);
	DrawLinePerc *_tmp_8 = (DrawLinePerc *) malloc(sizeof(DrawLinePerc));
	if (_tmp_8 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_line_perc_construct(_tmp_8, 0.55, suny, 0, 1, suny, 1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_8);

	SetFillSizePerc *_tmp_9 = (SetFillSizePerc *) malloc(sizeof(SetFillSizePerc));
	if (_tmp_9 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_fill_size_perc_construct(_tmp_9, 0.1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_9);
	SetStrokeColorSrgb *_tmp_10 = (SetStrokeColorSrgb *) malloc(sizeof(SetStrokeColorSrgb));
	if (_tmp_10 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_stroke_color_srgb_construct(_tmp_10, 0.1, 0, 0);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_10);
	DrawLinePerc *_tmp_11 = (DrawLinePerc *) malloc(sizeof(DrawLinePerc));
	if (_tmp_11 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_line_perc_construct(_tmp_11, 0, suny, 1, 0.45, suny, 0);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_11);
	DrawLinePerc *_tmp_12 = (DrawLinePerc *) malloc(sizeof(DrawLinePerc));
	if (_tmp_12 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_line_perc_construct(_tmp_12, 0.55, suny, 0, 1, suny, 1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_12);
	SetFillSizePerc *_tmp_13 = (SetFillSizePerc *) malloc(sizeof(SetFillSizePerc));
	if (_tmp_13 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_fill_size_perc_construct(_tmp_13, 0.5);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_13);
	SetStrokeColorSrgb *_tmp_14 = (SetStrokeColorSrgb *) malloc(sizeof(SetStrokeColorSrgb));
	if (_tmp_14 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_stroke_color_srgb_construct(_tmp_14, 1, 1, 0);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_14);
	DrawDotPerc *_tmp_15 = (DrawDotPerc *) malloc(sizeof(DrawDotPerc));
	if (_tmp_15 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_dot_perc_construct(_tmp_15, 0.5, 0.4, 1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_15);

	SetFillSizePerc *_tmp_16 = (SetFillSizePerc *) malloc(sizeof(SetFillSizePerc));
	if (_tmp_16 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_fill_size_perc_construct(_tmp_16, 0.3);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_16);
	DrawDotPerc *_tmp_17 = (DrawDotPerc *) malloc(sizeof(DrawDotPerc));
	if (_tmp_17 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_dot_perc_construct(_tmp_17, 0.5, 0.4, 1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_17);

	SelectRoundBrushPreset *_tmp_18 = (SelectRoundBrushPreset *) malloc(sizeof(SelectRoundBrushPreset));
	if (_tmp_18 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	select_round_brush_preset_construct(_tmp_18);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_18);
	SetFillSizePerc *_tmp_19 = (SetFillSizePerc *) malloc(sizeof(SetFillSizePerc));
	if (_tmp_19 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_fill_size_perc_construct(_tmp_19, sunsiz);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_19);
	SetStrokeColorSrgb *_tmp_20 = (SetStrokeColorSrgb *) malloc(sizeof(SetStrokeColorSrgb));
	if (_tmp_20 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_stroke_color_srgb_construct(_tmp_20, 1, 0.8, 0);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_20);
	DrawDotPerc *_tmp_21 = (DrawDotPerc *) malloc(sizeof(DrawDotPerc));
	if (_tmp_21 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_dot_perc_construct(_tmp_21, 0.5, 0.4, 1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_21);
	SetBrushOpacity *_tmp_22 = (SetBrushOpacity *) malloc(sizeof(SetBrushOpacity));
	if (_tmp_22 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	set_brush_opacity_construct(_tmp_22, 1);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_22);

	DrawVgradient *_tmp_23 = (DrawVgradient *) malloc(sizeof(DrawVgradient));
	if (_tmp_23 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_vgradient_construct(_tmp_23, 0.45, 1, 0.05, 0.5, 0.1, 0, 1, false);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_23);

	DrawVgradient *_tmp_24 = (DrawVgradient *) malloc(sizeof(DrawVgradient));
	if (_tmp_24 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	draw_vgradient_construct(_tmp_24, 0.45, 1, 0.05, 0.4, 0.2, 0, 0.1, true);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_24);

	SelectPencilBrushPreset *_tmp_25 = (SelectPencilBrushPreset *) malloc(sizeof(SelectPencilBrushPreset));
	if (_tmp_25 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	select_pencil_brush_preset_construct(_tmp_25);
	procedural_context_queue(tctx->pctx, (Instruction *) _tmp_25);
	double sunrad = sunsiz / 2.0;
	for (int i = 0; i <= 60; i += 2) {
		double yfac = i / 60.0;

		double finaly = suny + yfac * (yfac * 0.6);

		double xshiftlr = (finaly - suny) / 20.0;
		int _tmp_1;
		if (drand48() <= 0.5) {
			_tmp_1 = 1;
		} else {
			_tmp_1 = -1;
		}

		double xshiftl = (drand48() * (yfac * 0.01)) * _tmp_1;
		int _tmp_2;
		if (drand48() <= 0.5) {
			_tmp_2 = 1;
		} else {
			_tmp_2 = -1;
		}

		double xshiftr = (drand48() * (yfac * 0.01)) * _tmp_2;

		SetStrokeColorSrgb *_tmp_26 = (SetStrokeColorSrgb *) malloc(sizeof(SetStrokeColorSrgb));
		if (_tmp_26 == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		set_stroke_color_srgb_construct(_tmp_26, 1, 0.8, 0);
		procedural_context_queue(tctx->pctx, (Instruction *) _tmp_26);
		SetBrushOpacity *_tmp_27 = (SetBrushOpacity *) malloc(sizeof(SetBrushOpacity));
		if (_tmp_27 == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		set_brush_opacity_construct(_tmp_27, 1 - yfac);
		procedural_context_queue(tctx->pctx, (Instruction *) _tmp_27);
		SetFillSizePerc *_tmp_28 = (SetFillSizePerc *) malloc(sizeof(SetFillSizePerc));
		if (_tmp_28 == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		set_fill_size_perc_construct(_tmp_28, 0.002);
		procedural_context_queue(tctx->pctx, (Instruction *) _tmp_28);
		DrawLinePerc *_tmp_29 = (DrawLinePerc *) malloc(sizeof(DrawLinePerc));
		if (_tmp_29 == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		draw_line_perc_construct(_tmp_29, ((0.5 - sunrad) - xshiftlr) - xshiftl, finaly, 1, 0.5 + sunrad + xshiftlr + xshiftr, finaly, 1);
		procedural_context_queue(tctx->pctx, (Instruction *) _tmp_29);
		SetBrushOpacity *_tmp_30 = (SetBrushOpacity *) malloc(sizeof(SetBrushOpacity));
		if (_tmp_30 == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		set_brush_opacity_construct(_tmp_30, 1);
		procedural_context_queue(tctx->pctx, (Instruction *) _tmp_30);

		SetStrokeColorSrgb *_tmp_31 = (SetStrokeColorSrgb *) malloc(sizeof(SetStrokeColorSrgb));
		if (_tmp_31 == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		set_stroke_color_srgb_construct(_tmp_31, 0, 0, 0);
		procedural_context_queue(tctx->pctx, (Instruction *) _tmp_31);
		double dxstp = 0.01 + yfac / 10;
		int ixstp = (int) (dxstp * 100);

		if (ixstp > 0) {
			int alter = 1;
			for (int x = 0; x < 100; x += ixstp) {
				double curx = x / 100.0 + alter * dxstp;
				double cury = finaly + yfac * (alter * 0.001);

				DrawLinePerc *_tmp_32 = (DrawLinePerc *) malloc(sizeof(DrawLinePerc));
				if (_tmp_32 == NULL) {
					perror(NULL);
					exit(EXIT_FAILURE);
				}

				draw_line_perc_construct(_tmp_32, curx, cury, 1, curx + dxstp, cury, 0.5);
				procedural_context_queue(tctx->pctx, (Instruction *) _tmp_32);

				alter *= -1;
			}
		}
	}

	animate_procedural_context(tctx, 33);
}

void mctest_draw_vara_logo(ImageDocument *doc, int iconsiz)
{
	mchelper_pick_brush_tool_nongui(doc);

	double padding = 0.05;
	double stroke_width = 0.1;
	double fill_size = 0.3;

	double pencilnecky = 0.8;
	double brush_gap = stroke_width * 1.2;
	double handle_size = fill_size * 0.5;

	image_document_clear_layer(doc);

	double bezel_margin_x = 0.1;
	double bezel_margin_y = 0.2;
	double bezel_offset_y = -0.1;
	double bezel_height = 0.6;

	double bezel_y_bottom = bezel_margin_y + bezel_height + bezel_offset_y;

	image_fill_region(image_document_get_fglayer_must(doc)->img, LINEAR_PIXEL_WHITE, rectangle_new_from_boundaries((int) (bezel_margin_x * iconsiz), (int) ((bezel_margin_y + bezel_offset_y) * iconsiz), (int) (iconsiz - bezel_margin_x * iconsiz), (int) (bezel_y_bottom * iconsiz)));

	doc->drawparams.brushparams = prebrush_round();
	mchelper_set_fill_size_perc_nongui(doc, stroke_width);
	doc->drawparams.brushparams.pressure_size = true;

	double drop_shadow_offset_y = 0.02;

	mchelper_set_stroke_color_nongui(doc, 0.4, 0.4, 0.4);
	mchelper_draw_line_perc_nongui(doc, bezel_margin_x, bezel_y_bottom + drop_shadow_offset_y, 1, 1 - bezel_margin_x, bezel_y_bottom + drop_shadow_offset_y, 1);

	mchelper_set_stroke_color_nongui(doc, 0.6, 0.6, 0.6);

	mchelper_draw_line_perc_nongui(doc, bezel_margin_x, bezel_margin_y + bezel_offset_y, 1, 1 - bezel_margin_x, bezel_margin_y + bezel_offset_y, 1);
	mchelper_draw_line_perc_nongui(doc, bezel_margin_x, bezel_y_bottom, 1, 1 - bezel_margin_x, bezel_y_bottom, 1);
	mchelper_draw_line_perc_nongui(doc, bezel_margin_x, bezel_margin_y + bezel_offset_y, 1, bezel_margin_x, bezel_y_bottom, 1);
	mchelper_draw_line_perc_nongui(doc, 1 - bezel_margin_x, bezel_margin_y + bezel_offset_y, 1, 1 - bezel_margin_x, bezel_y_bottom, 1);

	double colors[5][3] = { { 1, 0, 0.1 }, { 1, 0.8, 0 }, { 0, 1, 0.1 }, { 0, 0.6, 1 }, { 0.9, 0, 1 } };

	int ncolors = 0;
	ncolors = sizeof(colors) / sizeof(colors[1]);
	double palette_dot_size = 1.0 / (ncolors + 1);
	mchelper_set_fill_size_perc_nongui(doc, palette_dot_size);

	double palette_startx = (0.5 - (palette_dot_size * ncolors) / 2) + palette_dot_size / 2;
	for (int i = 0; i < ncolors; i += 1) {
		double x = palette_startx + i * palette_dot_size;

		mchelper_set_stroke_color_nongui(doc, dmax2(colors[i][0] - 0.4, 0), dmax2(colors[i][1] - 0.4, 0), dmax2(colors[i][2] - 0.4, 0));
		mchelper_draw_line_perc_nongui(doc, x, bezel_y_bottom + palette_dot_size + drop_shadow_offset_y, 1, x, bezel_y_bottom + palette_dot_size + drop_shadow_offset_y, 1);

		mchelper_set_stroke_color_nongui(doc, colors[i][0], colors[i][1], colors[i][2]);
		mchelper_draw_line_perc_nongui(doc, x, bezel_y_bottom + palette_dot_size, 1, x, bezel_y_bottom + palette_dot_size, 1);
	}

	pencilnecky = bezel_y_bottom - 0.1;
	stroke_width = stroke_width * 0.5;

	mchelper_set_stroke_color_nongui(doc, 0.25, 0.25, 0.25);
	mchelper_set_fill_size_perc_nongui(doc, handle_size + stroke_width);
	mchelper_draw_line_perc_nongui(doc, 0.2, 0.6, 1, -0.4, 1, 0.6);

	mchelper_set_stroke_color_nongui(doc, 0, 0.2, 0.6);
	mchelper_set_fill_size_perc_nongui(doc, fill_size + stroke_width);
	double endx = 0.7;
	double endy = padding + 0.2;
	double endpr = 0.2;
	mchelper_draw_line_perc_nongui(doc, 0.4, pencilnecky - brush_gap, 1, endx, endy, endpr);
	mchelper_draw_line_perc_nongui(doc, endx, endy, endpr, endx + 0.04, endy - 0.06, 0);

	mchelper_set_stroke_color_nongui(doc, 0.5, 0.5, 0.5);
	mchelper_set_fill_size_perc_nongui(doc, handle_size);
	mchelper_draw_line_perc_nongui(doc, 0.2, 0.6, 1, -0.4, 1, 0.4);

	doc->drawparams.brushparams = prebrush_soft_round();
	mchelper_set_stroke_color_nongui(doc, 0.8, 0.8, 0.8);

	mchelper_set_fill_size_perc_nongui(doc, handle_size);
	mchelper_draw_line_perc_nongui(doc, 0.2, 0.59, 1, -0.4, 0.99, 0.3);

	doc->drawparams.brushparams = prebrush_round();
	doc->drawparams.brushparams.pressure_size = true;
	mchelper_set_stroke_color_nongui(doc, 0, 0.5, 1);
	mchelper_set_fill_size_perc_nongui(doc, fill_size);
	mchelper_draw_line_perc_nongui(doc, 0.4, pencilnecky - brush_gap, 1, endx, endy, endpr);
	mchelper_draw_line_perc_nongui(doc, endx, endy, endpr, endx + 0.04, endy - 0.06, 0);

	doc->drawparams.brushparams = prebrush_soft_round();
	mchelper_set_stroke_color_nongui(doc, 0.2, 0.65, 1);
	mchelper_set_fill_size_perc_nongui(doc, fill_size);
	mchelper_draw_line_perc_nongui(doc, 0.4, pencilnecky - brush_gap, 1, 0.4 - 0.04, (pencilnecky - brush_gap) - 0.025, 0);

	doc->drawparams.brushparams.pressure_size = false;
	doc->drawparams.brushparams.opacity = 1;
	doc->drawparams.brushparams.softness = 0;
	doc->drawparams.erase = true;
	mchelper_set_fill_size_perc_nongui(doc, padding);
	mchelper_draw_line_perc_nongui(doc, 0, 0, 1, 0, 1, 1);
}

void test_basic_drawing(GuiTestingContext *tctx)
{
	mchelper_draw_line_perc_nongui(tctx->app->proxydoc->prevdoc, 0, 0, 1, 1, 1, 1);
}

void test_brushes(GuiTestingContext *tctx)
{
	gui_testing_context_exmcmx(tctx, mctest_select_brush_tool);

	int xoffset = 100;

	int preset = 0;
	do {
		preset = helper_select_next_prebrush(tctx, preset);
		xoffset = test_next_xoffset(xoffset, tctx->app);
		test_draw(tctx->app, xoffset, true);

		double coords[3] = { -0.1, 0.5, 1.1 };

		for (int x1i = 0; x1i < sizeof(coords) / sizeof(coords[0]); x1i += 1) {
			for (int y1i = 0; y1i < sizeof(coords) / sizeof(coords[0]); y1i += 1) {
				for (int x2i = 0; x2i < sizeof(coords) / sizeof(coords[0]); x2i += 1) {
					for (int y2i = 0; y2i < sizeof(coords) / sizeof(coords[0]); y2i += 1) {
						mchelper_draw_line_perc(tctx->app, coords[x1i], coords[y1i], 1, coords[x2i], coords[y2i], 1);
					}
				}
			}
		}
	} while (preset > 0);
}

void test_layerops(GuiTestingContext *tctx)
{
	gui_testing_context_exmcmx(tctx, mctest_layerops_0);
	sleep(1);
	gui_testing_context_exmcmx(tctx, mctest_layerops_10);
	sleep(1);
	gui_testing_context_exmcmx(tctx, mctest_layerops_20);
	sleep(1);
	gui_testing_context_exmcmx(tctx, mctest_layerops_30);
}

int test_next_xoffset(int xoffset, myapp *app)
{
	xoffset += 50;
	if (xoffset > image_document_get_fglayer_must(app->proxydoc->prevdoc)->img->width) {
		xoffset = 20;
	}

	return xoffset;
}

void test_nocrash_brushes_while_floodfill(GuiTestingContext *tctx)
{
	gui_testing_context_exmcmx(tctx, mctest_select_floodfill_tool);

	int preset = 0;
	do {
		preset = helper_select_next_prebrush(tctx, preset);
	} while (preset > 0);
}

void testmain_general(myapp *app)
{
	thread *thrd = thread_start((void * (*)(void *usrdata)) tw_testmain_general, app);
	assert(thrd);
}

void testmain_draw_vara_logo(myapp *app)
{
}

ProceduralGuiTestingContext * testmain_sunset(myapp *app)
{
	ProceduralContext *_tmp_1 = (ProceduralContext *) malloc(sizeof(ProceduralContext));
	if (_tmp_1 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	procedural_context_construct(_tmp_1, app->proxydoc->prevdoc);
	ProceduralGuiTestingContext *tctx = (ProceduralGuiTestingContext *) malloc(sizeof(ProceduralGuiTestingContext));
	if (tctx == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	procedural_gui_testing_context_construct(tctx, app, _tmp_1);
	mctest_draw_sunset(tctx);

	return tctx;
}

void tw_testmain_general(myapp *app)
{
	GuiTestingContext *tctx = (GuiTestingContext *) malloc(sizeof(GuiTestingContext));
	if (tctx == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	gui_testing_context_construct(tctx, app);

	uidelay();

	test_basic_drawing(tctx);
	test_layerops(tctx);
	test_basic_drawing(tctx);

	test_nocrash_brushes_while_floodfill(tctx);

	test_brushes(tctx);

	for (int i = 0; i < 10; i += 1) {
		gui_testing_context_exmcmx(tctx, mctest_increase_fill_size);
	}

	for (int j = 0; j < 100; j += 1) {
		printf("draw %d\n", j);
		test_draw(app, 100, false);
	}

	puts("ok.");
	exit(EXIT_SUCCESS);
	if (tctx) {
		gui_testing_context_destruct(tctx);
		free(tctx);
	}
}
