step1_rearrange = function(tmel) {
	message_init()
	dev = getOption("tmap.devel.mode")

	# get options (mode specific)
	o = tmap_options_mode()

	# reset symbols
	assign("shapeLib", list(), envir = .TMAP)
	assign("justLib", list(), envir = .TMAP)

	# find shape, (aesthetic) layer, facet, and other elements
	is_tms = vapply(tmel, inherits, "tm_shape", FUN.VALUE = logical(1))
	is_tml = vapply(tmel, inherits, "tm_layer", FUN.VALUE = logical(1))
	is_tmf = vapply(tmel, inherits, "tm_facets", FUN.VALUE = logical(1))
	is_other = !is_tml & !is_tms & !is_tmf

	is_aux = vapply(tmel, inherits, "tm_aux_layer", FUN.VALUE = logical(1))
	is_proxy = vapply(tmel, inherits, "tm_proxy", FUN.VALUE = logical(1))

	# find layer id numbers (needed to plot layers in correct order, which is not trivial due to the two layer types)
	lay_id = cumsum(is_tml | is_aux)
	lay_id[!is_tml & !is_aux] = 0


	if (any(is_proxy)) {
		prx = tmel[is_proxy]
		proxy_z = lapply(prx, function(px) px$zindex)
	} else {
		prx = list()
		proxy_z = numeric(0)
	}

	# remove layers from q (via tm_remove_layer)
	# and determine highest pane number (400 default)
	if (.TMAP$proxy) {
		q = .TMAP$q
		.TMAP$pane_ids = setdiff(unique(c(.TMAP$pane_ids, q$lid)), 0)
		q = q[!(q$lid %in% proxy_z), ]
		.TMAP$q = q
		if (any(q$lid != 0)) {
			.TMAP$start_pane_id = max(q$lid)
		} else {
			.TMAP$start_pane_id = 400
		}
	} else {
		.TMAP$start_pane_id = 400
		.TMAP$pane_ids = NULL
	}

	# create groups, for each group: tms (tmap shape), tmls (tmap layers), tmf (tmap facets)
	ids = cumsum(is_tms)
	ids[is_other] = 0L
	tmel_spl = split(tmel, f = ids)
	lay_id_spl = split(lay_id, f = ids)
	if (any(is_other)) {
		oth = tmel_spl[[1]]
		oth_lay_id = lay_id_spl[[1]]
		tmel_spl = tmel_spl[-1]
		lay_id_spl = lay_id_spl[-1]
	} else {
		oth = list()
		oth_lay_id = list()
	}


	# warning("v3 code detected")
	# args$v3 = NULL
	# names(args) = paste0("legend.", names(args))
	# do.call(tm_options, args)
	#

	# update options with tm_option elements
	#sapply(oth, inherits, "tm_legend")

	is_opt = sapply(oth, inherits, "tm_options")
	if (any(is_opt)) for (id in which(is_opt)) {
		o2 = oth[[id]]

		cls = class(o2)[1]
		if (substr(cls, nchar(cls)-2,nchar(cls)) == "_v3") cls = substr(cls, 1, nchar(cls) - 3)
		if ("v3_tm_legend" %in% o2$calls) v3_tm_legend_general(fun = cls)

		# special case: position, in case c("left", "top") is used
		pids = grep(".position", names(o2), fixed = TRUE)

		if (length(pids) > 0L) {
			for (i in 1L:length(pids)) {
				if (is.character(o2[[pids[i]]]) || is.numeric(o2[[pids[i]]])) o2[[pids[i]]] = tm_pos_in(o2[[pids[i]]][1], o2[[pids[i]]][2])
			}
		}

		if ("style" %in% names(o2) && !is.na(o2$style)) { #() {
			o = tmap_options_mode(default.options = TRUE)
			styleOptions <- get("tmapStyles", envir = .TMAP)[[o2$style]]
			if (!is.null(styleOptions)) o = complete_options(styleOptions, o)
			o2$style = NULL
		}
		o = complete_options(o2, o)
	}
	o = preprocess_meta_step1(o)


	any_data_layer = length(tmel_spl) > 0L

	if (any_data_layer) {
		# organize groups, 1 tm_shape, at least 1 tm_layers, 1 tm_facets
		tmo = mapply(function(tmg, lid) {
			is_tms = vapply(tmg, inherits, "tm_shape", FUN.VALUE = logical(1))
			is_tml = vapply(tmg, inherits, "tm_layer", FUN.VALUE = logical(1))
			is_tmf = vapply(tmg, inherits, "tm_facets", FUN.VALUE = logical(1))

			# make sure there is exactly one tm_facets per group (if there are none, add one, if there are mutple, take last)
			if (!any(is_tmf)) {
				tmf = tm_facets()[[1]]
				tmf$calls = NULL
			} else {
				# get last tm_facets element
				k = sum(is_tmf)
				if (k < 1) warning("Multiple tm_facets defined per layer group. Only the last one is processed", call. = FALSE)
				tmf = tmg[[which(is_tmf)[k]]]
			}

			# extract layers and add layer id number
			tmls = mapply(function(l, i) {
				l$lid = if (is.na(l$zindex)) i + .TMAP$start_pane_id else l$zindex
				l
			}, tmg[is_tml], lid[is_tml], SIMPLIFY = FALSE)

			structure(list(tms = tmg[[1]], tmls = tmls, tmf = tmf), class = c("tmapGroup", "list"))
		}, tmel_spl, lay_id_spl, SIMPLIFY = FALSE)

		tmo = step1_rearrange_facets(tmo, o) # save smeta's and keep track of group id (to obtain smeta)
		tmf = tmo$tmf_global # global facetting options, to be appended to options o
		tmo$tmf_global = NULL

		# find the 'main' group: this is the group for which tm_shape is used for CRS and bbox. By default, take the first, unless is.main is set to TRUE.
		# is.main can be set multiple times: the CRS will be taken from the first, but the bbox from all
		ids = get_main_ids(tmo)

		tms = tmo[[ids[1]]]$tms




	} else {
		tmo = NULL
		tmf = tm_facets()[[1]]
		tmf$calls = NULL
		tmf$fn = c(1, 1, 1)
		tmf$n = 1
		tmf$fl = list(NULL, NULL, NULL)
		tmf$type = "wrap"
		ids = 0

	}

	# crs in options refers to which crs is used in the plot, not necessarily in the transformation (step 3)
	crs_option = o$crs

	# get main crs (used in step 3, not necessarily in the plot (e.g. view mode will use 4326/3857))
	crs_main = if (any_data_layer) get_crs(tms) else NA

	if (inherits(crs_option, "leaflet_crs")) {
		crs_leaflet = crs_option
		crs = leaflet2crs(crs_leaflet)
	} else if (any_data_layer && (is.na(crs_option[1]) || (is.numeric(crs_option) && crs_option == 0))) {
		crs = crs_main
		crs_leaflet = leafletSimple
	} else if (is.na(crs_option[1])) {
		crs = sf::st_crs(4326)
		crs_leaflet = crs2leaflet(get_option_class(crs, "dimensions"))
	} else {
		crs = crs_option
		crs_leaflet = crs2leaflet(get_option_class(crs, "dimensions"))
	}

	if (any_data_layer) {
		main_class = get_class(tms)
	} else {
		main_class = "stars" # basemaps
	}

	if (dev) timing_add(s2 = "facet meta")


	# add basemaps
	if (o$basemap.show && !.TMAP$proxy) {
		if (!any(vapply(oth, inherits, "tm_basemap", FUN.VALUE = logical(1)))) {
			oth = c(oth, tm_basemap())
			oth_lay_id = c(oth_lay_id, 0L)
		}
	}

	is_aux = vapply(oth, inherits, "tm_aux_layer", FUN.VALUE = logical(1))

	if (any(is_aux)) {

		aux = mapply(function(l, i) {
			l$lid = if (is.na(l$zindex)) i + .TMAP$start_pane_id else l$zindex

			cls = class(l)[1]
			ot = get_prefix_opt(class = cls, o = o)
			l$args = complete_options(l$args, ot)
			l
		}, oth[is_aux], oth_lay_id[is_aux], SIMPLIFY = FALSE)
	} else {
		aux = list()
	}


	is_comp = sapply(oth, inherits, "tm_component")
	if (any(is_comp)) {
		cmp = oth[is_comp]
		cmp = lapply(cmp, impute_comp, o = o)
	} else {
		cmp = list()
	}


	# to be used later
	o$main = ids # to determine total bounding box in step 4
	o$main_class = main_class # inner.margins are class specific (preprecess_meta)
	o$crs = crs # in step 3, when other shapes are transformed to this crs
	o$crs_leaflet = crs_leaflet
	o$crs_main = crs_main


	o = c(o, tmf)
	# process shapes: put non-spatial data in data.table, keep spatial data separately


	# disable s2 in case earth.boundaries are drawn
	if (o$earth.boundary && sf::sf_use_s2()) {
		suppressMessages(sf::sf_use_s2(FALSE))
		.TMAP$set_s2 = TRUE
	}

	if (any_data_layer) {
		tmo = structure(lapply(tmo, function(tmg) {
			tmg$tms = do.call(tmapShape, c(tmg$tms, list(o = o, tmf = tmg$tmf)))
			tmg
		}), names = paste0("group", seq_len(length(tmo))), class = c("tmapObject", "list"))
	}

	if (dev) timing_add(s2 = "prep shape")

	if (any_data_layer && !is.null(tms$unit)) o$unit = tms$unit

	list(tmo = tmo, aux = aux, cmp = cmp, prx = prx, o = o)
}

# see above
get_main_ids = function(tmo) {
	is_main = vapply(tmo, function(tmg) {
		identical(tmg$tms$is.main, TRUE)
	}, FUN.VALUE = logical(1), USE.NAMES = FALSE)

	if (any(is_main)) which(is_main) else 1L
}


get_crs = function(tms) {
	if (is.null(tms$crs)) sf::st_crs(tms$shp) else tms$crs
}

get_class = function(tms) {
	class(tms$shp)
}

impute_comp = function(a, o) {
	cls = class(a)[1]

	ot = get_prefix_opt(class = cls, o = o)

	ca = class(a)

	call = names(a)

	# special case: position, in case c("left", "top") is used
	if (is.character(a$position)) a$position = str2pos(a$position)
	if (is.numeric(a$position)) a$position = num2pos(a$position)
	if (inherits(a$position, "tm_pos")) {
		a$position = complete_options(a$position, o$component.position[[a$position$type]])
		if (a$position$type %in% c("autoin", "autoout")) message_pos_auto(a$position$type)
	}


	a = complete_options(a, ot)
	a$call = call

	class(a) = ca
	a
}


