typedef struct {
	void (*arrange)(Monitor *, int, int, int, int, int, int, int);
} LayoutArranger;

typedef struct {
	void (*arrange)(Monitor *, int, int, int, int, int, int, int, int, int);
} TileArranger;

static const LayoutArranger flexlayouts[] = {
	{ layout_no_split },
	{ layout_split_vertical },
	{ layout_split_horizontal },
	{ layout_split_centered_vertical },
	{ layout_split_centered_horizontal },
	{ layout_split_vertical_dual_stack },
	{ layout_split_horizontal_dual_stack },
	{ layout_floating_master },
	{ layout_split_vertical_fixed },
	{ layout_split_horizontal_fixed },
	{ layout_split_centered_vertical_fixed },
	{ layout_split_centered_horizontal_fixed },
	{ layout_split_vertical_dual_stack_fixed },
	{ layout_split_horizontal_dual_stack_fixed },
	{ layout_floating_master_fixed },
};

static const TileArranger flextiles[] = {
	{ arrange_top_to_bottom },
	{ arrange_left_to_right },
	{ arrange_monocle },
	{ arrange_gapplessgrid },
	{ arrange_gridmode },
	{ arrange_horizgrid },
	{ arrange_dwindle },
	{ arrange_spiral },
};

static float
getfactsforrange(Monitor *m, int an, int ai)
{
	int i;
	float facts;
	Client *c;

	facts = 0;
	for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
		if (i >= ai && i < (ai + an))
			#if CFACTS_PATCH
			facts += c->cfact;
			#else
			facts += 1;
			#endif // CFACTS_PATCH

	return facts;
}

static void
layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, n, 0);
}

static void
layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	/* Split master into master + stack if we have enough clients */
	if (m->nmaster && n > m->nmaster) {
		layout_split_vertical_fixed(m, x, y, h, w, ih, iv, n);
	} else {
		layout_no_split(m, x, y, h, w, ih, iv, n);
	}
}

static void
layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	int sw, sx;

	sw = (w - iv) * (1 - m->mfact);
	w = (w - iv) * m->mfact;
	if (m->ltaxis[LAYOUT] < 0) { // mirror
		sx = x;
		x += sw + iv;
	} else {
		sx = x + w + iv;
	}

	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0);
	(&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, n - m->nmaster, m->nmaster);
}

static void
layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	/* Split master into master + stack if we have enough clients */
	if (!m->nmaster || n <= m->nmaster) {
		layout_no_split(m, x, y, h, w, ih, iv, n);
	} else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) {
		layout_split_vertical(m, x, y, h, w, ih, iv, n);
	} else {
		layout_split_vertical_dual_stack_fixed(m, x, y, h, w, ih, iv, n);
	}
}

static void
layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	int sh, sw, sx, oy, sc;

	if (m->nstack)
		sc = m->nstack;
	else
		sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0);

	sw = (w - iv) * (1 - m->mfact);
	sh = (h - ih) / 2;
	w = (w - iv) * m->mfact;
	oy = y + sh + ih;
	if (m->ltaxis[LAYOUT] < 0) { // mirror
		sx = x;
		x += sw + iv;
	} else {
		sx = x + w + iv;
	}

	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0);
	(&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, sh, sw, ih, iv, n, sc, m->nmaster);
	(&flextiles[m->ltaxis[STACK2]])->arrange(m, sx, oy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc);
}

static void
layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	/* Split master into master + stack if we have enough clients */
	if (m->nmaster && n > m->nmaster) {
		layout_split_horizontal_fixed(m, x, y, h, w, ih, iv, n);
	} else {
		layout_no_split(m, x, y, h, w, ih, iv, n);
	}
}

static void
layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	int sh, sy;

	sh = (h - ih) * (1 - m->mfact);
	h = (h - ih) * m->mfact;
	if (m->ltaxis[LAYOUT] < 0) { // mirror
		sy = y;
		y += sh + ih;
	} else {
		sy = y + h + ih;
	}

	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0);
	(&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, n - m->nmaster, m->nmaster);
}

static void
layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	/* Split master into master + stack if we have enough clients */
	if (!m->nmaster || n <= m->nmaster) {
		layout_no_split(m, x, y, h, w, ih, iv, n);
	} else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) {
		layout_split_horizontal(m, x, y, h, w, ih, iv, n);
	} else {
		layout_split_horizontal_dual_stack_fixed(m, x, y, h, w, ih, iv, n);
	}
}

static void
layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	int sh, sy, ox, sc;

	if (m->nstack)
		sc = m->nstack;
	else
		sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0);

	sh = (h - ih) * (1 - m->mfact);
	h = (h - ih) * m->mfact;
	sw = (w - iv) / 2;
	ox = x + sw + iv;
	if (m->ltaxis[LAYOUT] < 0) { // mirror
		sy = y;
		y += sh + ih;
	} else {
		sy = y + h + ih;
	}

	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0);
	(&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, sw, ih, iv, n, sc, m->nmaster);
	(&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, sy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc);
}

static void
layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	/* Split master into master + stack if we have enough clients */
	if (!m->nmaster || n <= m->nmaster) {
		layout_no_split(m, x, y, h, w, ih, iv, n);
	} else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) {
		layout_split_vertical(m, x, y, h, w, ih, iv, n);
	} else {
		layout_split_centered_vertical_fixed(m, x, y, h, w, ih, iv, n);
	}
}

static void
layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	int sw, sx, ox, sc;

	if (m->nstack)
		sc = m->nstack;
	else
		sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0);

	sw = (w - 2*iv) * (1 - m->mfact) / 2;
	w = (w - 2*iv) * m->mfact;
	if (m->ltaxis[LAYOUT] < 0)  { // mirror
		sx = x;
		x += sw + iv;
		ox = x + w + iv;
	} else {
		ox = x;
		x += sw + iv;
		sx = x + w + iv;
	}

	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0);
	(&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, sc, m->nmaster);
	(&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, y, h, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc);
}

static void
layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	/* Split master into master + stack if we have enough clients */
	if (!m->nmaster || n <= m->nmaster) {
		layout_no_split(m, x, y, h, w, ih, iv, n);
	} else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) {
		layout_split_horizontal(m, x, y, h, w, ih, iv, n);
	} else {
		layout_split_centered_horizontal_fixed(m, x, y, h, w, ih, iv, n);
	}
}

static void
layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	int sh, sy, oy, sc;

	if (m->nstack)
		sc = m->nstack;
	else
		sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0);

	sh = (h - 2*ih) * (1 - m->mfact) / 2;
	h = (h - 2*ih) * m->mfact;
	if (m->ltaxis[LAYOUT] < 0)  { // mirror
		sy = y;
		y += sh + ih;
		oy = y + h + ih;
	} else {
		oy = y;
		y += sh + ih;
		sy = y + h + ih;
	}

	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0);
	(&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, sc, m->nmaster);
	(&flextiles[m->ltaxis[STACK2]])->arrange(m, x, oy, sh, w, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc);
}

static void
layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	/* Split master into master + stack if we have enough clients */
	if (!m->nmaster || n <= m->nmaster) {
		layout_no_split(m, x, y, h, w, ih, iv, n);
	} else {
		layout_floating_master_fixed(m, x, y, h, w, ih, iv, n);
	}
}

static void
layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n)
{
	int mh, mw;

	/* Draw stack area first */
	(&flextiles[m->ltaxis[STACK]])->arrange(m, x, y, h, w, ih, iv, n, n - m->nmaster, m->nmaster);

	if (w > h) {
		mw = w * m->mfact;
		mh = h * 0.9;
	} else {
		mw = w * 0.9;
		mh = h * m->mfact;
	}
	x += (w - mw) / 2;
	y += (h - mh) / 2;

	(&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, mh, mw, ih, iv, n, m->nmaster, 0);
}

static void
arrange_left_to_right(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	int i;
	float facts, fact = 1;
	Client *c;

	w -= iv * (an - 1);
	facts = getfactsforrange(m, an, ai);
	for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
		if (i >= ai && i < (ai + an)) {
			#if CFACTS_PATCH
			fact = c->cfact;
			#endif // CFACTS_PATCH
			resize(c, x, y, w * (fact / facts) - (2*c->bw), h - (2*c->bw), 0);
			x += WIDTH(c) + iv;
		}
	}
}

static void
arrange_top_to_bottom(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	int i;
	float facts, fact = 1;
	Client *c;

	h -= ih * (an - 1);
	facts = getfactsforrange(m, an, ai);
	for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
		if (i >= ai && i < (ai + an)) {
			#if CFACTS_PATCH
			fact = c->cfact;
			#endif // CFACTS_PATCH
			resize(c, x, y, w - (2*c->bw), h * (fact / facts) - (2*c->bw), 0);
			y += HEIGHT(c) + ih;
		}
	}
}

static void
arrange_monocle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	int i;
	Client *c;

	for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
		if (i >= ai && i < (ai + an))
			resize(c, x, y, w - (2*c->bw), h - (2*c->bw), 0);
}

static void
arrange_gridmode(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	int i, cols, rows, ch, cw, cx, cy; // counters
	Client *c;

	/* grid dimensions */
	for (rows = 0; rows <= an/2; rows++)
		if (rows*rows >= an)
			break;
	cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows;

	/* window geoms (cell height/width) */
	ch = (h - ih * (rows - 1)) / (rows ? rows : 1);
	cw = (w - iv * (cols - 1)) / (cols ? cols : 1);
	for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
		if (i >= ai && i < (ai + an)) {
			cx = x + ((i - ai) / rows) * (cw + iv);
			cy = y + ((i - ai) % rows) * (ch + ih);
			resize(c, cx, cy, cw - 2*c->bw, ch - 2*c->bw, False);
		}
	}
}

static void
arrange_horizgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	int ntop, nbottom, i;
	Client *c;

	/* Exception when there is only one client; don't split into two rows */
	if (an == 1) {
		arrange_monocle(m, x, y, h, w, ih, iv, n, an, ai);
		return;
	}

	ntop = an / 2;
	nbottom = an - ntop;
	for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
		if (i >= ai && i < (ai + an)) {
			if ((i - ai) < ntop)
				resize(
					c,
					x + (i - ai) * ((w - iv*(ntop - 1)) / ntop + iv),
					y,
					(w - iv*(ntop - 1)) / ntop - (2*c->bw),
					(h - ih) / 2 - (2*c->bw),
					False
				);
			else
				resize(
					c,
					x + (i - ai - ntop) * ((w - iv*(nbottom - 1)) / nbottom + iv),
					y + ih + (h - ih) / 2,
					(w - iv*(nbottom - 1)) / nbottom - (2*c->bw),
					(h - ih) / 2 - (2*c->bw),
					False
				);
		}
	}
}

static void
arrange_gapplessgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	int i, cols, rows, cn, rn, cc; // counters
	Client *c;

	/* grid dimensions */
	for (cols = 1; cols <= an/2; cols++)
		if (cols*cols >= an)
			break;
	if (an == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
		cols = 2;
	rows = an/cols;
	cn = rn = cc = 0; // reset cell no, row no, client count

	for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
		if (i >= ai && i < (ai + an)) {
 			if (cc/rows + 1 > cols - an%cols)
				rows = an/cols + 1;
			resize(c,
				x + cn*((w - iv*(cols - 1)) / cols + iv),
				y + rn*((h - ih*(rows - 1)) / rows + ih),
				(w - iv*(cols - 1)) / cols,
				(h - ih*(rows - 1)) / rows,
				0);
			rn++;
			cc++;
			if (rn >= rows) {
				rn = 0;
				cn++;
			}
		}
	}
}

static void
arrange_fibonacci(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai, int s)
{
	int i, j, nx = x, ny = y, nw = w, nh = h, r = 1;
	Client *c;

	for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) {
		if (j >= ai && j < (ai + an)) {
			if (r) {
				if ((i % 2 && ((nh - ih) / 2) <= (20 + 2*c->bw)) || (!(i % 2) && ((nw - iv) / 2) <= (20 + 2*c->bw))) {
					r = 0;
				}
				if (r && i < an - 1) {
					if (i % 2)
						nh = (nh - ih) / 2;
					else
						nw = (nw - iv) / 2;

					if ((i % 4) == 2 && !s)
						nx += nw + iv;
					else if ((i % 4) == 3 && !s)
						ny += nh + ih;
				}
				if ((i % 4) == 0) {
					if (s)
						ny += nh + ih;
					else
						ny -= nh + ih;
				}
				else if ((i % 4) == 1)
					nx += nw + iv;
				else if ((i % 4) == 2)
					ny += nh + ih;
				else if ((i % 4) == 3) {
					if (s)
						nx += nw + iv;
					else
						nx -= nw + iv;
				}
				if (i == 0)	{
					if (an != 1)
						nw = (w - iv) * m->mfact;
					ny = y;
				}
				else if (i == 1)
					nw = w - nw - iv;
				i++;
			}
			resize(c, nx, ny, nw - 2 * c->bw, nh - 2 * c->bw, False);
		}
	}
}

static void
arrange_dwindle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 1);
}

static void
arrange_spiral(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai)
{
	arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 0);
}

static void
flextile(Monitor *m)
{
	unsigned int n;
	int oh = 0, ov = 0, ih = 0, iv = 0; // gaps outer/inner horizontal/vertical

	#if VANITYGAPS_PATCH
	getgaps(m, &oh, &ov, &ih, &iv, &n);
	#else
	Client *c;
	for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
	#endif // VANITYGAPS_PATCH

	if (m->lt[m->sellt]->preset.layout != m->ltaxis[LAYOUT] ||
			m->lt[m->sellt]->preset.masteraxis != m->ltaxis[MASTER] ||
			m->lt[m->sellt]->preset.stack1axis != m->ltaxis[STACK] ||
			m->lt[m->sellt]->preset.stack2axis != m->ltaxis[STACK2])
		setflexsymbols(m, n);
	else if (m->lt[m->sellt]->preset.symbolfunc != NULL)
		m->lt[m->sellt]->preset.symbolfunc(m, n);

	if (n == 0)
		return;

	#if VANITYGAPS_PATCH
	/* No outer gap if full screen monocle */
	if (abs(m->ltaxis[MASTER]) == MONOCLE && (abs(m->ltaxis[LAYOUT]) == NO_SPLIT || n <= m->nmaster)) {
		oh = 0;
		ov = 0;
	}
	#endif // VANITYGAPS_PATCH

	(&flexlayouts[abs(m->ltaxis[LAYOUT])])->arrange(m, m->wx + ov, m->wy + oh, m->wh - 2*oh, m->ww - 2*ov, ih, iv, n);
	return;
}

static void
setflexsymbols(Monitor *m, unsigned int n)
{
	int l;
	char sym1, sym2, sym3;
	Client *c;

	if (n == 0)
		for (c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);

	l = abs(m->ltaxis[LAYOUT]);
	if (m->ltaxis[MASTER] == MONOCLE && (l == NO_SPLIT || !m->nmaster || n <= m->nmaster)) {
		monoclesymbols(m, n);
		return;
	}

	if (m->ltaxis[STACK] == MONOCLE && (l == SPLIT_VERTICAL || l == SPLIT_HORIZONTAL_FIXED)) {
		decksymbols(m, n);
		return;
	}

	/* Layout symbols */
	if (l == NO_SPLIT || !m->nmaster) {
		sym1 = sym2 = sym3 = (int)tilesymb[m->ltaxis[MASTER]];
	} else {
		sym2 = layoutsymb[l];
		if (m->ltaxis[LAYOUT] < 0) {
			sym1 = tilesymb[m->ltaxis[STACK]];
			sym3 = tilesymb[m->ltaxis[MASTER]];
		} else {
			sym1 = tilesymb[m->ltaxis[MASTER]];
			sym3 = tilesymb[m->ltaxis[STACK]];
		}
	}

	snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym1, sym2, sym3);
}

static void
monoclesymbols(Monitor *m, unsigned int n)
{
	if (n > 0)
		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
	else
		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[M]");
}

static void
decksymbols(Monitor *m, unsigned int n)
{
	if (n > m->nmaster)
		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[]%d", n);
	else
		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[D]");
}

/* Mirror layout axis for flextile */
void
mirrorlayout(const Arg *arg)
{
	if (!selmon->lt[selmon->sellt]->arrange)
		return;
	selmon->ltaxis[LAYOUT] *= -1;
	#if PERTAG_PATCH
	selmon->pertag->ltaxis[selmon->pertag->curtag][0] = selmon->ltaxis[LAYOUT];
	#endif // PERTAG_PATCH
	arrange(selmon);
}

/* Rotate layout axis for flextile */
void
rotatelayoutaxis(const Arg *arg)
{
	if (!selmon->lt[selmon->sellt]->arrange)
		return;
	if (arg->i == 0) {
		if (selmon->ltaxis[LAYOUT] >= 0)
			selmon->ltaxis[LAYOUT] = selmon->ltaxis[LAYOUT] + 1 >= LAYOUT_LAST ? 0 : selmon->ltaxis[LAYOUT] + 1;
		else
			selmon->ltaxis[LAYOUT] = selmon->ltaxis[LAYOUT] - 1 <= -LAYOUT_LAST ? -0 : selmon->ltaxis[LAYOUT] - 1;
	} else
		selmon->ltaxis[arg->i] = selmon->ltaxis[arg->i] + 1 >= AXIS_LAST ? 0 : selmon->ltaxis[arg->i] + 1;
	#if PERTAG_PATCH
	selmon->pertag->ltaxis[selmon->pertag->curtag][arg->i] = selmon->ltaxis[arg->i];
	#endif // PERTAG_PATCH
	arrange(selmon);
	setflexsymbols(selmon, 0);
}

void
incnstack(const Arg *arg)
{
	#if PERTAG_PATCH
	selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag] = MAX(selmon->nstack + arg->i, 0);
	#else
	selmon->nstack = MAX(selmon->nstack + arg->i, 0);
	#endif // PERTAG_PATCH
	arrange(selmon);
}