#!/usr/bin/env bash

VERSION=1.5
BASH_BINARY="$(command -v bash)"
TERMV_AUTO_UPDATE=${TERMV_AUTO_UPDATE:-true}
TERMV_FULL_SCREEN=${TERMV_FULL_SCREEN:-false}

TERMV_CHANNELS_URL=${TERMV_CHANNELS_URL:-"https://iptv-org.github.io/api/channels.json"}
TERMV_STREAMS_URL=${TERMV_STREAMS_URL:-"https://iptv-org.github.io/api/streams.json"}

FZF_VERSION_FULL=$(fzf --version)
FZF_VERSION_MAJOR="${FZF_VERSION_FULL:0:1}"
FZF_VERSION_MINOR="${FZF_VERSION_FULL:2:2}"

declare -x TERMV_SWALLOW=${TERMV_SWALLOW:-false}
declare -x TERMV_MPV_FLAGS="${TERMV_DEFAULT_MPV_FLAGS:---no-resume-playback}"

TERMV_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/termv"
mkdir -p "${TERMV_CACHE_DIR:?}"

has() {
	case "$(command -v "$1" 2>/dev/null)" in
	alias* | "") return 1 ;;
	esac
}

_phi() {
	printf >&2 '    %s\n' "$1"
}

_pht() {
	printf >&2 '%s\n' "$@"
}

_p_warn() {
	printf '\033[33;1m %s \033[0m\n' "$@"
}

_p_success() {
	printf '\033[32;1m %s \033[0m\n' "$@"
}

_p_fail() {
	printf '\033[31;1m %s \033[0m\n' "$@"
	exit 1
}

# print error message & exit
_pemx() {
	_p_fail "error : $1"
}

version() {
	_pht "$(basename "$0") $VERSION"
}

usage() {
	_pht "Usage:   $(basename "$0") [OPTIONS] query"
	_pht
	_pht "Options:"
	_pht "  General Options:"
	_phi "-h, --help                Print this help text and exit."
	_phi "-v, --version             Print program version and exit."
	_phi "-u, --update              Update channel list to latest version."
	_pht
	_pht "  Player Options:"
	_phi "-f, --full-screen         Open mpv in fullscreen."
	_phi "-s, --swallow             Swallow terminal during playback (X11 only) based on devour; https://github.com/salman-abedin/devour.sh"
	_pht
	_pht "  Environment variables:  "
	_phi "TERMV_AUTO_UPDATE         Auto update channel list to latest version. (default: true)"
	_phi "TERMV_SWALLOW             Always swallow terminal during playback. (default: false)"
	_phi "TERMV_FULL_SCREEN         Always open mpv in fullscreen. (default: false)"
	_phi "TERMV_DEFAULT_MPV_FLAGS   Default arguments which are passed to mpv. (default: --no-resume-playback)"
	_phi "TERMV_CHANNELS_URL             URL to the channel list. (default: https://iptv-org.github.io/api/channels.json)"
	_phi "                          Any other URL must be in the same format as the default one."
	_phi "TERMV_STREAMS_URL             URL to the streams list. (default: https://iptv-org.github.io/api/streams.json)"
	_phi "                          Any other URL must be in the same format as the default one."
	_pht "  Improve me on GitHub:"
	_phi "https://github.com/Roshan-R/termv"
}

merge_json_files() {
	jq 'map(select(.channel != null)) | group_by(.channel) | map({id: .[0].channel, urls: [.[].url]})' "${TERMV_CACHE_DIR:?}/streams_data.json" >"${TERMV_CACHE_DIR:?}/tmp_streams_data.json"
	jq 'map({id, name, category: (.categories[0] // "N/A"), is_nsfw, languages, country})' "${TERMV_CACHE_DIR:?}/channels_data.json" >"${TERMV_CACHE_DIR:?}/tmp_channels_data.json"
	jq -s 'add | group_by(.id) | map(add)' "${TERMV_CACHE_DIR:?}/tmp_streams_data.json" "${TERMV_CACHE_DIR:?}/tmp_channels_data.json" | jq 'map(select ( .urls != null ))' >"${TERMV_CACHE_DIR:?}/data.json"
	rm "${TERMV_CACHE_DIR:?}/tmp_streams_data.json"
	rm "${TERMV_CACHE_DIR:?}/tmp_channels_data.json"
}

update_jsonfiles() {
	update_file "channels" "${TERMV_CHANNELS_URL}"
	update_file "streams" "${TERMV_STREAMS_URL}"
	merge_json_files
}

update_file() {
	name="$1"
	url="$2"

	printf '%s' "Downloading ${url:?}... "

	etagpath="${TERMV_CACHE_DIR:?}/${name}_etag"
	[ -f "${etagpath:?}" ] && oldetag=$(<"${etagpath}") || oldetag="null"

	curl -s "${url}" --etag-compare "${etagpath:?}" \
		--etag-save "${etagpath:?}" \
		-o "${TERMV_CACHE_DIR:?}/${name}_data.json_new" \
		-H "accept-encoding:gzip" --compressed && downloaded=1 || downloaded=0

	newetag=$(<"${etagpath}")

	if [ "${downloaded}" -eq 1 ] && [ "${newetag}" == "${oldetag}" ]; then
		touch "${TERMV_CACHE_DIR:?}/${name}_data.json"
		_p_warn "Your version is already up to date."
	elif [ "${downloaded}" -eq 1 ]; then
		mv -f "${TERMV_CACHE_DIR:?}/${name}_data.json_new" "${TERMV_CACHE_DIR:?}/${name}_data.json"
		_p_success "Done!"
	elif [ "${downloaded}" -eq 0 ]; then
		rm -f "${etagpath:?}" "${TERMV_CACHE_DIR:?}/${name}_data.json_new"
		echo "${oldetag}" >"${etagpath}"
		_p_fail "Uh oh, failed!"
	fi
}

_play() {
	printf '%s\n' "Fetching channel, please wait..."
	LAST_STRING="${*##* }"
	IFS=',' read -ra URLS <<<"$LAST_STRING"
	if [ "${TERMV_SWALLOW}" = true ]; then
		WID=$(xdo id)
		xdo hide
		# shellcheck disable=SC2086
		mpv "${URLS[@]}" ${TERMV_MPV_FLAGS} --force-media-title="${*%%  *}" --force-window=immediate
		xdo show "$WID" && xdo activate "$WID"
  elif [ "$(uname -o)" == "Android"  ]; then
    am start -n is.xyz.mpv/is.xyz.mpv.MPVActivity -e filepath "${URLS[0]}"
	else
		# shellcheck disable=SC2086
		mpv "${URLS[@]}" ${TERMV_MPV_FLAGS} --force-media-title="${*%%  *}"
	fi
}

_playbg() {
	{ setsid -f mpv "${*##* }" "${TERMV_MPV_FLAGS}" --force-media-title="${*%%  *}" --force-window=immediate >/dev/null 2>&1; }
}

export -f _play
export -f _playbg

# check if necessary programs are installed
DEPENDENCIES="fzf jq curl cut column"
[ "$(uname -o)" != "Android" ] && DEPENDENCIES="mpv ${DEPENDENCIES}"
for prog in ${DEPENDENCIES}; do
     ! has "$prog" && dependencies_not_installed="${dependencies_not_installed}${prog}, "
done

[ -n "${dependencies_not_installed}" ] && _pemx "Missing dependencies, please install: ${dependencies_not_installed%??}."

while [ "$1" ]; do
    case "$1" in
        "-h"|"--help")
            usage ; exit 0 ;;
        "-v"|"--version")
            version ; exit 0 ;;
        "-u"|"--update")
            update_jsonfiles ; exit ;;
        "-f"|"--full-screen")
            TERMV_FULL_SCREEN=true   ; shift ;;
        "-s"|"--swallow")
            TERMV_SWALLOW=true       ; shift ;;
        "--")
            shift ; break ;;
        -*)
            _pemx "$1 in not a supported option" ;;
        *)
            break ;;
    esac
done

[ "${TERMV_SWALLOW}" = true ] && { ! has "xdo" && _pemx "Dependency missing for '-s' flag, please install xdo."; }
[ "${TERMV_FULL_SCREEN}" = true ] && TERMV_MPV_FLAGS="${TERMV_MPV_FLAGS} --fs"

# Automatic check for updates
[ "${TERMV_AUTO_UPDATE}" = true ] && { [ ! "$(stat -c %y "${TERMV_CACHE_DIR:?}/channels_data.json" 2>/dev/null | cut -d' ' -f1)" = "$(date '+%Y-%m-%d')" ] && update_jsonfiles; }
[ "${TERMV_AUTO_UPDATE}" = true ] && { [ ! "$(stat -c %y "${TERMV_CACHE_DIR:?}/streams_data.json" 2>/dev/null | cut -d' ' -f1)" = "$(date '+%Y-%m-%d')" ] && update_jsonfiles; }

CHANNELS_LIST=$(
	jq -r '.[] | [.name, ("|"+.category), ("|"+.country), (.urls | join(","))] | @tsv' "${TERMV_CACHE_DIR:?}/data.json" | column -t -s $'\t'
)

if (("${FZF_VERSION_MINOR}" < 25 && "$FZF_VERSION_MAJOR" == 0)); then
	# binding alt-] does not work in fzf version below 0.25.0
	SHELL="${BASH_BINARY}" \
		fzf -e -i --reverse --cycle --with-nth="1..-2" \
		--bind "enter:execute(_play {})" \
		--bind "double-click:execute(_play {})" \
		--header="Select channel (press Escape to exit)" -q "${*:-}" \
		< <(printf '%s\n' "${CHANNELS_LIST}")
else
	SHELL="${BASH_BINARY}" \
		fzf -e -i --reverse --cycle --with-nth="1..-2" \
		--bind "alt-]:execute-silent(_playbg {})" \
		--bind "enter:execute(_play {})" \
		--bind "double-click:execute(_play {})" \
		--header="Select channel (press Escape to exit)" -q "${*:-}" \
		< <(printf '%s\n' "${CHANNELS_LIST}")
fi
