/*
 * Set predefined flextile layout.
 *
 * The arg int value is a binary representation of the setup where certain bits have different
 * meanings, similar to how Linux permissions work.
 *
 * The first two bits represents the stack axis, bits 3 and 4 the master axis. Bits 5 and 6
 * are used to control the layout while bit 7 indicates whether or not the layout is mirrored.
 * The 8th bit is reserved while bit 9 through 12 control nmaster with up to 15 clients in the
 * master stack.
 *
 * Bitwise layout:
 *
 *    0000          (nmaster: 0-15 = clients in master stack)
 *        0         (reserved)
 *         0        (orientation: 0 = normal, 1 = mirror)
 *          00      (layout: 00 = vertical, 01 = horizontal, 10 = centered (vert), 11 = centered (horz))
 *            00    (master axis: 00 = left to right, 01 = top to bottom, 10 = monocle, 11 = grid)
 *              00  (stack axis:  00 = left to right, 01 = top to bottom, 10 = monocle, 11 = grid)
 *
 * Examples:
 *    binary         int  layout
 *    --------------------------
 *    000000000110     6  monocle
 *    000100000110   262  deck layout
 *    000100010000   272  bstack layout
 *    000100010001   273  bstackhoriz layout
 *    000000000111     7  grid layout
 *    000100000101   261  default tile layout
 *    000100100101   293  centered master
 *    000100000111   263  default tile layout with grid stack
 *    000100000001   257  columns (col) layout
 */
void
setflexlayout(const Arg *arg)
{
	int i;

	/* Find flextile layout */
	for (i = 0; i < LENGTH(layouts); i++)
		if (layouts[i].arrange == flextile)
			break;

	selmon->nmaster = ((arg->i & 0x0F00) >> 8);
	selmon->ltaxis[0] = (1 + ((arg->i & 0x30) >> 4)) * (arg->i & 0x40 ? -1 : 1);
	selmon->ltaxis[1] = 1 + ((arg->i & 0xC) >> 2);
	selmon->ltaxis[2] = 1 + (arg->i & 0x3);

	#if PERTAG_PATCH
	selmon->pertag->nmasters[selmon->pertag->curtag] = selmon->nmaster;
	selmon->pertag->ltaxes[selmon->pertag->curtag][0] = selmon->ltaxis[0];
	selmon->pertag->ltaxes[selmon->pertag->curtag][1] = selmon->ltaxis[1];
	selmon->pertag->ltaxes[selmon->pertag->curtag][2] = selmon->ltaxis[2];
	#endif

	setlayout(&((Arg) { .v = &layouts[i] }));
}

#if VANITYGAPS_PATCH
static void
flextile(Monitor *m)
{
	unsigned int i, n, nc = 0, sc = 0, lt, cn = 0, rn = 0, cc = 0; // counters
	int cols = 1, rows = 1;
	int x, y, h, w;     // master x, y, height, width
	int sx, sy, sh, sw; // stack x, y, height, width
	int ox, oy;         // other stack x, y (centered layout)
	int oh, ov, ih, iv; // gaps outer/inner horizontal/vertical

	float facts, sfacts, ofacts;
	Client *c;

	getgaps(m, &oh, &ov, &ih, &iv, &n);
	setflexsymbols(m, n);

	if (n == 0)
		return;

	/* No outer gap if full screen monocle */
	if ((!m->nmaster && m->ltaxis[STACK] == MONOCLE) || (n <= m->nmaster && m->ltaxis[MASTER] == MONOCLE)) {
		ox = sx = x = m->wx;
		oy = sy = y = m->wy;
		sh = h = m->wh;
		sw = w = m->ww;
	} else {
		ox = sx = x = m->wx + ov;
		oy = sy = y = m->wy + oh;
		sh = h = m->wh - 2*oh;
		sw = w = m->ww - 2*ov;
	}
	sc = n - m->nmaster;

	#if CFACTS_PATCH
	getfacts(m, &facts, &sfacts);
	ofacts = sfacts;
	#else
	facts = MIN(n, m->nmaster);
	ofacts = sfacts = sc;
	#endif // CFACTS_PATCH

	/* Split master into master + stack if we have enough clients */
	if (m->nmaster && n > m->nmaster) {
		if (abs(m->ltaxis[LAYOUT]) == SPLIT_VERTICAL
				|| (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_V && n == m->nmaster + 1)) {
			sw = (w - iv) * (1 - m->mfact);
			w = (w - iv) * m->mfact;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				x = sx + sw + iv;
			else
				sx = x + w + iv;
		} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_HORIZONTAL
				|| (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H && n == m->nmaster + 1)) {
			sh = (h - ih) * (1 - m->mfact);
			h = (h - ih) * m->mfact;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				y = sy + sh + ih;
			else
				sy = y + h + ih;
		} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_V) {
			sw = (w - 2*iv) * (1 - m->mfact) / 2;
			w = (w - 2*iv) * m->mfact;
			x = sx + sw + iv;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				ox = x + w + iv;
			else
				sx = x + w + iv;
		} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H) {
			sh = (h - 2*ih) * (1 - m->mfact) / 2;
			h = (h - 2*ih) * m->mfact;
			y = sy + sh + ih;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				oy = y + h + ih;
			else
				sy = y + h + ih;
		}

		if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_V || abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H) {
			sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0);
			facts = sfacts = ofacts = 0;
			for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
				#if CFACTS_PATCH
				if (i < m->nmaster)
					facts += c->cfact; // total factor of master area
				else if (sc && i < m->nmaster + sc)
					sfacts += c->cfact; // total factor of first stack area
				else
					ofacts += c->cfact; // total factor of second stack area
				#else
				if (i < m->nmaster)
					facts += 1;
				else if (sc && i < m->nmaster + sc)
					sfacts += 1;
				else
					ofacts += 1;
				#endif // CFACTS_PATCH
			}
		}
	}

	for (i = 0, lt = MASTER, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
		if (i == 0 || (m->nmaster && i == m->nmaster) || i == (m->nmaster + sc)) {
			nc = MIN(n, m->nmaster);
			if (!m->nmaster || i == m->nmaster) { // switch to stack area
				x = sx, y = sy, h = sh, w = sw, facts = sfacts, lt = STACK;
				nc = sc;
			} else if (i > 0 && i == (m->nmaster + sc)) { // switch to second stack area
				x = ox, y = oy, h = sh, w = sw, nc = n - i, facts = ofacts;
			}

			if (m->ltaxis[lt] == LEFT_TO_RIGHT)
				w -= iv * (nc - 1);
			else if (m->ltaxis[lt] == TOP_TO_BOTTOM)
				h -= ih * (nc - 1);
			else if (m->ltaxis[lt] == GRID) {
				/* grid dimensions */
				for (cols = 1; cols <= nc/2; cols++)
					if (cols*cols >= nc)
						break;
				if (nc == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
					cols = 2;
				rows = nc/cols;
				cn = rn = cc = 0; // reset cell no, row no, client count
			}
		}

		if (m->ltaxis[lt] == LEFT_TO_RIGHT) {
			#if CFACTS_PATCH
			resize(c, x, y, w * (c->cfact / facts) - (2*c->bw), h - (2*c->bw), 0);
			#else
			resize(c, x, y, w / facts - (2*c->bw), h - (2*c->bw), 0);
			#endif // CFACTS_PATCH
			x = x + WIDTH(c) + iv;
		} else if (m->ltaxis[lt] == TOP_TO_BOTTOM) {
			#if CFACTS_PATCH
			resize(c, x, y, w - (2*c->bw), h * (c->cfact / facts) - (2*c->bw), 0);
			#else
			resize(c, x, y, w - (2*c->bw), h / facts - (2*c->bw), 0);
			#endif // CFACTS_PATCH
			y = y + HEIGHT(c) + ih;
		} else if (m->ltaxis[lt] == MONOCLE) {
			resize(c, x, y, w - (2*c->bw), h - (2*c->bw), 0);
		} else if (m->ltaxis[lt] == GRID) {
			if (cc/rows + 1 > cols - nc%cols)
				rows = nc/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++;
			}
		}
	}
}
#else
static void
flextile(Monitor *m)
{
	unsigned int i, n, nc = 0, sc = 0, lt, cn = 0, rn = 0, cc = 0; // counters
	int cols = 1, rows = 1;
	int x, y, h, w;     // master x, y, height, width
	int sx, sy, sh, sw; // stack x, y, height, width
	int ox, oy;         // other stack x, y (centered layout)

	float facts, sfacts, ofacts;
	Client *c;

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

	ox = sx = x = m->wx;
	oy = sy = y = m->wy;
	sh = h = m->wh;
	sw = w = m->ww;
	sc = n - m->nmaster;

	#if CFACTS_PATCH
	getfacts(m, &facts, &sfacts);
	ofacts = sfacts;
	#else
	facts = MIN(n, m->nmaster);
	ofacts = sfacts = sc;
	#endif // CFACTS_PATCH

	/* Split master into master + stack if we have enough clients */
	if (m->nmaster && n > m->nmaster) {
		if (abs(m->ltaxis[LAYOUT]) == SPLIT_VERTICAL
				|| (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_V && n == m->nmaster + 1)) {
			sw = w * (1 - m->mfact);
			w = w * m->mfact;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				x = sx + sw;
			else
				sx = x + w;
		} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_HORIZONTAL
				|| (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H && n == m->nmaster + 1)) {
			sh = h * (1 - m->mfact);
			h = h * m->mfact;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				y = sy + sh;
			else
				sy = y + h;
		} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_V) {
			sw = w * (1 - m->mfact) / 2;
			w = w * m->mfact;
			x = sx + sw;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				ox = x + w;
			else
				sx = x + w;
		} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H) {
			sh = h * (1 - m->mfact) / 2;
			h = h * m->mfact;
			y = sy + sh;
			if (m->ltaxis[LAYOUT] < 0) // mirror
				oy = y + h;
			else
				sy = y + h;
		}

		if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_V || abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H) {
			sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0);
			facts = sfacts = ofacts = 0;
			for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
				#if CFACTS_PATCH
				if (i < m->nmaster)
					facts += c->cfact; // total factor of master area
				else if (sc && i < m->nmaster + sc)
					sfacts += c->cfact; // total factor of first stack area
				else
					ofacts += c->cfact; // total factor of second stack area
				#else
				if (i < m->nmaster)
					facts += 1;
				else if (sc && i < m->nmaster + sc)
					sfacts += 1;
				else
					ofacts += 1;
				#endif // CFACTS_PATCH
			}
		}
	}

	for (i = 0, lt = MASTER, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
		if (i == 0 || (m->nmaster && i == m->nmaster) || i == (m->nmaster + sc)) {
			nc = MIN(n, m->nmaster);
			if (!m->nmaster || i == m->nmaster) { // switch to stack area
				x = sx, y = sy, h = sh, w = sw, facts = sfacts, lt = STACK;
				nc = sc;
			} else if (i > 0 && i == (m->nmaster + sc)) { // switch to second stack area
				x = ox, y = oy, h = sh, w = sw, nc = n - i, facts = ofacts;
			}

			if (m->ltaxis[lt] == GRID) {
				/* grid dimensions */
				for (cols = 1; cols <= nc/2; cols++)
					if (cols*cols >= nc)
						break;
				if (nc == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
					cols = 2;
				rows = nc/cols;
				cn = rn = cc = 0; // reset cell no, row no, client count
			}
		}

		if (m->ltaxis[lt] == LEFT_TO_RIGHT) {
			#if CFACTS_PATCH
			resize(c, x, y, w * (c->cfact / facts) - (2*c->bw), h - (2*c->bw), 0);
			#else
			resize(c, x, y, w / facts - (2*c->bw), h - (2*c->bw), 0);
			#endif // CFACTS_PATCH
			x = x + WIDTH(c);
		} else if (m->ltaxis[lt] == TOP_TO_BOTTOM) {
			#if CFACTS_PATCH
			resize(c, x, y, w - (2*c->bw), h * (c->cfact / facts) - (2*c->bw), 0);
			#else
			resize(c, x, y, w - (2*c->bw), h / facts - (2*c->bw), 0);
			#endif // CFACTS_PATCH
			y = y + HEIGHT(c);
		} else if (m->ltaxis[lt] == MONOCLE) {
			resize(c, x, y, w - (2*c->bw), h - (2*c->bw), 0);
		} else if (m->ltaxis[lt] == GRID) {
			if (cc/rows + 1 > cols - nc%cols)
				rows = nc/cols + 1;
			resize(c,
				x + cn * (w / cols),
				y + rn * (h / rows),
				w / cols,
				h / rows,
				0);
			rn++;
			cc++;
			if (rn >= rows) {
				rn = 0;
				cn++;
			}
		}
	}
}
#endif


static void
setflexsymbols(Monitor *m, unsigned int n)
{
	char sym1 = 61, sym2 = 93, sym3 = 61, sym = 0;

	/* Predefined layouts */
	/* bstack */
	if (abs(m->ltaxis[LAYOUT]) == SPLIT_HORIZONTAL && m->ltaxis[MASTER] == LEFT_TO_RIGHT && m->ltaxis[STACK] == LEFT_TO_RIGHT) {
		snprintf(m->ltsymbol, sizeof m->ltsymbol, (m->ltaxis[LAYOUT] < 0 ? "⚍⚍⚍" : "⚎⚎⚎"));
		return;
	}

	/* bstackhoriz */
	if (abs(m->ltaxis[LAYOUT]) == SPLIT_HORIZONTAL && m->ltaxis[MASTER] == LEFT_TO_RIGHT && m->ltaxis[STACK] == TOP_TO_BOTTOM) {
		snprintf(m->ltsymbol, sizeof m->ltsymbol, (m->ltaxis[LAYOUT] < 0 ? "☳☳☳" : "☶☶☶"));
		return;
	}

	/* centered master horizontal split */
	if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H && m->ltaxis[MASTER] == TOP_TO_BOTTOM && m->ltaxis[STACK] == TOP_TO_BOTTOM) {
		snprintf(m->ltsymbol, sizeof m->ltsymbol, "☰☰☰");
		return;
	}

	if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H && m->ltaxis[MASTER] == LEFT_TO_RIGHT && m->ltaxis[STACK] == LEFT_TO_RIGHT) {
		snprintf(m->ltsymbol, sizeof m->ltsymbol, "☵☵☵");
		return;
	}

	/* monocle */
	if (n <= 1 && ((!m->nmaster && m->ltaxis[STACK] == MONOCLE) || (n <= m->nmaster && m->ltaxis[MASTER] == MONOCLE))) {
		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[M]");
		return;
	}

	/* Layout symbols */
	if (abs(m->ltaxis[LAYOUT]) == SPLIT_VERTICAL) {
		if (m->nmaster > 1 || m->ltaxis[MASTER] == MONOCLE)
			sym2 = 124; // |
		else if (m->ltaxis[LAYOUT] < 0)
			sym2 = 91; // [
		else
			sym2 = 93; // ]
	} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_HORIZONTAL) {
		if (m->nmaster > 1 || m->ltaxis[MASTER] == MONOCLE)
			sym2 = 58; // :
		else if (m->ltaxis[LAYOUT] < 0)
			sym2 = 91; // [
		else
			sym2 = 93; // ]
	} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_V) {
		if (m->ltaxis[LAYOUT] < 0)
			sym2 = 87; // W
		else
			sym2 = 77; // M
	} else if (abs(m->ltaxis[LAYOUT]) == SPLIT_CENTERED_H) {
		if (m->ltaxis[LAYOUT] < 0)
			sym2 = 87; // W
		else
			sym2 = 77; // M
	}

	if (m->ltaxis[MASTER] == LEFT_TO_RIGHT)
		sym1 = 124; // | ⏸
	else if (m->ltaxis[MASTER] == TOP_TO_BOTTOM)
		sym1 = 61; // =
	else if (m->ltaxis[MASTER] == MONOCLE)
		sym1 = MIN(n, m->nmaster);
	else if (m->ltaxis[MASTER] == GRID)
		sym1 = 35; // #

	if (m->ltaxis[STACK] == LEFT_TO_RIGHT)
		sym3 = 124; // |
	else if (m->ltaxis[STACK] == TOP_TO_BOTTOM)
		sym3 = 61; // =
	else if (m->ltaxis[STACK] == MONOCLE)
		sym3 = n - m->nmaster;
	else if (m->ltaxis[STACK] == GRID)
		sym3 = 35; // #


	/* Generic symbols */
	if (!m->nmaster) {
		if (m->ltaxis[STACK] == MONOCLE) {
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%d%c", 91, sym3, 93);
		} else {
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym3, sym3, sym3);
		}
		return;
	}

	if (n <= m->nmaster) {
		if (m->ltaxis[MASTER] == MONOCLE) {
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%d%c", 91, sym1, 93);
		} else {
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", 91, sym1, 93);
		}
	} else {
		if (m->ltaxis[LAYOUT] < 0) {
			sym = sym1;
			sym1 = sym3;
			sym3 = sym;
		}
		if (m->nmaster == 1 && abs(m->ltaxis[LAYOUT]) <= SPLIT_HORIZONTAL && m->ltaxis[MASTER] != MONOCLE) {
			if (m->ltaxis[LAYOUT] > 0)
				sym1 = 91;
			else
				sym3 = 93;
		}
		if (m->ltaxis[MASTER] == MONOCLE && m->ltaxis[STACK] == MONOCLE)
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%d%c%d", sym1, sym2, sym3);
		else if ((m->nmaster && m->ltaxis[MASTER] == MONOCLE && m->ltaxis[LAYOUT] > 0) || (m->ltaxis[STACK] == MONOCLE && m->ltaxis[LAYOUT] < 0))
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%d%c%c", sym1, sym2, sym3);
		else if ((m->ltaxis[STACK] == MONOCLE && m->ltaxis[LAYOUT] > 0) || (m->nmaster && m->ltaxis[MASTER] == MONOCLE && m->ltaxis[LAYOUT] < 0))
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%d", sym1, sym2, n - m->nmaster);
		else
			snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym1, sym2, sym3);
	}
}

/* Mirror layout axis for flextile */
void
mirrorlayout(const Arg *arg)
{
	if (!selmon->lt[selmon->sellt]->arrange)
		return;
	selmon->ltaxis[0] *= -1;
	#if PERTAG_PATCH
	selmon->pertag->ltaxes[selmon->pertag->curtag][0] = selmon->ltaxis[0];
	#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[0] > 0)
			selmon->ltaxis[0] = selmon->ltaxis[0] + 1 > 4 ? 1 : selmon->ltaxis[0] + 1;
		else
			selmon->ltaxis[0] = selmon->ltaxis[0] - 1 < -4 ? -1 : selmon->ltaxis[0] - 1;
	} else
		selmon->ltaxis[arg->i] = selmon->ltaxis[arg->i] + 1 > 4 ? 1 : selmon->ltaxis[arg->i] + 1;
	#if PERTAG_PATCH
	selmon->pertag->ltaxes[selmon->pertag->curtag][arg->i] = selmon->ltaxis[arg->i];
	#endif // PERTAG_PATCH
	arrange(selmon);
}