/*
** Copyright (C) 2001-2024 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

#include "dbupgrade.h"

#include "zbxalgo.h"
#include "zbxdb.h"
#include "zbxdbschema.h"

/*
 * 5.2 development database patches
 */

#ifndef HAVE_SQLITE3

static int	DBpatch_5010000(void)
{
	const zbx_db_field_t	field = {"default_lang", "en_GB", NULL, NULL, 5, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010001(void)
{
	const zbx_db_field_t	field = {"lang", "default", NULL, NULL, 7, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBmodify_field_type("users", &field, NULL);
}

static int	DBpatch_5010002(void)
{
	if (ZBX_DB_OK > zbx_db_execute("update users set lang='default',theme='default' where alias='guest'"))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5010003(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute(
			"delete from profiles "
			"where idx in ('web.latest.toggle','web.latest.toggle_other')"))
		return FAIL;

	return SUCCEED;
}

static int	DBpatch_5010004(void)
{
	zbx_db_row_t	row;
	zbx_db_result_t	result;
	int		ret = SUCCEED;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	result = zbx_db_select("select userid from profiles where idx='web.latest.sort' and value_str='lastclock'");

	while (NULL != (row = zbx_db_fetch(result)))
	{
		if (ZBX_DB_OK > zbx_db_execute(
			"delete from profiles"
			" where userid='%s'"
				" and idx in ('web.latest.sort','web.latest.sortorder')", row[0]))
		{
			ret = FAIL;
			break;
		}
	}
	zbx_db_free_result(result);

	return ret;
}

static int	DBpatch_5010005(void)
{
	const zbx_db_field_t	field = {"default_timezone", "system", NULL, NULL, 50, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010006(void)
{
	const zbx_db_field_t	field = {"timezone", "default", NULL, NULL, 50, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("users", &field);
}

static int	DBpatch_5010007(void)
{
	const zbx_db_field_t	field = {"login_attempts", "5", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010008(void)
{
	const zbx_db_field_t	field = {"login_block", "30s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010009(void)
{
	const zbx_db_field_t	field = {"show_technical_errors", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010010(void)
{
	const zbx_db_field_t	field = {"validate_uri_schemes", "1", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010011(void)
{
	const zbx_db_field_t	field = {"uri_valid_schemes", "http,https,ftp,file,mailto,tel,ssh", NULL, NULL, 255,
			ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010012(void)
{
	const zbx_db_field_t	field = {"x_frame_options", "SAMEORIGIN", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL,
			0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010013(void)
{
	const zbx_db_field_t	field = {"iframe_sandboxing_enabled", "1", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010014(void)
{
	const zbx_db_field_t	field = {"iframe_sandboxing_exceptions", "", NULL, NULL, 255, ZBX_TYPE_CHAR,
			ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010015(void)
{
	const zbx_db_field_t	field = {"max_overview_table_size", "50", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010016(void)
{
	const zbx_db_field_t	field = {"history_period", "24h", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010017(void)
{
	const zbx_db_field_t	field = {"period_default", "1h", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010018(void)
{
	const zbx_db_field_t	field = {"max_period", "2y", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010019(void)
{
	const zbx_db_field_t	field = {"socket_timeout", "3s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010020(void)
{
	const zbx_db_field_t	field = {"connect_timeout", "3s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010021(void)
{
	const zbx_db_field_t	field = {"media_type_test_timeout", "65s", NULL, NULL, 32,
			ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010022(void)
{
	const zbx_db_field_t	field = {"script_timeout", "60s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010023(void)
{
	const zbx_db_field_t	field = {"item_test_timeout", "60s", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010024(void)
{
	const zbx_db_field_t	field = {"session_key", "", NULL, NULL, 32, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("config", &field);
}

static int	DBpatch_5010025(void)
{
	const zbx_db_field_t	field = {"value", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBmodify_field_type("hostmacro", &field, NULL);
}

static int	DBpatch_5010026(void)
{
	const zbx_db_field_t	field = {"value", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBmodify_field_type("globalmacro", &field, NULL);
}

static int	DBpatch_5010027(void)
{
	const zbx_db_field_t	old_field = {"data", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"data", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("task_data", &field, &old_field);
}

static int	DBpatch_5010028(void)
{
	const zbx_db_field_t	old_field = {"info", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"info", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("task_result", &field, &old_field);
}

static int	DBpatch_5010029(void)
{
	const zbx_db_field_t	old_field = {"params", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"params", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("items", &field, &old_field);
}

static int	DBpatch_5010030(void)
{
	const zbx_db_field_t	old_field = {"description", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"description", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("items", &field, &old_field);
}

static int	DBpatch_5010031(void)
{
	const zbx_db_field_t	old_field = {"posts", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"posts", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("items", &field, &old_field);
}

static int	DBpatch_5010032(void)
{
	const zbx_db_field_t	old_field = {"headers", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"headers", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("items", &field, &old_field);
}

static int	DBpatch_5010033(void)
{
	const zbx_db_field_t	field = {"custom_interfaces", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0};

	return DBadd_field("hosts", &field);
}

static int	DBpatch_5010034(void)
{
	const zbx_db_field_t	old_field = {"value_str", "", NULL, NULL, 255, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};
	const zbx_db_field_t	field = {"value_str", "", NULL, NULL, 0, ZBX_TYPE_TEXT, ZBX_NOTNULL, 0};

	return DBmodify_field_type("profiles", &field, &old_field);
}

static int	DBpatch_5010035(void)
{
	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where idx like 'web.hostsmon.filter.%%' or"
			" idx like 'web.problem.filter%%'"))
	{
		return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5010036(void)
{
	const zbx_db_field_t	field = {"event_name", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0};

	return DBadd_field("triggers", &field);
}

static int	DBpatch_5010038(void)
{
	const zbx_db_field_t	field = {"templateid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0};

	return DBadd_field("dashboard", &field);
}

static int	DBpatch_5010039(void)
{
	const zbx_db_field_t	field = {"userid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, 0, 0};

	return DBdrop_not_null("dashboard", &field);
}

static int	DBpatch_5010040(void)
{
	return DBcreate_index("dashboard", "dashboard_1", "userid", 0);
}

static int	DBpatch_5010041(void)
{
#ifdef HAVE_MYSQL	/* MySQL automatically creates index and might not remove it on some conditions */
	if (SUCCEED == zbx_db_index_exists("dashboard", "c_dashboard_1"))
		return DBdrop_index("dashboard", "c_dashboard_1");
#endif
	return SUCCEED;
}

static int	DBpatch_5010042(void)
{
	return DBcreate_index("dashboard", "dashboard_2", "templateid", 0);
}

static int	DBpatch_5010043(void)
{
	const zbx_db_field_t	field = {"templateid", 0, "hosts", "hostid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("dashboard", 2, &field);
}

typedef struct
{
	uint64_t	screenitemid;
	uint64_t	screenid;
	int		resourcetype;
	uint64_t	resourceid;
	int		width;
	int		height;
	int		x;
	int		y;
	int		colspan;
	int		rowspan;
	int		elements;
	int		style;
	char		*url;
	int		max_columns;
}
zbx_db_screen_item_t;

typedef struct
{
	uint64_t	widget_fieldid;
	int		type;
	char		*name;
	int		value_int;
	char		*value_str;
	uint64_t	value_itemid;
	uint64_t	value_graphid;
}
zbx_db_widget_field_t;

typedef struct
{
	int	position;
	int	span;
	int	size;
}
zbx_screen_item_dim_t;

typedef struct
{
	uint64_t	dashboardid;
	char		*name;
	uint64_t	userid;
	int		private;
	uint64_t	templateid;
}
zbx_db_dashboard_t;

typedef struct
{
	uint64_t	widgetid;
	uint64_t	dashboardid;
	char		*type;
	char		*name;
	int		x;
	int		y;
	int		width;
	int		height;
	int		view_mode;
}
zbx_db_widget_t;

#define DASHBOARD_MAX_COLS			(24)
#define DASHBOARD_MAX_ROWS			(64)
#define DASHBOARD_WIDGET_MIN_ROWS		(2)
#define DASHBOARD_WIDGET_MAX_ROWS		(32)
#define SCREEN_MAX_ROWS				(100)
#define SCREEN_MAX_COLS				(100)

#undef SCREEN_RESOURCE_CLOCK
#define SCREEN_RESOURCE_CLOCK			(7)
#undef SCREEN_RESOURCE_GRAPH
#define SCREEN_RESOURCE_GRAPH			(0)
#undef SCREEN_RESOURCE_SIMPLE_GRAPH
#define SCREEN_RESOURCE_SIMPLE_GRAPH		(1)
#undef SCREEN_RESOURCE_LLD_GRAPH
#define SCREEN_RESOURCE_LLD_GRAPH		(20)
#undef SCREEN_RESOURCE_LLD_SIMPLE_GRAPH
#define SCREEN_RESOURCE_LLD_SIMPLE_GRAPH	(19)
#undef SCREEN_RESOURCE_PLAIN_TEXT
#define SCREEN_RESOURCE_PLAIN_TEXT		(3)
#undef SCREEN_RESOURCE_URL
#define SCREEN_RESOURCE_URL			(11)

#define ZBX_WIDGET_FIELD_TYPE_INT32		(0)
#define ZBX_WIDGET_FIELD_TYPE_STR		(1)
#define ZBX_WIDGET_FIELD_TYPE_ITEM		(4)
#define ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE	(5)
#define ZBX_WIDGET_FIELD_TYPE_GRAPH		(6)
#define ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE	(7)

/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH				(0) */
/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH			(1) */
/* #define ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE		(2) */
/* #define ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE	(3) */

#define ZBX_WIDGET_TYPE_CLOCK			("clock")
#define ZBX_WIDGET_TYPE_GRAPH_CLASSIC		("graph")
#define ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE		("graphprototype")
#define ZBX_WIDGET_TYPE_PLAIN_TEXT		("plaintext")
#define ZBX_WIDGET_TYPE_URL			("url")

#define POS_EMPTY	(127)
#define POS_TAKEN	(1)

ZBX_VECTOR_DECL(scitem_dim, zbx_screen_item_dim_t)
ZBX_VECTOR_IMPL(scitem_dim, zbx_screen_item_dim_t)
ZBX_VECTOR_DECL(char, char)
ZBX_VECTOR_IMPL(char, char)

#define SKIP_EMPTY(vector,index)	if (POS_EMPTY == vector->values[index]) continue

static void	DBpatch_init_dashboard(zbx_db_dashboard_t *dashboard, char *name, uint64_t templateid)
{
	memset((void *)dashboard, 0, sizeof(zbx_db_dashboard_t));
	dashboard->templateid = templateid;
	dashboard->name = zbx_strdup(NULL, name);
}

static void	DBpatch_widget_field_free(zbx_db_widget_field_t *field)
{
	zbx_free(field->name);
	zbx_free(field->value_str);
	zbx_free(field);
}

static void	DBpatch_screen_item_free(zbx_db_screen_item_t *si)
{
	zbx_free(si->url);
	zbx_free(si);
}

static int	DBpatch_is_convertible_screen_item(int rt)
{
	return SCREEN_RESOURCE_CLOCK == rt || SCREEN_RESOURCE_GRAPH  == rt || SCREEN_RESOURCE_SIMPLE_GRAPH == rt ||
			SCREEN_RESOURCE_LLD_GRAPH == rt || SCREEN_RESOURCE_LLD_SIMPLE_GRAPH == rt ||
			SCREEN_RESOURCE_PLAIN_TEXT == rt || SCREEN_RESOURCE_URL == rt;
}

static size_t	DBpatch_array_max_used_index(char *array, size_t arr_size)
{
	size_t	i, m = 0;

	for (i = 0; i < arr_size; i++)
	{
		if (0 != array[i])
			m = i;
	}

	return m;
}

static void DBpatch_normalize_screen_items_pos(zbx_vector_ptr_t *scr_items)
{
	char	used_x[SCREEN_MAX_COLS], used_y[SCREEN_MAX_ROWS];
	char	keep_x[SCREEN_MAX_COLS], keep_y[SCREEN_MAX_ROWS];
	int	i, n, x;

	memset((void *)used_x, 0, sizeof(used_x));
	memset((void *)used_y, 0, sizeof(used_y));
	memset((void *)keep_x, 0, sizeof(keep_x));
	memset((void *)keep_y, 0, sizeof(keep_y));

	for (i = 0; i < scr_items->values_num; i++)
	{
		zbx_db_screen_item_t	*c = (zbx_db_screen_item_t *)scr_items->values[i];

		for (n = c->x; n < c->x + c->colspan && n < SCREEN_MAX_COLS; n++)
			used_x[n] = 1;
		for (n = c->y; n < c->y + c->rowspan && n < SCREEN_MAX_ROWS; n++)
			used_y[n] = 1;

		keep_x[c->x] = 1;
		if (c->x + c->colspan < SCREEN_MAX_COLS)
			keep_x[c->x + c->colspan] = 1;
		keep_y[c->y] = 1;
		if (c->y + c->rowspan < SCREEN_MAX_ROWS)
			keep_y[c->y + c->rowspan] = 1;
	}

#define COMPRESS_SCREEN_ITEMS(axis, span, a_size)							\
													\
do {													\
	for (x = (int)DBpatch_array_max_used_index(keep_ ## axis, a_size); x >= 0; x--)			\
	{												\
		if (0 != keep_ ## axis[x] && 0 != used_ ## axis[x])					\
			continue;									\
													\
		for (i = 0; i < scr_items->values_num; i++)						\
		{											\
			zbx_db_screen_item_t	*c = (zbx_db_screen_item_t *)scr_items->values[i];	\
													\
			if (x < c->axis)								\
				c->axis--;								\
													\
			if (x > c->axis && x < c->axis + c->span)					\
				c->span--;								\
		}											\
	}												\
} while (0)

	COMPRESS_SCREEN_ITEMS(x, colspan, SCREEN_MAX_COLS);
	COMPRESS_SCREEN_ITEMS(y, rowspan, SCREEN_MAX_ROWS);

#undef COMPRESS_SCREEN_ITEMS
}

static void	DBpatch_get_preferred_widget_size(zbx_db_screen_item_t *item, int *w, int *h)
{
	*w = item->width;
	*h = item->height;

	if (SCREEN_RESOURCE_LLD_GRAPH == item->resourcetype ||
			SCREEN_RESOURCE_LLD_SIMPLE_GRAPH == item->resourcetype ||
			SCREEN_RESOURCE_GRAPH == item->resourcetype ||
			SCREEN_RESOURCE_SIMPLE_GRAPH == item->resourcetype)
	{
		*h += 215;	/* SCREEN_LEGEND_HEIGHT */
	}

	if (SCREEN_RESOURCE_PLAIN_TEXT == item->resourcetype)
		*h = 2 + 2 * MIN(25, item->elements) / 5;
	else
		*h = (int)round((double)*h / 70);			/* WIDGET_ROW_HEIGHT */

	*w = (int)round((double)*w / 1920 * DASHBOARD_MAX_COLS);	/* DISPLAY_WIDTH */

	*w = MIN(DASHBOARD_MAX_COLS, MAX(1, *w));
	*h = MIN(DASHBOARD_WIDGET_MAX_ROWS, MAX(DASHBOARD_WIDGET_MIN_ROWS, *h));
}

static void	DBpatch_get_min_widget_size(zbx_db_screen_item_t *item, int *w, int *h)
{
	switch (item->resourcetype)
	{
		case SCREEN_RESOURCE_CLOCK:
			*w = 1; *h = 2;
			break;
		case SCREEN_RESOURCE_GRAPH:
		case SCREEN_RESOURCE_SIMPLE_GRAPH:
		case SCREEN_RESOURCE_LLD_GRAPH:
		case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
			*w = 4; *h = 4;
			break;
		case SCREEN_RESOURCE_PLAIN_TEXT:
		case SCREEN_RESOURCE_URL:
			*w = 4; *h = 2;
			break;
		default:
			zabbix_log(LOG_LEVEL_WARNING, "%s: unknown resource type %d", __func__, item->resourcetype);
	}
}

static char	*lw_array_to_str(zbx_vector_char_t *v)
{
	static char	str[MAX_STRING_LEN];
	char		*ptr;
	int		i, max = MAX_STRING_LEN, len;

	ptr = str;
	len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
	ptr += len;
	max -= len;

	for (i = 0; 0 < max && i < v->values_num; i++)
	{
		if (POS_EMPTY != v->values[i])
		{
			len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, (int)v->values[i]);
			ptr += len;
			max -= len;
		}
	}

	if (max > 1)
		strcat(ptr, "]");

	return str;
}

static void	lw_array_debug(char *pfx, zbx_vector_char_t *v)
{
	zabbix_log(LOG_LEVEL_TRACE, "%s: %s", pfx, lw_array_to_str(v));
}

static void	int_array_debug(char *pfx, int *a, int alen, int emptyval)
{
	static char	str[MAX_STRING_LEN];
	char		*ptr;
	int		i, max = MAX_STRING_LEN, len;

	ptr = str;
	len = (int)zbx_snprintf(ptr, (size_t)max, "[ ");
	ptr += len;
	max -= len;

	for (i = 0; 0 < max && i < alen; i++)
	{
		if (emptyval != a[i])
		{
			len = (int)zbx_snprintf(ptr, (size_t)max, "%d:%d ", i, a[i]);
			ptr += len;
			max -= len;
		}
	}

	if (max > 1)
		strcat(ptr, "]");

	zabbix_log(LOG_LEVEL_TRACE, "%s: %s", pfx, str);
}

static zbx_vector_char_t	*lw_array_create(void)
{
	zbx_vector_char_t	*v;
	static char		fill[SCREEN_MAX_ROWS];

	if (0 == fill[0])
		memset(fill, POS_EMPTY, SCREEN_MAX_ROWS);

	v = (zbx_vector_char_t *)malloc(sizeof(zbx_vector_char_t));

	zbx_vector_char_create(v);
	zbx_vector_char_append_array(v, fill, SCREEN_MAX_ROWS);

	return v;
}

static void	lw_array_free(zbx_vector_char_t *v)
{
	if (NULL != v)
	{
		zbx_vector_char_destroy(v);
		zbx_free(v);
	}
}

static zbx_vector_char_t	*lw_array_create_fill(int start, size_t num)
{
	size_t			i;
	zbx_vector_char_t	*v;

	v = lw_array_create();

	for (i = (size_t)start; i < (size_t)start + num && i < (size_t)v->values_num; i++)
		v->values[i] = POS_TAKEN;

	return v;
}

static zbx_vector_char_t	*lw_array_diff(zbx_vector_char_t *a, zbx_vector_char_t *b)
{
	int			i;
	zbx_vector_char_t	*v;

	v = lw_array_create();

	for (i = 0; i < a->values_num; i++)
	{
		SKIP_EMPTY(a, i);
		if (POS_EMPTY == b->values[i])
			v->values[i] = a->values[i];
	}

	return v;
}

static zbx_vector_char_t	*lw_array_intersect(zbx_vector_char_t *a, zbx_vector_char_t *b)
{
	int			i;
	zbx_vector_char_t	*v;

	v = lw_array_create();

	for (i = 0; i < a->values_num; i++)
	{
		SKIP_EMPTY(a, i);
		if (POS_EMPTY != b->values[i])
			v->values[i] = a->values[i];
	}

	return v;
}

static int	lw_array_count(zbx_vector_char_t *v)
{
	int	i, c = 0;

	for (i = 0; i < v->values_num; i++)
	{
		if (POS_EMPTY != v->values[i])
			c++;
	}

	return c;
}

static int	lw_array_sum(zbx_vector_char_t *v)
{
	int	i, c = 0;

	for (i = 0; i < v->values_num; i++)
	{
		if (POS_EMPTY != v->values[i])
			c += v->values[i];
	}

	return c;
}

typedef struct
{
	int			index;	/* index for zbx_vector_scitem_dim_t */
	zbx_vector_char_t	*r_block;
}
sciitem_block_t;

static zbx_vector_char_t	*sort_dimensions;

static int	DBpatch_block_compare_func(const void *d1, const void *d2)
{
	const sciitem_block_t	*i1 = *(const sciitem_block_t * const *)d1;
	const sciitem_block_t	*i2 = *(const sciitem_block_t * const *)d2;
	zbx_vector_char_t	*diff1, *diff2;
	int			unsized_a, unsized_b;

	diff1 = lw_array_diff(i1->r_block, sort_dimensions);
	diff2 = lw_array_diff(i2->r_block, sort_dimensions);

	unsized_a = lw_array_count(diff1);
	unsized_b = lw_array_count(diff2);

	lw_array_free(diff1);
	lw_array_free(diff2);

	ZBX_RETURN_IF_NOT_EQUAL(unsized_a, unsized_b);

	return 0;
}

static zbx_vector_char_t	*DBpatch_get_axis_dimensions(zbx_vector_scitem_dim_t *scitems)
{
	int			i;
	zbx_vector_ptr_t	blocks;
	sciitem_block_t		*block;
	zbx_vector_char_t	*dimensions;

	zabbix_log(LOG_LEVEL_TRACE, "In %s()", __func__);

	zbx_vector_ptr_create(&blocks);
	dimensions = lw_array_create();

	for (i = 0; i < scitems->values_num; i++)
	{
		block = (sciitem_block_t *)malloc(sizeof(sciitem_block_t));
		block->r_block = lw_array_create_fill(scitems->values[i].position, (size_t)scitems->values[i].span);
		block->index = i;
		zbx_vector_ptr_append(&blocks, (void *)block);
	}

	sort_dimensions = dimensions;

	while (0 < blocks.values_num)
	{
		zbx_vector_char_t	*block_dimensions, *block_unsized, *r_block;
		int			block_dimensions_sum, block_unsized_count, size_overflow, n;

		zbx_vector_ptr_sort(&blocks, DBpatch_block_compare_func);
		block = blocks.values[0];
		r_block = block->r_block;

		block_dimensions = lw_array_intersect(dimensions, r_block);
		block_dimensions_sum = lw_array_sum(block_dimensions);
		lw_array_free(block_dimensions);

		block_unsized = lw_array_diff(r_block, dimensions);
		block_unsized_count = lw_array_count(block_unsized);
		size_overflow = scitems->values[block->index].size - block_dimensions_sum;

		if (0 < block_unsized_count)
		{
			for (n = 0; n < block_unsized->values_num; n++)
			{
				SKIP_EMPTY(block_unsized, n);
				dimensions->values[n] = (char)MAX(1, size_overflow / block_unsized_count);
				size_overflow -= dimensions->values[n];
				block_unsized_count--;
			}
		}
		else if (0 < size_overflow)
		{
			for (n = 0; n < r_block->values_num; n++)
			{
				double	factor;
				int	new_dimension;

				SKIP_EMPTY(r_block, n);
				factor = (double)(size_overflow + block_dimensions_sum) / block_dimensions_sum;
				new_dimension = (int)round(factor * dimensions->values[n]);
				block_dimensions_sum -= dimensions->values[n];
				size_overflow -= new_dimension - dimensions->values[n];
				dimensions->values[n] = (char)new_dimension;
			}
		}

		lw_array_free(block->r_block);
		zbx_free(block);
		lw_array_free(block_unsized);
		zbx_vector_ptr_remove(&blocks, 0);
	}

	zbx_vector_ptr_destroy(&blocks);

	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): dim:%s", __func__, lw_array_to_str(dimensions));

	return dimensions;
}

/* modifies widget units in first argument */
static void	DBpatch_adjust_axis_dimensions(zbx_vector_char_t *d, zbx_vector_char_t *d_min, int target)
{
	int	dimensions_sum, i;

	zabbix_log(LOG_LEVEL_TRACE, "In %s(): d:%s", __func__, lw_array_to_str(d));
	zabbix_log(LOG_LEVEL_TRACE, "  d_min:%s", lw_array_to_str(d_min));

	dimensions_sum = lw_array_sum(d);

	while (dimensions_sum != target)
	{
		int	potential_index = -1;
		double	potential_value;

		for (i = 0; i < d->values_num; i++)
		{
			double	value;

			SKIP_EMPTY(d, i);
			value = (double)d->values[i] / d_min->values[i];

			if (0 > potential_index ||
					(dimensions_sum > target && value > potential_value) ||
					(dimensions_sum < target && value < potential_value))
			{
				potential_index = i;
				potential_value = value;
			}
		}

		if (0 <= potential_index)
		{
			zabbix_log(LOG_LEVEL_TRACE, "dim_sum:%d pot_idx/val:%d/%.2lf", dimensions_sum,
					potential_index, potential_value);
		}

		if (dimensions_sum > target && d->values[potential_index] == d_min->values[potential_index])
			break;

		if (dimensions_sum > target)
		{
			d->values[potential_index]--;
			dimensions_sum--;
		}
		else
		{
			d->values[potential_index]++;
			dimensions_sum++;
		}
	}

	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): d:%s", __func__, lw_array_to_str(d));
}

static void	DBpatch_get_dashboard_dimensions(zbx_vector_ptr_t *scr_items, zbx_vector_char_t **x,
		zbx_vector_char_t **y)
{
	zbx_vector_char_t	*dim_x_pref, *dim_x_min;
	zbx_vector_char_t	*dim_y_pref, *dim_y_min;
	zbx_vector_scitem_dim_t	items_x_pref, items_y_pref;
	zbx_vector_scitem_dim_t	items_x_min, items_y_min;
	int			i;

	zabbix_log(LOG_LEVEL_TRACE, "In %s()", __func__);

	zbx_vector_scitem_dim_create(&items_x_pref);
	zbx_vector_scitem_dim_create(&items_y_pref);
	zbx_vector_scitem_dim_create(&items_x_min);
	zbx_vector_scitem_dim_create(&items_y_min);

	for (i = 0; i < scr_items->values_num; i++)
	{
		int			pref_size_w, pref_size_h;
		int			min_size_w, min_size_h;
		zbx_screen_item_dim_t	item;
		zbx_db_screen_item_t	*si;

		si = scr_items->values[i];
		DBpatch_get_preferred_widget_size(si, &pref_size_w, &pref_size_h);
		DBpatch_get_min_widget_size(si, &min_size_w, &min_size_h);

		item.position = si->x;
		item.span = si->colspan;
		item.size = MAX(pref_size_w, min_size_w);
		zbx_vector_scitem_dim_append(&items_x_pref, item);

		item.position = si->y;
		item.span = si->rowspan;
		item.size = MAX(pref_size_h, min_size_h);
		zbx_vector_scitem_dim_append(&items_y_pref, item);

		item.position = si->x;
		item.span = si->colspan;
		item.size = min_size_w;
		zbx_vector_scitem_dim_append(&items_x_min, item);

		item.position = si->y;
		item.span = si->rowspan;
		item.size = min_size_h;
		zbx_vector_scitem_dim_append(&items_y_min, item);
	}

	dim_x_pref = DBpatch_get_axis_dimensions(&items_x_pref);
	dim_x_min = DBpatch_get_axis_dimensions(&items_x_min);

	zabbix_log(LOG_LEVEL_TRACE, "%s: dim_x_pref:%s", __func__, lw_array_to_str(dim_x_pref));
	zabbix_log(LOG_LEVEL_TRACE, "  dim_x_min:%s", lw_array_to_str(dim_x_min));

	DBpatch_adjust_axis_dimensions(dim_x_pref, dim_x_min, DASHBOARD_MAX_COLS);

	dim_y_pref = DBpatch_get_axis_dimensions(&items_y_pref);
	dim_y_min = DBpatch_get_axis_dimensions(&items_y_min);

	if (DASHBOARD_MAX_ROWS < lw_array_sum(dim_y_pref))
		DBpatch_adjust_axis_dimensions(dim_y_pref, dim_y_min, DASHBOARD_MAX_ROWS);

	lw_array_free(dim_x_min);
	lw_array_free(dim_y_min);
	zbx_vector_scitem_dim_destroy(&items_x_pref);
	zbx_vector_scitem_dim_destroy(&items_y_pref);
	zbx_vector_scitem_dim_destroy(&items_x_min);
	zbx_vector_scitem_dim_destroy(&items_y_min);

	*x = dim_x_pref;
	*y = dim_y_pref;

	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): x:%s y:%s", __func__, lw_array_to_str(*x), lw_array_to_str(*y));
}

static zbx_db_widget_field_t	*DBpatch_make_widget_field(int type, char *name, void *value)
{
	zbx_db_widget_field_t	*wf;

	wf = (zbx_db_widget_field_t *)zbx_calloc(NULL, 1, sizeof(zbx_db_widget_field_t));
	wf->name = zbx_strdup(NULL, name);
	wf->type = type;

	switch (type)
	{
		case ZBX_WIDGET_FIELD_TYPE_INT32:
			wf->value_int = *((int *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_STR:
			wf->value_str = zbx_strdup(NULL, (char *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_ITEM:
		case ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE:
			wf->value_itemid = *((uint64_t *)value);
			break;
		case ZBX_WIDGET_FIELD_TYPE_GRAPH:
		case ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE:
			wf->value_graphid = *((uint64_t *)value);
			break;
		default:
			zabbix_log(LOG_LEVEL_WARNING, "%s: unknown field type: %d", __func__, type);
	}

	if (NULL == wf->value_str)
		wf->value_str = zbx_strdup(NULL, "");

	return wf;
}

static void DBpatch_widget_from_screen_item(zbx_db_screen_item_t *si, zbx_db_widget_t *w, zbx_vector_ptr_t *fields)
{
	zbx_db_widget_field_t	*f;
	int			tmp;

	w->name = zbx_strdup(NULL, "");
	w->view_mode = 0;	/* ZBX_WIDGET_VIEW_MODE_NORMAL */

#define ADD_FIELD(a, b, c)				\
							\
do {							\
	f = DBpatch_make_widget_field(a, b, c);		\
	zbx_vector_ptr_append(fields, (void *)f);	\
} while (0)

	switch (si->resourcetype)
	{
		case SCREEN_RESOURCE_CLOCK:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_CLOCK);

			/* here are below in this switch we add only those fields that are not */
			/* considered default by frontend API */

			if (0 != si->style)	/* style 0 is default, don't add */
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "time_type", (void *)&si->style);
			if (2 == si->style)	/* TIME_TYPE_HOST */
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemid", (void *)&si->resourceid);
			break;
		case SCREEN_RESOURCE_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_CLASSIC);
			/* source_type = ZBX_WIDGET_FIELD_RESOURCE_GRAPH (0); don't add because it's default */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GRAPH, "graphid", (void *)&si->resourceid);
			break;
		case SCREEN_RESOURCE_SIMPLE_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_CLASSIC);
			tmp = 1;	/* source_type = ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "source_type", (void *)&tmp);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemid", (void *)&si->resourceid);
			break;
		case SCREEN_RESOURCE_LLD_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE);
			/* source_type = ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE (2) */
			/* don't add because it's default */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE, "graphid", (void *)&si->resourceid);
			/* add field "columns" because the default value is 2 */
			tmp = 1;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "columns", (void *)&tmp);
			/* don't add field "rows" because 1 is default */
			break;
		case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE);
			tmp = 3;	/* source_type = ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE */
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "source_type", (void *)&tmp);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE, "itemid", (void *)&si->resourceid);
			tmp = 1;
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "columns", (void *)&tmp);
			/* don't add field "rows" because 1 is default */
			break;
		case SCREEN_RESOURCE_PLAIN_TEXT:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_PLAIN_TEXT);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_ITEM, "itemids", (void *)&si->resourceid);
			if (0 != si->style)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_as_html", (void *)&si->style);
			if (25 != si->elements)
				ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_INT32, "show_lines", (void *)&si->elements);
			break;
		case SCREEN_RESOURCE_URL:
			w->type = zbx_strdup(NULL, ZBX_WIDGET_TYPE_URL);
			ADD_FIELD(ZBX_WIDGET_FIELD_TYPE_STR, "url", (void *)si->url);
			break;
		default:
			zabbix_log(LOG_LEVEL_WARNING, "%s: unknown screen resource type: %d", __func__,
					si->resourcetype);
	}
#undef ADD_FIELD
}

static char	*DBpatch_resourcetype_str(int rtype)
{
	switch (rtype)
	{
		case SCREEN_RESOURCE_CLOCK:
			return "clock";
		case SCREEN_RESOURCE_GRAPH:
			return "graph";
		case SCREEN_RESOURCE_SIMPLE_GRAPH:
			return "simplegraph";
		case SCREEN_RESOURCE_LLD_GRAPH:
			return "lldgraph";
		case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH:
			return "lldsimplegraph";
		case SCREEN_RESOURCE_PLAIN_TEXT:
			return "plaintext";
		case SCREEN_RESOURCE_URL:
			return "url";
	}

	return "*unknown*";
}

static void	DBpatch_trace_screen_item(zbx_db_screen_item_t *item)
{
	zabbix_log(LOG_LEVEL_TRACE, "    screenitemid:" ZBX_FS_UI64 " screenid:" ZBX_FS_UI64,
			item->screenitemid, item->screenid);
	zabbix_log(LOG_LEVEL_TRACE, "        resourcetype: %s resourceid:" ZBX_FS_UI64,
			DBpatch_resourcetype_str(item->resourcetype), item->resourceid);
	zabbix_log(LOG_LEVEL_TRACE, "        w/h: %dx%d (x,y): (%d,%d) (c,rspan): (%d,%d)",
			item->width, item->height, item->x, item->y, item->colspan, item->rowspan);
}

static void	DBpatch_trace_widget(zbx_db_widget_t *w)
{
	zabbix_log(LOG_LEVEL_TRACE, "    widgetid:" ZBX_FS_UI64 " dbid:" ZBX_FS_UI64 " type:%s",
			w->widgetid, w->dashboardid, w->type);
	zabbix_log(LOG_LEVEL_TRACE, "    widget type: %s w/h: %dx%d (x,y): (%d,%d)",
			w->type, w->width, w->height, w->x, w->y);
}

/* adds new dashboard to the DB, sets new dashboardid in the struct */
static int 	DBpatch_add_dashboard(zbx_db_dashboard_t *dashboard)
{
	char	*name_esc;
	int	res;

	dashboard->dashboardid = zbx_db_get_maxid("dashboard");
	name_esc = zbx_db_dyn_escape_string(dashboard->name);

	zabbix_log(LOG_LEVEL_TRACE, "adding dashboard id:" ZBX_FS_UI64, dashboard->dashboardid);

	res = zbx_db_execute("insert into dashboard (dashboardid,name,templateid) values ("
			ZBX_FS_UI64 ",'%s'," ZBX_FS_UI64 ")", dashboard->dashboardid, name_esc,
			dashboard->templateid);

	zbx_free(name_esc);

	return ZBX_DB_OK > res ? FAIL : SUCCEED;
}

/* adds new widget and widget fields to the DB */
static int	DBpatch_add_widget(uint64_t dashboardid, zbx_db_widget_t *widget, zbx_vector_ptr_t *fields)
{
	uint64_t	new_fieldid;
	int		i, ret = SUCCEED;
	char		*name_esc;

	widget->widgetid = zbx_db_get_maxid("widget");
	widget->dashboardid = dashboardid;
	name_esc = zbx_db_dyn_escape_string(widget->name);

	zabbix_log(LOG_LEVEL_TRACE, "adding widget id: " ZBX_FS_UI64 ", type: %s", widget->widgetid, widget->type);

	if (ZBX_DB_OK > zbx_db_execute("insert into widget (widgetid,dashboardid,type,name,x,y,width,height,view_mode) "
			"values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",'%s','%s',%d,%d,%d,%d,%d)",
			widget->widgetid, widget->dashboardid, widget->type, name_esc, widget->x, widget->y,
			widget->width, widget->height, widget->view_mode))
	{
		ret = FAIL;
	}

	zbx_free(name_esc);

	if (SUCCEED == ret && 0 < fields->values_num)
		new_fieldid = zbx_db_get_maxid_num("widget_field", fields->values_num);

	for (i = 0; SUCCEED == ret && i < fields->values_num; i++)
	{
		char			*url_esc;
		zbx_db_widget_field_t	*f;

		f = (zbx_db_widget_field_t *)fields->values[i];
		url_esc = zbx_db_dyn_escape_string(f->value_str);

		if (ZBX_DB_OK > zbx_db_execute("insert into widget_field (widget_fieldid,widgetid,type,name,value_int,"
				"value_str,value_itemid,value_graphid) values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,"
				"'%s',%d,'%s',%s,%s)", new_fieldid++, widget->widgetid, f->type, f->name, f->value_int,
				url_esc, zbx_db_sql_id_ins(f->value_itemid), zbx_db_sql_id_ins(f->value_graphid)))
		{
			ret = FAIL;
		}

		zbx_free(url_esc);
	}

	return ret;
}

static int	DBpatch_delete_screen(uint64_t screenid)
{
	if (ZBX_DB_OK > zbx_db_execute("delete from screens_items where screenid=" ZBX_FS_UI64, screenid))
		return FAIL;

	if (ZBX_DB_OK > zbx_db_execute("delete from screens where screenid=" ZBX_FS_UI64, screenid))
		return FAIL;

	return SUCCEED;
}

#define OFFSET_ARRAY_SIZE	(SCREEN_MAX_ROWS + 1)

static int	DBpatch_convert_screen(uint64_t screenid, char *name, uint64_t templateid)
{
	zbx_db_result_t		result;
	zbx_db_row_t		row;
	int			i, ret;
	zbx_db_screen_item_t	*scr_item;
	zbx_db_dashboard_t	dashboard;
	zbx_vector_ptr_t	screen_items;
	zbx_vector_char_t	*dim_x, *dim_y;
	int			offsets_x[OFFSET_ARRAY_SIZE], offsets_y[OFFSET_ARRAY_SIZE];

	result = zbx_db_select(
			"select screenitemid,screenid,resourcetype,resourceid,width,height,x,y,colspan,rowspan"
			",elements,style,url,max_columns from screens_items where screenid=" ZBX_FS_UI64,
			screenid);

	if (NULL == result)
		return FAIL;

	zbx_vector_ptr_create(&screen_items);
	DBpatch_init_dashboard(&dashboard, name, templateid);

	while (NULL != (row = zbx_db_fetch(result)))
	{
		scr_item = (zbx_db_screen_item_t*)zbx_calloc(NULL, 1, sizeof(zbx_db_screen_item_t));

		ZBX_DBROW2UINT64(scr_item->screenitemid, row[0]);
		ZBX_DBROW2UINT64(scr_item->screenid, row[1]);
		scr_item->resourcetype = atoi(row[2]);
		ZBX_DBROW2UINT64(scr_item->resourceid, row[3]);
		scr_item->width = atoi(row[4]);
		scr_item->height = atoi(row[5]);
		scr_item->x = atoi(row[6]);
		scr_item->y = atoi(row[7]);
		scr_item->colspan = atoi(row[8]);
		scr_item->rowspan = atoi(row[9]);
		scr_item->elements = atoi(row[10]);
		scr_item->style = atoi(row[11]);
		scr_item->url = zbx_strdup(NULL, row[12]);
		scr_item->max_columns = atoi(row[13]);

		if (0 == scr_item->colspan)
		{
			scr_item->colspan = 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: colspan is 0, converted to 1 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		if (0 == scr_item->rowspan)
		{
			scr_item->rowspan = 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: rowspan is 0, converted to 1 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		if (SCREEN_MAX_COLS <= scr_item->x)
		{
			scr_item->x = SCREEN_MAX_COLS - 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: x is more than %d, limited for item " ZBX_FS_UI64,
					scr_item->x, scr_item->screenitemid);
		}

		if (0 > scr_item->x)
		{
			scr_item->x = 0;
			zabbix_log(LOG_LEVEL_WARNING, "warning: x is negative, set to 0 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		if (SCREEN_MAX_ROWS <= scr_item->y)
		{
			scr_item->y = SCREEN_MAX_ROWS - 1;
			zabbix_log(LOG_LEVEL_WARNING, "warning: y is more than %d, limited for item " ZBX_FS_UI64,
					scr_item->y, scr_item->screenitemid);
		}

		if (0 > scr_item->y)
		{
			scr_item->y = 0;
			zabbix_log(LOG_LEVEL_WARNING, "warning: y is negative, set to 0 for item " ZBX_FS_UI64,
					scr_item->screenitemid);
		}

		DBpatch_trace_screen_item(scr_item);

		if (0 == DBpatch_is_convertible_screen_item(scr_item->resourcetype))
		{
			zabbix_log(LOG_LEVEL_WARNING, "discarding screen item " ZBX_FS_UI64
					" because it is not convertible", scr_item->screenitemid);
			DBpatch_screen_item_free(scr_item);
			continue;
		}

		zbx_vector_ptr_append(&screen_items, (void *)scr_item);
	}

	zbx_db_free_result(result);

	if (screen_items.values_num > 0)
	{
		zabbix_log(LOG_LEVEL_TRACE, "total %d screen items", screen_items.values_num);

		DBpatch_normalize_screen_items_pos(&screen_items);
		DBpatch_get_dashboard_dimensions(&screen_items, &dim_x, &dim_y);

		lw_array_debug("dim_x", dim_x);
		lw_array_debug("dim_y", dim_y);

		offsets_x[0] = 0;
		offsets_y[0] = 0;
		for (i = 1; i < OFFSET_ARRAY_SIZE; i++)
		{
			offsets_x[i] = -1;
			offsets_y[i] = -1;
		}

		for (i = 0; i < dim_x->values_num; i++)
		{
			if (POS_EMPTY != dim_x->values[i])
				offsets_x[i + 1] = i == 0 ? dim_x->values[i] : offsets_x[i] + dim_x->values[i];
			if (POS_EMPTY != dim_y->values[i])
				offsets_y[i + 1] = i == 0 ? dim_y->values[i] : offsets_y[i] + dim_y->values[i];
		}

		int_array_debug("offsets_x", offsets_x, OFFSET_ARRAY_SIZE, -1);
		int_array_debug("offsets_y", offsets_y, OFFSET_ARRAY_SIZE, -1);
	}

	ret = DBpatch_add_dashboard(&dashboard);

	for (i = 0; SUCCEED == ret && i < screen_items.values_num; i++)
	{
		int			offset_idx_x, offset_idx_y;
		zbx_db_widget_t		w;
		zbx_vector_ptr_t	widget_fields;
		zbx_db_screen_item_t	*si;

		si = screen_items.values[i];

		offset_idx_x = si->x + si->colspan;
		if (offset_idx_x > OFFSET_ARRAY_SIZE - 1)
		{
			offset_idx_x = OFFSET_ARRAY_SIZE - 1;
			zabbix_log(LOG_LEVEL_WARNING, "config error, x screen size overflow for item " ZBX_FS_UI64,
					si->screenitemid);
		}

		offset_idx_y = si->y + si->rowspan;
		if (offset_idx_y > OFFSET_ARRAY_SIZE - 1)
		{
			offset_idx_y = OFFSET_ARRAY_SIZE - 1;
			zabbix_log(LOG_LEVEL_WARNING, "config error, y screen size overflow for item " ZBX_FS_UI64,
					si->screenitemid);
		}

		memset((void *)&w, 0, sizeof(zbx_db_widget_t));
		w.x = offsets_x[si->x];
		w.y = offsets_y[si->y];
		w.width = offsets_x[offset_idx_x] - offsets_x[si->x];
		w.height = offsets_y[offset_idx_y] - offsets_y[si->y];

		/* skip screen items not fitting on the dashboard */
		if (w.x + w.width > DASHBOARD_MAX_COLS || w.y + w.height > DASHBOARD_MAX_ROWS)
		{
			zabbix_log(LOG_LEVEL_WARNING, "skipping screenitemid " ZBX_FS_UI64
					" (too wide, tall or offscreen)", si->screenitemid);
			continue;
		}

		zbx_vector_ptr_create(&widget_fields);

		DBpatch_widget_from_screen_item(si, &w, &widget_fields);

		ret = DBpatch_add_widget(dashboard.dashboardid, &w, &widget_fields);

		DBpatch_trace_widget(&w);

		zbx_vector_ptr_clear_ext(&widget_fields, (zbx_clean_func_t)DBpatch_widget_field_free);
		zbx_vector_ptr_destroy(&widget_fields);
		zbx_free(w.name);
		zbx_free(w.type);
	}

	zbx_free(dashboard.name);

	if (screen_items.values_num > 0)
	{
		lw_array_free(dim_x);
		lw_array_free(dim_y);
	}

	zbx_vector_ptr_clear_ext(&screen_items, (zbx_clean_func_t)DBpatch_screen_item_free);
	zbx_vector_ptr_destroy(&screen_items);

	return ret;
}

#undef OFFSET_ARRAY_SIZE

static int	DBpatch_5010044(void)
{
	zbx_db_result_t	result;
	zbx_db_row_t	row;
	int		ret = SUCCEED;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return ret;

	result = zbx_db_select("select screenid,name,templateid from screens where templateid is not null");

	while (SUCCEED == ret && NULL != (row = zbx_db_fetch(result)))
	{
		uint64_t	screenid, templateid;

		ZBX_DBROW2UINT64(screenid, row[0]);
		ZBX_DBROW2UINT64(templateid, row[2]);

		if (SUCCEED == (ret = DBpatch_convert_screen(screenid, row[1], templateid)))
			ret = DBpatch_delete_screen(screenid);
	}

	zbx_db_free_result(result);

	return ret;
}

#undef DASHBOARD_MAX_COLS
#undef DASHBOARD_MAX_ROWS
#undef DASHBOARD_WIDGET_MIN_ROWS
#undef DASHBOARD_WIDGET_MAX_ROWS

#undef SCREEN_MAX_ROWS
#undef SCREEN_MAX_COLS
#undef SCREEN_RESOURCE_CLOCK
#undef SCREEN_RESOURCE_GRAPH
#undef SCREEN_RESOURCE_SIMPLE_GRAPH
#undef SCREEN_RESOURCE_LLD_GRAPH
#undef SCREEN_RESOURCE_LLD_SIMPLE_GRAPH
#undef SCREEN_RESOURCE_PLAIN_TEXT
#undef SCREEN_RESOURCE_URL

#undef ZBX_WIDGET_FIELD_TYPE_INT32
#undef ZBX_WIDGET_FIELD_TYPE_STR
#undef ZBX_WIDGET_FIELD_TYPE_ITEM
#undef ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE
#undef ZBX_WIDGET_FIELD_TYPE_GRAPH
#undef ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE

/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH */
/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH */
/* #undef ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE */
/* #undef ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE */

#undef ZBX_WIDGET_TYPE_CLOCK
#undef ZBX_WIDGET_TYPE_GRAPH_CLASSIC
#undef ZBX_WIDGET_TYPE_GRAPH_PROTOTYPE
#undef ZBX_WIDGET_TYPE_PLAIN_TEXT
#undef ZBX_WIDGET_TYPE_URL
#undef POS_EMPTY
#undef POS_TAKEN
#undef SKIP_EMPTY

static int	DBpatch_5010045(void)
{
	return DBdrop_foreign_key("screens", 1);
}

static int	DBpatch_5010046(void)
{
	return DBdrop_field("screens", "templateid");
}

static int	DBpatch_5010047(void)
{
	return DBcreate_index("screens", "screens_1", "userid", 0);
}

static int	DBpatch_5010048(void)
{
#ifdef HAVE_MYSQL	/* fix automatic index name on MySQL */
	if (SUCCEED == zbx_db_index_exists("screens", "c_screens_3"))
	{
		return DBdrop_index("screens", "c_screens_3");
	}
#endif
	return SUCCEED;
}

static int	DBpatch_5010049(void)
{
	const zbx_db_table_t	table =
			{"item_parameter", "item_parameterid", 0,
				{
					{"item_parameterid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"itemid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value", "", NULL, NULL, 2048, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5010050(void)
{
	return DBcreate_index("item_parameter", "item_parameter_1", "itemid", 0);
}

static int	DBpatch_5010051(void)
{
	const zbx_db_field_t	field = {"itemid", NULL, "items", "itemid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("item_parameter", 1, &field);
}

static int	DBpatch_5010052(void)
{
	return DBdrop_field("config", "refresh_unsupported");
}

static int	DBpatch_5010053(void)
{
	const zbx_db_table_t	table =
			{"role", "roleid", 0,
				{
					{"roleid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"type", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"readonly", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5010054(void)
{
	return DBcreate_index("role", "role_1", "name", 1);
}

static int	DBpatch_5010055(void)
{
	const zbx_db_table_t	table =
			{"role_rule", "role_ruleid", 0,
				{
					{"role_ruleid", NULL, NULL, NULL, 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0},
					{"roleid", NULL, "role", "roleid", 0, ZBX_TYPE_ID, ZBX_NOTNULL, ZBX_FK_CASCADE_DELETE},
					{"type", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"name", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value_int", "0", NULL, NULL, 0, ZBX_TYPE_INT, ZBX_NOTNULL, 0},
					{"value_str", "", NULL, NULL, 255, ZBX_TYPE_CHAR, ZBX_NOTNULL, 0},
					{"value_moduleid", NULL, "module", "moduleid", 0, ZBX_TYPE_ID, 0, 0},
					{0}
				},
				NULL
			};

	return DBcreate_table(&table);
}

static int	DBpatch_5010056(void)
{
	return DBcreate_index("role_rule", "role_rule_1", "roleid", 0);
}

static int	DBpatch_5010057(void)
{
	return DBcreate_index("role_rule", "role_rule_2", "value_moduleid", 0);
}

static int	DBpatch_5010058(void)
{
	int		i;
	const char	*columns = "roleid,name,type,readonly";
	const char	*values[] = {
			"1,'User role',1,0",
			"2,'Admin role',2,0",
			"3,'Super admin role',3,1",
			"4,'Guest role',1,0",
			NULL
		};

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	for (i = 0; NULL != values[i]; i++)
	{
		if (ZBX_DB_OK > zbx_db_execute("insert into role (%s) values (%s)", columns, values[i]))
			return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5010059(void)
{
	int		i;
	const char	*columns = "role_ruleid,roleid,type,name,value_int,value_str,value_moduleid";
	const char	*values[] = {
			"1,1,0,'ui.default_access',1,'',NULL",
			"2,1,0,'modules.default_access',1,'',NULL",
			"3,1,0,'api.access',1,'',NULL",
			"4,1,0,'actions.default_access',1,'',NULL",
			"5,2,0,'ui.default_access',1,'',NULL",
			"6,2,0,'modules.default_access',1,'',NULL",
			"7,2,0,'api.access',1,'',NULL",
			"8,2,0,'actions.default_access',1,'',NULL",
			"9,3,0,'ui.default_access',1,'',NULL",
			"10,3,0,'modules.default_access',1,'',NULL",
			"11,3,0,'api.access',1,'',NULL",
			"12,3,0,'actions.default_access',1,'',NULL",
			"13,4,0,'ui.default_access',1,'',NULL",
			"14,4,0,'modules.default_access',1,'',NULL",
			"15,4,0,'api.access',0,'',NULL",
			"16,4,0,'actions.default_access',0,'',NULL",
			NULL
		};

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	for (i = 0; NULL != values[i]; i++)
	{
		if (ZBX_DB_OK > zbx_db_execute("insert into role_rule (%s) values (%s)", columns, values[i]))
			return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5010060(void)
{
	const zbx_db_field_t	field = {"roleid", NULL, "role", "roleid", 0, ZBX_TYPE_ID, 0, 0};

	return DBadd_field("users", &field);
}

static int	DBpatch_5010061(void)
{
	int	i;

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	for (i = 1; i <= 3; i++)
	{
		if (ZBX_DB_OK > zbx_db_execute("update users set roleid=%d where type=%d", i, i))
			return FAIL;
	}

	return SUCCEED;
}

static int	DBpatch_5010062(void)
{
	const zbx_db_field_t	field = {"roleid", NULL, "role", "roleid", 0, ZBX_TYPE_ID, ZBX_NOTNULL, 0};

	return DBset_not_null("users", &field);
}

static int	DBpatch_5010063(void)
{
	return DBdrop_field("users", "type");
}

static int	DBpatch_5010064(void)
{
	const zbx_db_field_t	field = {"roleid", NULL, "role", "roleid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("users", 1, &field);
}

static int	DBpatch_5010065(void)
{
	const zbx_db_field_t	field = {"roleid", NULL, "role", "roleid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("role_rule", 1, &field);
}

static int	DBpatch_5010066(void)
{
	const zbx_db_field_t	field = {"value_moduleid", NULL, "module", "moduleid", 0, 0, 0, ZBX_FK_CASCADE_DELETE};

	return DBadd_foreign_key("role_rule", 2, &field);
}

static int	DBpatch_5010067(void)
{
	int	i;

	/* 1 - USER TYPE / USER ROLE */
	/* 2 - ADMIN TYPE / ADMIN ROLE */
	/* 3 - SUPER ADMIN TYPE / SUPER ADMIN ROLE */
	const char	*values[] = {
			"1",
			"2",
			"3",
			NULL
		};

	if (0 == (DBget_program_type() & ZBX_PROGRAM_TYPE_SERVER))
		return SUCCEED;

	for (i = 0; NULL != values[i]; i++)
	{
		if (ZBX_DB_OK > zbx_db_execute("update profiles set value_id=%s,type=1,value_int=0 "
				"where idx='web.user.filter_type' and value_int=%s", values[i], values[i]))
		{
			return FAIL;
		}
	}

	/* -1 - ANY PROFILE */
	if (ZBX_DB_OK > zbx_db_execute("delete from profiles where idx='web.user.filter_type' and value_int=-1"))
		return FAIL;

	return SUCCEED;
}
#endif

DBPATCH_START(5010)

/* version, duplicates flag, mandatory flag */

DBPATCH_ADD(5010000, 0, 1)
DBPATCH_ADD(5010001, 0, 1)
DBPATCH_ADD(5010002, 0, 1)
DBPATCH_ADD(5010003, 0, 1)
DBPATCH_ADD(5010004, 0, 1)
DBPATCH_ADD(5010005, 0, 1)
DBPATCH_ADD(5010006, 0, 1)
DBPATCH_ADD(5010007, 0, 1)
DBPATCH_ADD(5010008, 0, 1)
DBPATCH_ADD(5010009, 0, 1)
DBPATCH_ADD(5010010, 0, 1)
DBPATCH_ADD(5010011, 0, 1)
DBPATCH_ADD(5010012, 0, 1)
DBPATCH_ADD(5010013, 0, 1)
DBPATCH_ADD(5010014, 0, 1)
DBPATCH_ADD(5010015, 0, 1)
DBPATCH_ADD(5010016, 0, 1)
DBPATCH_ADD(5010017, 0, 1)
DBPATCH_ADD(5010018, 0, 1)
DBPATCH_ADD(5010019, 0, 1)
DBPATCH_ADD(5010020, 0, 1)
DBPATCH_ADD(5010021, 0, 1)
DBPATCH_ADD(5010022, 0, 1)
DBPATCH_ADD(5010023, 0, 1)
DBPATCH_ADD(5010024, 0, 1)
DBPATCH_ADD(5010025, 0, 1)
DBPATCH_ADD(5010026, 0, 1)
DBPATCH_ADD(5010027, 0, 1)
DBPATCH_ADD(5010028, 0, 1)
DBPATCH_ADD(5010029, 0, 1)
DBPATCH_ADD(5010030, 0, 1)
DBPATCH_ADD(5010031, 0, 1)
DBPATCH_ADD(5010032, 0, 1)
DBPATCH_ADD(5010033, 0, 1)
DBPATCH_ADD(5010034, 0, 1)
DBPATCH_ADD(5010035, 0, 1)
DBPATCH_ADD(5010036, 0, 1)
DBPATCH_ADD(5010038, 0, 1)
DBPATCH_ADD(5010039, 0, 1)
DBPATCH_ADD(5010040, 0, 1)
DBPATCH_ADD(5010041, 0, 1)
DBPATCH_ADD(5010042, 0, 1)
DBPATCH_ADD(5010043, 0, 1)
DBPATCH_ADD(5010044, 0, 1)
DBPATCH_ADD(5010045, 0, 1)
DBPATCH_ADD(5010046, 0, 1)
DBPATCH_ADD(5010047, 0, 1)
DBPATCH_ADD(5010048, 0, 1)
DBPATCH_ADD(5010049, 0, 1)
DBPATCH_ADD(5010050, 0, 1)
DBPATCH_ADD(5010051, 0, 1)
DBPATCH_ADD(5010052, 0, 1)
DBPATCH_ADD(5010053, 0, 1)
DBPATCH_ADD(5010054, 0, 1)
DBPATCH_ADD(5010055, 0, 1)
DBPATCH_ADD(5010056, 0, 1)
DBPATCH_ADD(5010057, 0, 1)
DBPATCH_ADD(5010058, 0, 1)
DBPATCH_ADD(5010059, 0, 1)
DBPATCH_ADD(5010060, 0, 1)
DBPATCH_ADD(5010061, 0, 1)
DBPATCH_ADD(5010062, 0, 1)
DBPATCH_ADD(5010063, 0, 1)
DBPATCH_ADD(5010064, 0, 1)
DBPATCH_ADD(5010065, 0, 1)
DBPATCH_ADD(5010066, 0, 1)
DBPATCH_ADD(5010067, 0, 1)

DBPATCH_END()