#!/bin/sh
#
# Wove - talk to SliTaz: configure the system in plain words.
#
# Wove understands a sentence with its lexicons (share/wove/lang), finds
# the intent, asks what is missing, shows what it will run and runs it
# with trame (system) and spk (packages). See doc/README.
#
# Copyright (C) SliTaz GNU/Linux - BSD License
# Author: See AUTHORS files
#

# Locate libs: dev tree first, then the installed path.
_dev="$(cd "$(dirname "$0")/.." 2>/dev/null && pwd)"
if [ -r "$_dev/lib/understand.awk" ]; then
	WOVE_LIB="$_dev/lib"
	: ${WOVE_SHARE:="$_dev/share/wove"}
	[ -d "$_dev/po/mo" ] && WOVE_LOCALEDIR="$_dev/po/mo"
else
	WOVE_LIB=/usr/lib/wove
fi
unset _dev
export WOVE_LIB WOVE_SHARE

# The user's answers are read on fd 3: a question inside a pipeline
# ("list | ui_choose") still reads the user, not the pipe.
exec 3<&0

. "$WOVE_LIB/common.sh"
. "$WOVE_LIB/understand.sh"
. "$WOVE_LIB/slots.sh"
. "$WOVE_LIB/packages.sh"
. "$WOVE_LIB/brain.sh"
. "$WOVE_LIB/dialog.sh"
. "$WOVE_LIB/flows.sh"

usage() {
	cat <<EOT

$(boldify "$(gettext 'Usage:')") wove [command|"sentence"]

$(gettext 'Talk to SliTaz: configure the system in plain words.')

$(boldify "$(gettext 'Commands:')")
  $(gettext '(none)')          $(gettext 'Talk with Wove until you say bye (Ctrl-D)')
  "sentence"      $(gettext 'Do one thing and quit')
  help            $(gettext 'Display this short usage')
  doctor          $(gettext 'Check what Wove needs and what it can do')
  intents         $(gettext 'List everything Wove can do')
  debug "s"       $(gettext 'Show how Wove understands a sentence')
  version         $(gettext 'Print the version')

$(boldify "$(gettext 'Options:')")
  --dry-run       $(gettext 'Show the commands, run nothing')
  --pipe          $(gettext 'Talk with a front-end over stdin/stdout (doc/PROTOCOL)')

$(boldify "$(gettext 'Examples:')")
  wove "je veux un nouvel éditeur de texte"
  wove "connecte-moi au wifi"
  wove "mets le son à 40"
  wove "how do I change the keyboard to french"
  wove --dry-run "éteins l'ordinateur"

$(boldify "$(gettext 'About understanding:')")
  $(gettext 'Wove reads your words with the lexicons of your language and of')
  $(gettext 'English. When two things fit, or something is missing, it asks.')
  $(gettext 'It never runs a guess: changes are shown and confirmed first, and')
  $(gettext '"undo" puts back what the last action changed.')

EOT
}

# Talk until bye.
repl() {
	local line
	ui_title "$(_n 'Wove %s - talk to SliTaz' "$VERSION")"
	ui_info "$(gettext 'Say what you need, in your words. "help" for ideas, "bye" to quit.')"
	while :; do
		printf '\n%s ' "$(boldify 'wove>')"
		IFS= read -r line <&3 || { echo; break; }
		case "$(printf '%s' "$line" | wove_fold)" in
			"") continue ;;
			q|quit|exit|bye|ciao|salut\ !|au\ revoir|tchao) break ;;
		esac
		wove_handle "$line"
	done
}

# Front-end mode: one sentence per line on stdin, events on stdout.
pipe() {
	local line
	. "$WOVE_LIB/ui-pipe.sh"
	printf 'hello\t%s\n' "$VERSION"
	# What a front-end can offer before the first word.
	sed -n 's/^! \([^:]*\): /suggest\t\1\t/p' $(wove_lexicons | head -n1)
	while :; do
		printf 'ready\n'
		IFS= read -r line <&3 || break
		case "$line" in
			"") continue ;;
			bye) break ;;
		esac
		wove_handle "$line"
	done
}

# What Wove needs, what it can do, what trame offers that it does not.
doctor() {
	local t ok=0 bad=0 sub cmd covered missing
	ui_title "$(_n 'Wove %s' "$VERSION")"
	for t in trame spk-add jq awk; do
		if command -v $t >/dev/null 2>&1; then
			ui_ok "  ok    $t"
		else
			ui_error "  MISS  $t"
			bad=$((bad + 1))
		fi
	done
	ui_say "  $(gettext 'lexicons:') $(wove_lexicons | sed 's|.*/||' | tr '\n' ' ')"
	ui_say "  $(gettext 'intents:') $(grep -c '^[a-z_]*\.[a-z_]*$' "$WOVE_SHARE/intents")"
	if t=$(brain_status); then
		ui_ok "  $(_n 'brain: decide (%s)' "$DECIDE_MODEL")"
	else
		ui_info "  $(_n 'brain: off, %s' "$t")"
	fi
	command -v trame >/dev/null || return 1
	# Every trame action an intent runs must exist.
	ui_title "$(gettext 'Intents against trame')"
	for cmd in $(awk -F'\t' '$2 == "run" && $3 ~ /^trame / {
		split($3, w, " "); a = w[3]; if (w[4] ~ /^[a-z]/ && w[4] !~ /[{]/) a = a "." w[4]
		print w[2] ":" a }' "$WOVE_SHARE/intents" | sort -u); do
		sub=${cmd%%:*}
		if trame "$sub" --schema 2>/dev/null | jq -e --arg a "${cmd#*:}" \
			--arg b "${cmd#*:}" '.actions[$a] // .actions[($b | split(".")[0])]' >/dev/null; then
			ok=$((ok + 1))
		else
			ui_error "  $(_n 'unknown trame action: %s' "$sub ${cmd#*:}")"
			bad=$((bad + 1))
		fi
	done
	ui_say "  $(_n '%s trame actions used, all known' "$ok")"
	covered=$(awk -F'\t' '$2 == "run" && $3 ~ /^trame / { split($3, w, " ");
		print w[2] " " w[3]; print w[2] " " w[3] "." w[4] }' "$WOVE_SHARE/intents" \
		; grep -ho 'trame [a-z]* [a-z.-]*\( [a-z]*\)\?' "$WOVE_LIB/flows.sh" \
		| awk '{ print $2 " " $3; print $2 " " $3 "." $4 }' \
		; grep -ho 'wove_query [a-z]* [a-z.-]*\( [a-z]*\)\?' "$WOVE_LIB"/*.sh \
		| awk '{ print $2 " " $3; print $2 " " $3 "." $4 }' \
		; grep -ho '@[a-z.]*:' "$WOVE_SHARE/intents" | tr -d '@:' \
		| awk -F. '{ print $1 " " $2; print $1 " " $2 "." $3 }')
	missing=$(trame --schemas 2>/dev/null | jq -r '.[] | .command as $c
		| .actions | keys[] | "\($c) \(.)"' | while read -r sub t; do
		printf '%s\n' "$covered" | grep -qx "$sub $t" || printf '%s ' "$sub.$t"
	done)
	[ -n "$missing" ] && ui_info "  $(gettext 'trame actions Wove does not use:') $missing"
	[ $bad -eq 0 ]
}

intents() {
	awk -F'\t' '/^[a-z]/ { id = $1 } $2 == "label" && $0 !~ /Hidden/ { print id "\t" $3 }' \
		"$WOVE_SHARE/intents" | while IFS="$TAB" read -r id label; do
		printf '%-24s %s\n' "$id" "$(tr_ "$label")"
	done
}

debug() {
	wove_analyze "$1"
	printf '%s\n' "$ANALYSIS"
}

case "$1" in
	--pipe) pipe; exit 0 ;;
	--dry-run)
		WOVE_DRY=1
		ui_confirm() { ui_info "$1 [dry-run: yes]"; return 0; }
		ui_confirm_word() { ui_info "$1 [dry-run: yes]"; return 0; }
		shift ;;
esac
. "$WOVE_LIB/ui-term.sh"
if [ -n "$WOVE_DRY" ]; then
	ui_confirm() { ui_info "$1 [dry-run: yes]"; return 0; }
	ui_confirm_word() { ui_info "$1 [dry-run: yes]"; return 0; }
fi

case "$1" in
	"")                 repl ;;
	help|-h|--help)     usage ;;
	version|-V|--version) echo "$VERSION" ;;
	doctor)             doctor ;;
	intents)            intents ;;
	debug)              shift; debug "$*" ;;
	*)                  wove_handle "$*" ;;
esac
