# Copyright 2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: hyprpm.eclass
# @MAINTAINER:
# Marc Meier <marc.meier@coatl.ch>
# @AUTHOR:
# Marc Meier <marc.meier@coatl.ch>
# @SUPPORTED_EAPIS: 8 9
# @BLURB: Eclass for fetching and installing hyprland plugins defined via a hyprpm.toml
# @DESCRIPTION:
# Will automatically resolve the correct git commit of
# a given plugin to use for the installed Hyprland version
# and automatically installs built plugin files.
# 
# Building of the plugin files is not automatically done,
# but often a simple call to make.
# 
# As the effective commit built (and thus the source tree)
# is dependant on the Hyprland version in use, using patch
# files is often a fool's errand. It is advisable to use
# sed where possible instead.

case ${EAPI} in
	8|9) ;;
	*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac

if [[ ! ${_HYPRPM_ECLASS} ]]; then
_HYPRPM_ECLASS=1

DEPEND="
	gui-wm/hyprland:=
	gui-libs/aquamarine:=
	gui-libs/hyprutils:=
	dev-libs/hyprgraphics:=
	gui-libs/hyprcursor:=
	dev-libs/hyprlang:=
"
BDEPEND="
	app-misc/yq-go
	${DEPEND}
"
RDEPEND="${DEPEND}"

inherit git-r3

if [[ ${EAPI} = 8 ]]; then
	inherit eapi9-pipestatus
fi

# @ECLASS_VARIABLE: HYPRPM_REPO_URI
# @REQUIRED
# @DESCRIPTION:
# URI for the source of the plugin

# @ECLASS_VARIABLE: HYPRPM_OVERRIDE_HYPRLAND_COMMIT
# @USER_VARIABLE
# @DEFAULT_UNSET
# @DESCRIPTION:
# Do not check Hyprland commit and use this instead
# 
# Intended in case the specific Hyprland version in use
# is not a 1:1 match for one of the tagged versions, and
# thus commit resolution would fail.
# 
# This is necessary, for example, when running live ebuilds.

# @ECLASS_VARIABLE: HYPRPM_RESOLVE_COMMIT
# @DEFAULT_UNSET
# @DESCRIPTION:
# The commit used to resolve versioned commits
# 
# As the effective commit built is dependant on
# the Hyprland version built against, it is not really
# possible to practically version hyprpm plugins. This
# variable is intended to bring some consitency to plugins,
# as defining this will force a specific commit from which
# to extract the effectivly built commit.
# 
# If unset will fall back to the head of the git repo

# @ECLASS_VARIABLE: HYPRPM_COMMIT_PINS_OVERRIDE
# @DEFAULT_UNSET
# @DESCRIPTION:
# A bash associative array specifying commit pins overrides
#
# This can be used to provide missing commit pins or override incorrect
# commit pins from the upstream hyprpm.toml.
#
# Priority order:
# 1. HYPRPM_OVERRIDE_PLUGIN_<plugin_name>_COMMIT
# 2. HYPRPM_COMMIT_PINS_OVERRIDE[<hyprland_commit>]
# 3. hyprpm.toml from upstream
#
# If unset or if no matching key exists, it will be skipped.

# @ECLASS_VARIABLE: HYPRPM_PLUGIN_DIR
# @DEFAULT_UNSET
# @DESCRIPTION:
# Will override the directory to which the plugins are installed
# 
# If unset will use /usr/lib/hyprland-plugins/
HYPRPM_PLUGIN_DIR=${HYPRPM_PLUGIN_DIR:-/usr/lib/hyprland-plugins/}

# @ECLASS_VARIABLE: HYPRPM_INSTALLED_OBJECTS
# @INTERNAL
# @DESCRIPTION:
# Keeps track of the plugin objects that were installed to
# be able to make an informative postinstall
HYPRPM_INSTALLED_OBJECTS=()

hyprpm_src_unpack() {
	debug-print-function ${FUNCNAME} "$@"

	EGIT_REPO_URI=${HYPRPM_REPO_URI}

	local esc_pn=${PN//[-+]/_}
	local flagname=HYPRPM_OVERRIDE_PLUGIN_${esc_pn^^}_COMMIT

	if [[ -n ${!flagname} ]]; then
		einfo "Override ${flagname} found"
		einfo "Will use commit '${!flagname}' and not consult \`hyprpm.toml\`"

		EGIT_COMMIT="${!flagname}"
	else
		einfo "Resolving matching commit"
		einfo "To override this use the following flag:"
		einfo "  ${flagname}"
		einfo

		if [[ ! -z "${HYPRPM_RESOLVE_COMMIT}" ]]; then
			EGIT_COMMIT="${HYPRPM_RESOLVE_COMMIT}"
		fi

		einfo "Fetching repo to get access to the hyprpm.toml"
		git-r3_src_fetch
		git-r3_checkout

		local hyprland_commit

		if [[ ! -z "${HYPRPM_OVERRIDE_HYPRLAND_COMMIT}" ]]; then
			ewarn "Hyprland's commit hash was manually overwritten."
			ewarn "This can lead to serious compatibility issues and"
			ewarn "Hyprland crashes."

			hyprland_commit="${HYPRPM_OVERRIDE_HYPRLAND_COMMIT}"
		else
			hyprland_commit=$(
				XDG_RUNTIME_DIR="" Hyprland --version | head -n 1 | grep -Eo "[0-9a-f]{40}";
				pipestatus || die "Failed to get installed Hyprland's commit hash"
			)

			einfo "Got Hyprland commit ${hyprland_commit} from the installed Hyprland instance"
		fi

		local plugin_commit

		if declare -p HYPRPM_COMMIT_PINS_OVERRIDE &>/dev/null &&
			[[ -v "HYPRPM_COMMIT_PINS_OVERRIDE[${hyprland_commit}]" ]]
		then
			plugin_commit=${HYPRPM_COMMIT_PINS_OVERRIDE[${hyprland_commit}]}
			einfo "Using plugin commit ${plugin_commit} from ebuild override"
		else
			local toml_query=".repository.commit_pins.[] | select(.[0] == \"${hyprland_commit}\") | .[1]"
			plugin_commit=$(yq "${toml_query}" "${WORKDIR}/${P}/hyprpm.toml" || die "Failed to query hyprpm.toml")
			einfo "Extracted plugin commit ${plugin_commit} from hyprpm.toml"
		fi

		if [[ -n ${plugin_commit} ]]; then
			EGIT_COMMIT="${plugin_commit}"
			einfo "Refetching repo with the correct commit"
		else
			ewarn "No matching commit pin was found in the hyprpm.toml of this"
			ewarn "plugin repository for the installed Hyprland commit:"
			ewarn "  ${hyprland_commit}"
			ewarn "Falling back to repository HEAD to match upstream hyprpm behavior."
			ewarn "Compatibility with the installed Hyprland version is not guaranteed."
			ewarn "This can lead to serious compatibility issues and Hyprland crashes."
			unset EGIT_COMMIT
		fi

	fi

	git-r3_src_fetch
	git-r3_checkout
}

# @FUNCTION: hyprpm_doplugin
# @USAGE: <plugin-file>
# @DESCRIPTION:
# Install a passed plugin file to the plugin dir.
# 
# Will use ${HYPRPM_PLUGIN_DIR} or its fallback
# as a target
# 
# Use hyprpm_autoinstall instead when possible
hyprpm_doplugin() {
	[[ -z "${1}" ]] && die "\`hyprpm_doplugin\` needs a parameter"

	insinto "${HYPRPM_PLUGIN_DIR}"
	doins "${1}"

	HYPRPM_INSTALLED_OBJECTS+="${HYPRPM_PLUGIN_DIR}$(basename "${1}")"
}

# @FUNCTION: hyprpm_autoinstall
# @USAGE: 
# @DESCRIPTION:
# Parse the hyprpm.toml file and automatically find
# where all the plugin files are to install
# 
# This is generally prefered over using hyprpm_doplugin
# directly
hyprpm_autoinstall() {

	local toml_query="to_entries | filter(.key != \"repository\") | .[].value.output"

	for file in $(yq "${toml_query}" "${WORKDIR}/${P}/hyprpm.toml" || die "Failed to query hyprpm.toml"); do
		if [[ -f "${file}" ]]; then
			hyprpm_doplugin "${file}"
		else
			einfo "Library ${file} does not exist. Skipping..."
		fi
	done

}

hyprpm_src_install() {
	hyprpm_autoinstall
}




hyprpm_pkg_postinst() {
	elog
	elog "The following plugins have been installed:"

	for plugin in "${HYPRPM_INSTALLED_OBJECTS[@]}"; do
		elog " - $plugin"
	done

	elog "Remember to load them in your hyprland config as follows:"

	for plugin in "${HYPRPM_INSTALLED_OBJECTS[@]}"; do
		elog "> plugin = $plugin"
	done

	elog
}

fi # _HYPRPM_ECLASS

EXPORT_FUNCTIONS src_unpack src_install pkg_postinst
