module ugui;


fn void! Ctx.layout_set_row(&ctx)
{
	Id parent_id = ctx.tree.get(ctx.active_div)!;
	Elem *parent = ctx.cache.search(parent_id)!;

	if (parent.type != ETYPE_DIV) {
		// what?
		return UgError.UNEXPECTED_ELEMENT?;
	}

	parent.div.layout = LAYOUT_ROW;
}

fn void! Ctx.layout_set_column(&ctx)
{
	Id parent_id = ctx.tree.get(ctx.active_div)!;
	Elem *parent = ctx.cache.search(parent_id)!;

	if (parent.type != ETYPE_DIV) {
		// what?
		return UgError.UNEXPECTED_ELEMENT?;
	}

	parent.div.layout = LAYOUT_COLUMN;
}

fn void! Ctx.layout_set_floating(&ctx)
{
	Id parent_id = ctx.tree.get(ctx.active_div)!;
	Elem *parent = ctx.cache.search(parent_id)!;

	if (parent.type != ETYPE_DIV) {
		// what?
		return UgError.UNEXPECTED_ELEMENT?;
	}

	parent.div.layout = LAYOUT_FLOATING;
}

fn void! Ctx.layout_next_row(&ctx)
{
	Id parent_id = ctx.tree.get(ctx.active_div)!;
	Elem *parent = ctx.cache.search(parent_id)!;

	if (parent.type != ETYPE_DIV) {
		// what?
		return UgError.UNEXPECTED_ELEMENT?;
	}

	parent.div.origin_r = Point{
		.x = parent.bounds.x,
		.y = parent.div.origin_c.y,
	};
	parent.div.origin_c = parent.div.origin_r;
}

fn void! Ctx.layout_next_column(&ctx)
{
	Id parent_id = ctx.tree.get(ctx.active_div)!;
	Elem *parent = ctx.cache.search(parent_id)!;

	if (parent.type != ETYPE_DIV) {
		// what?
		return UgError.UNEXPECTED_ELEMENT?;
	}

	parent.div.origin_c = Point{
		.x = parent.div.origin_r.x,
		.y = parent.bounds.y,
	};
	parent.div.origin_r = parent.div.origin_c;
}

// position the rectangle inside the parent according to the layout
// parent: parent div
// rect: the requested size
// style: apply style
<*
@require ctx != null
@require parent.type == ETYPE_DIV
*>
fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
{
	Rect placement;
	Point origin;
	ElemDiv* div = &parent.div;

	// 1. Select the right origin
	switch (div.layout) {
	case LAYOUT_ROW:
		origin = div.origin_r;
	case LAYOUT_COLUMN:
		origin = div.origin_c;
	case LAYOUT_FLOATING: // none
	default:
		// Error
	}

	// the bottom-right border of the element box
	Point pl_corner;

	// 2. Calculate the placement
	placement.x = (short)max(origin.x + rect.x, 0);
	placement.y = (short)max(origin.y + rect.y, 0);
	placement.w = rect.w > 0 ? rect.w : (short)max(parent.bounds.w - (placement.x - parent.bounds.x), 0);
	placement.h = rect.h > 0 ? rect.h : (short)max(parent.bounds.h - (placement.y - parent.bounds.y), 0);

	pl_corner.x = placement.x + placement.w;
	pl_corner.y = placement.y + placement.h;

	// 2.1 apply style, css box model
	if (style) {
		Rect margin = ctx.style.margin;
		Rect border = ctx.style.border;
		Rect padding = ctx.style.padding;

		placement.x += margin.x;
		placement.y += margin.y;
		if (rect.w != 0) { placement.w += border.x+border.w + padding.x+padding.w; }
		if (rect.h != 0) { placement.h += border.y+border.h + padding.y+padding.h; }

		pl_corner.x = placement.x + placement.w + margin.w;
		pl_corner.y = placement.y + placement.h + margin.h;
	}

	// 3. Update the origins of the parent
	div.origin_r = Point{
		.x = pl_corner.x,
		.y = origin.y,
	};
	div.origin_c = Point{
		.x = origin.x,
		.y = pl_corner.y,
	};

	// 4. Calculate the "scrolled" view
	Point off;
	Rect* cb = &div.children_bounds;
	if (div.scroll.can_x && div.scroll.on_x) {
		off.x = (short)((float)(div.pcb.w - parent.bounds.w) * div.scroll.value_x);
	}
	if (div.scroll.can_y && div.scroll.on_y) {
		off.y = (short)((float)(div.pcb.h - parent.bounds.h) * div.scroll.value_y);
	}
	Rect view = {
		.x = parent.bounds.x + off.x,
		.y = parent.bounds.y + off.y,
		.w = parent.bounds.w,
		.h = parent.bounds.h,
	};

	// 5. check if the placement overflows the children ounds, if so update them
	if (!point_in_rect(pl_corner, *cb)) {
		if (pl_corner.y > cb.y+cb.h) {
			cb.h = pl_corner.y - cb.y;
		}
		if (pl_corner.x > cb.x+cb.w) {
			cb.w += pl_corner.x - (cb.x + cb.w);
		}
	}

	// 6. check if the placement is inside the view
	if (placement.collides(view)) {
		return Rect{
			.x = placement.x - off.x,
			.y = placement.y - off.y,
			.w = placement.w,
			.h = placement.h,
		};
	} else {
		return Rect{};
	}
}