#!/bin/sh
# arcticfetch - system summary with the Arctic mark
#
#   arcticfetch          logo and system information
#   arcticfetch -l       just the logo
#   arcticfetch -n       no logo, information only
#
# Written in POSIX sh so it works on a bare install with nothing but busybox.
# shellcheck shell=sh disable=SC2039

set -u

MODE=full
case "${1:-}" in
-l|--logo) MODE=logo ;;
-n|--no-logo) MODE=info ;;
-h|--help) sed -n '2,8p' "$0" | sed 's/^# \?//'; exit 0 ;;
esac

if [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
	R=$(printf '\033[0m'); B=$(printf '\033[1m')
	SNOW=$(printf '\033[38;5;231m'); GREY=$(printf '\033[38;5;245m')
	TEAL=$(printf '\033[38;5;44m')
else
	R= ; B= ; SNOW= ; GREY= ; TEAL=
fi

# The mark, coloured down the Arctic ramp: violet at the apex, teal at the feet.
RAMP="99 98 63 69 69 75 81 81 44 44 49"

logo_lines() {
	cat <<'MARK'
                   .xx
                   xxxx
                  xxxxxx
                 xxxxxxx
                xxxxxxxxx
               .xx ;xxxxxx
               xx   xxxxxxx
              xx+    xxxxxx+
             xxx      xxxxxx
            xxx        xxxxxx
           .xx         ;xxxxxx
           xx           xxxxxxx
          xx+            xxxxxx;
         xxxxxxxxxxxxxxxxxxxxxxx
        xxx                xxxxxx
       .xx                 +xxxxxx
       xx                   xxxxxxx
      xx+                    xxxxxx:
     xxx                      xxxxxx
    xxxx                      xxxxxxx
 xxxxxxxxx;:              :xxxxxxxxxxxxx:
MARK
}

# ------------------------------------------------------------------ information

rel() { sed -n "s/^$1=//p" /etc/arctic-release 2>/dev/null | tr -d '"' | head -1; }

info_os() {
	n=$(rel PRETTY_NAME); : "${n:=$(rel NAME)}"
	[ -n "$n" ] || n=$(sed -n 's/^PRETTY_NAME=//p' /etc/os-release 2>/dev/null | tr -d '"')
	printf '%s %s' "${n:-Linux}" "$(uname -m)"
}

info_host() {
	p=/sys/devices/virtual/dmi/id
	v=$(cat "$p/product_name" 2>/dev/null)
	f=$(cat "$p/sys_vendor" 2>/dev/null)
	[ -n "$v$f" ] && printf '%s %s' "$f" "$v" || printf '%s' "$(uname -n)"
}

info_uptime() {
	s=$(cut -d. -f1 /proc/uptime 2>/dev/null)
	[ -n "$s" ] || { printf 'unknown'; return; }
	d=$((s/86400)); h=$(((s%86400)/3600)); m=$(((s%3600)/60))
	o=""
	[ "$d" -gt 0 ] && o="$d day$([ "$d" -ne 1 ] && printf s)"
	[ "$h" -gt 0 ] && o="$o${o:+, }$h hour$([ "$h" -ne 1 ] && printf s)"
	[ "$d" -eq 0 ] && [ "$m" -gt 0 ] && o="$o${o:+, }$m min"
	printf '%s' "${o:-less than a minute}"
}

info_pkgs() {
	n=$(ls -1 /var/lib/alpm/local 2>/dev/null | wc -l | tr -d ' ')
	printf '%s (alpm)' "${n:-0}"
}

info_cpu() {
	m=$(sed -n 's/^model name[ \t]*: //p' /proc/cpuinfo 2>/dev/null | head -1)
	[ -n "$m" ] || m=$(sed -n 's/^Model[ \t]*: //p' /proc/cpuinfo 2>/dev/null | head -1)
	c=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null)
	printf '%s (%s)' "${m:-unknown}" "${c:-?}"
}

info_gpu() {
	g=$(lspci 2>/dev/null | grep -iE 'vga|3d controller' | \
		sed 's/.*: //; s/ (rev .*//' | head -2 | tr '\n' ',' | sed 's/,$//; s/,/, /')
	printf '%s' "${g:-unknown}"
}

info_mem() {
	t=$(awk '/^MemTotal:/{print $2}' /proc/meminfo 2>/dev/null)
	a=$(awk '/^MemAvailable:/{print $2}' /proc/meminfo 2>/dev/null)
	[ -n "$t" ] || { printf 'unknown'; return; }
	u=$(( (t - ${a:-0}) / 1024 ))
	printf '%s MiB / %s MiB' "$u" "$((t/1024))"
}

info_disk() {
	df -h / 2>/dev/null | awk 'NR==2{printf "%s / %s (%s)", $3, $2, $5}'
}

info_shell() {
	s=${SHELL:-}
	[ -n "$s" ] || s=$(getent passwd "$(id -u)" 2>/dev/null | cut -d: -f7)
	b=$(basename "${s:-sh}")
	case "$b" in
	zsh) v=$(zsh --version 2>/dev/null | cut -d' ' -f2) ;;
	*) v="" ;;
	esac
	printf '%s %s' "$b" "$v"
}

info_term() {
	printf '%s' "${TERM_PROGRAM:-${TERMINAL_EMULATOR:-$(
		# Walk up to find the terminal that owns this shell.
		p=$PPID
		i=0
		while [ "$i" -lt 6 ] && [ -n "$p" ] && [ "$p" != "1" ]; do
			n=$(cat "/proc/$p/comm" 2>/dev/null)
			case "$n" in
			foot|foot-server|alacritty|kitty|konsole|st|xterm|urxvt|wezterm)
				printf '%s' "$n"; break ;;
			esac
			p=$(awk '{print $4}' "/proc/$p/stat" 2>/dev/null)
			i=$((i+1))
		done
		[ "$i" -ge 6 ] && printf '%s' "${TERM:-tty}"
	)}}"
}

info_wm() {
	[ -n "${XDG_CURRENT_DESKTOP:-}" ] && { printf '%s' "$XDG_CURRENT_DESKTOP"; return; }
	for w in Hyprland niri dwl sway plasmashell xfwm4 dwm; do
		pgrep -x "$w" >/dev/null 2>&1 && { printf '%s' "$w"; return; }
	done
	printf 'none (tty)'
}

info_init() { printf 'busybox init'; }

info_libc() {
	# glibc ships ldd(1) as a bash script and Arctic has no bash, so ldd is out.
	# getconf is part of glibc itself and answers directly. Do not try running
	# libc.so.6 as a program for this: it works on a normal system but is not
	# something to depend on from a boot-time tool.
	# /etc/arctic-release records it at build time, which is the only fully
	# reliable source here: getconf(1) on Arctic is toybox's and does not know
	# GNU_LIBC_VERSION, and ldd is a bash script glibc ships that Arctic cannot
	# run. Fall back to asking the library itself.
	v=$(rel LIBC_VERSION)
	[ -n "$v" ] && { printf 'glibc %s' "$v"; return; }
	v=$(/usr/bin/getconf GNU_LIBC_VERSION 2>/dev/null | sed -n 's/^glibc //p')
	[ -n "$v" ] && { printf 'glibc %s' "$v"; return; }
	printf 'glibc'
}

info_cc() {
	if command -v clang >/dev/null 2>&1; then
		printf 'clang %s' "$(clang --version 2>/dev/null | sed -n '1s/.*version \([0-9.]*\).*/\1/p')"
	else
		printf 'llvm'
	fi
}

palette() {
	o="   "
	for c in 0 1 2 3 4 5 6 7; do o="$o$(printf '\033[4%sm   ' "$c")"; done
	printf '%s%s\n' "$o" "$R"
	o="   "
	for c in 0 1 2 3 4 5 6 7; do o="$o$(printf '\033[10%sm   ' "$c")"; done
	printf '%s%s\n' "$o" "$R"
}

build_info() {
	u="$(id -un)@$(uname -n)"
	printf '%s\n' "$SNOW$B$u$R"
	printf '%s\n' "$GREY$(printf '%s' "$u" | sed 's/./-/g')$R"
	row() { printf '%s%-11s%s %s\n' "$TEAL$B" "$1" "$R" "$2"; }
	row "os"        "$(info_os)"
	row "host"      "$(info_host)"
	row "kernel"    "$(uname -r)"
	row "init"      "$(info_init)"
	row "uptime"    "$(info_uptime)"
	row "packages"  "$(info_pkgs)"
	row "shell"     "$(info_shell)"
	row "wm"        "$(info_wm)"
	row "terminal"  "$(info_term)"
	row "cpu"       "$(info_cpu)"
	row "gpu"       "$(info_gpu)"
	row "memory"    "$(info_mem)"
	row "disk (/)"  "$(info_disk)"
	row "libc"      "$(info_libc)"
	row "compiler"  "$(info_cc)"
	printf '\n'
	palette
}

# --------------------------------------------------------------------- assembly

LOGO_W=42

case "$MODE" in
logo)
	i=0
	logo_lines | while IFS= read -r l; do
		n=$(( i * 11 / 21 + 1 )); [ "$n" -gt 11 ] && n=11
		printf '\033[38;5;%sm%s%s\n' "$(printf '%s' "$RAMP" | cut -d' ' -f"$n")" "$l" "$R"
		i=$((i+1))
	done
	exit 0 ;;
info)
	printf '\n'
	build_info
	exit 0 ;;
esac

# Side by side. Both halves are generated first, then zipped line by line so we
# never depend on paste(1) or a terminal that understands cursor save/restore.
tmpl=/tmp/.arcticfetch.logo.$$
tmpi=/tmp/.arcticfetch.info.$$
trap 'rm -f "$tmpl" "$tmpi"' EXIT

i=0
logo_lines >"$tmpl.raw"
: >"$tmpl"
while IFS= read -r l; do
	n=$(( i * 11 / 21 + 1 )); [ "$n" -gt 11 ] && n=11
	c=$(printf '%s' "$RAMP" | cut -d' ' -f"$n")
	# Pad to a fixed width before adding colour, so the seam stays straight.
	printf '\033[38;5;%sm%-*s%s\n' "$c" "$LOGO_W" "$l" "$R" >>"$tmpl"
	i=$((i+1))
done <"$tmpl.raw"
rm -f "$tmpl.raw"

build_info >"$tmpi"

nl=$(wc -l <"$tmpl"); ni=$(wc -l <"$tmpi")
rows=$nl; [ "$ni" -gt "$rows" ] && rows=$ni

printf '\n'
r=1
while [ "$r" -le "$rows" ]; do
	left=$(sed -n "${r}p" "$tmpl")
	right=$(sed -n "${r}p" "$tmpi")
	if [ -z "$left" ]; then
		printf '%*s%s\n' "$LOGO_W" "" "$right"
	else
		printf '%s  %s\n' "$left" "$right"
	fi
	r=$((r+1))
done
printf '\n'
