#!/bin/bash

# omarchy:summary=Cycle the focused window through preset sizes
# omarchy:args=[next|prev]

set -uo pipefail

DIRECTION="${1:-next}"

# Preset widths, as a fraction of the screen. Edit to taste.
# These only apply to dwindle and to floating windows. On a scrolling
# workspace the layout cycles its own list instead. Set that list with:
#   hl.config({ scrolling = { explicit_column_widths = "0.333, 0.5, 0.667, 1.0" } })
FRACTIONS=(0.333 0.5 0.667)

# Height of a floating window, as a fraction of the screen.
FLOATING_HEIGHT=0.8

STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/window-sizes"

# Try the Lua dispatcher first, then fall back to the legacy name.
hypr_dispatch() {
  local lua="$1"
  shift

  hyprctl dispatch "$lua" >/dev/null 2>&1 || hyprctl dispatch "$@" >/dev/null
}

calc() {
  awk "BEGIN { printf \"%s\", $1 }"
}

ACTIVE=$(hyprctl activewindow -j 2>/dev/null)
[[ -n $ACTIVE && $ACTIVE != "{}" ]] || exit 0

ADDRESS=$(jq -r '.address // empty' <<<"$ACTIVE")
FLOATING=$(jq -r '.floating // false' <<<"$ACTIVE")
WORKSPACE=$(jq -r '.workspace.id // empty' <<<"$ACTIVE")
LAYOUT=$(hyprctl activeworkspace -j 2>/dev/null | jq -r '.tiledLayout // empty')

# --- Scrolling: the layout keeps its own preset list ------------------------

if [[ $FLOATING != "true" && $LAYOUT == "scrolling" ]]; then
  if [[ $DIRECTION == "prev" ]]; then
    MSG="colresize -conf"
  else
    MSG="colresize +conf"
  fi

  hypr_dispatch "hl.dsp.layout(\"$MSG\")" layoutmsg "$MSG"
  exit 0
fi

# --- Step to the next preset ------------------------------------------------

# Floating windows keep an index of their own. Tiled windows share one index
# per workspace, because the split is a property of the workspace.
if [[ $FLOATING == "true" ]]; then
  STATE_KEY="window-${ADDRESS}"
else
  STATE_KEY="workspace-${WORKSPACE}"
fi

STATE_FILE="$STATE_DIR/${STATE_KEY}.index"
COUNT=${#FRACTIONS[@]}

INDEX=0
if [[ -f $STATE_FILE ]]; then
  INDEX=$(<"$STATE_FILE")
  [[ $INDEX =~ ^[0-9]+$ ]] || INDEX=0
fi

if [[ $DIRECTION == "prev" ]]; then
  INDEX=$(((INDEX - 1 + COUNT) % COUNT))
else
  INDEX=$(((INDEX + 1) % COUNT))
fi

mkdir -p "$STATE_DIR"
printf '%s\n' "$INDEX" >"$STATE_FILE"

FRACTION=${FRACTIONS[$INDEX]}

# --- Floating: set an absolute size and center ------------------------------

if [[ $FLOATING == "true" ]]; then
  read -r MON_W MON_H < <(hyprctl monitors -j |
    jq -r '.[] | select(.focused) | "\(((.width / .scale) | floor)) \(((.height / .scale) | floor))"')

  [[ -n ${MON_W:-} && -n ${MON_H:-} ]] || exit 1

  WIDTH=$(calc "int($MON_W * $FRACTION)")
  HEIGHT=$(calc "int($MON_H * $FLOATING_HEIGHT)")

  hypr_dispatch "hl.dsp.window.resize({ x = $WIDTH, y = $HEIGHT })" resizeactive exact "$WIDTH" "$HEIGHT"
  hypr_dispatch "hl.dsp.window.center()" centerwindow
  exit 0
fi

# --- Dwindle: set the split ratio -------------------------------------------

# splitratio 1.0 is an even split, so a fraction of 0.5 maps to 1.0.
# With the default split_bias of 0, the ratio goes to the left or top window.
# So when the focused window is the right or bottom one, the fraction is
# mirrored. Which side the window sits on is read from its position.
read -r WIN_X WIN_Y WIN_W WIN_H < <(jq -r '"\(.at[0]) \(.at[1]) \(.size[0]) \(.size[1])"' <<<"$ACTIVE")
read -r MON_X MON_Y MON_W MON_H < <(hyprctl monitors -j |
  jq -r '.[] | select(.focused) | "\(.x) \(.y) \(((.width / .scale) | floor)) \(((.height / .scale) | floor))"')

MIRROR=false
if ((WIN_W * 100 > MON_W * 90)); then
  # The window spans the screen, so the split is top and bottom.
  ((WIN_Y + WIN_H / 2 > MON_Y + MON_H / 2)) && MIRROR=true
else
  ((WIN_X + WIN_W / 2 > MON_X + MON_W / 2)) && MIRROR=true
fi

if [[ $MIRROR == "true" ]]; then
  RATIO=$(calc "2 * (1 - $FRACTION)")
else
  RATIO=$(calc "2 * $FRACTION")
fi

# Hyprland accepts 0.1 to 1.9.
RATIO=$(calc "($RATIO < 0.1 ? 0.1 : ($RATIO > 1.9 ? 1.9 : $RATIO))")

hypr_dispatch "hl.dsp.layout(\"splitratio $RATIO exact\")" layoutmsg "splitratio $RATIO exact"
