#!/bin/sh
#   keylist - list hotkey->actions found in .lht files in a html table
#   Copyright (C) 2015..2016, 2018 Tibor 'Igor2' Palinkas
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License along
#   with this program; if not, write to the Free Software Foundation, Inc.,
#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#   http://repo.hu/projects/pcb-rnd

AWK=awk

export LANG=C

need_lht()
{
	echo "lhtflat not found. Please install lihata to run this script. The" >&2
	echo "svn extern in src_3rd/ is not enough, this script requires a full" >&2
	echo "installation from svn://repo.hu/lihata/trunk" >&2
	exit 1
}

# make sure lhtflat is installed
echo "" | lhtflat || need_lht


if test -z "$*"
then
	echo ""
	echo "$0: Generate a html table from pcb menu lht files."
	echo "Usage: $0 file1 [file2 [file3 ... [fileN]]]"
	echo ""
	exit
else
	cmd="html"
	while test $# -gt 0
	do
		case "$1" in
			--html) cmd="html";;
			--boxed) cmd="boxed";;
			--lst) cmd="lst";;
			--dot) cmd="dot"; nodenames=$2; shift 1;;
			*) res_files="$res_files $1" ;;
		esac
		shift 1
	done
fi

extract_from_lht()
{
	lhtflat | $AWK -F '[\t]' -v "fn=$1" '

#data	text	//main_menu/1::Edit/submenu/11::Edit name of/submenu/1::pin on layout/a	Shift Ctrl<Key>n
#data	text	//main_menu/1::Edit/submenu/11::Edit name of/submenu/1::pin on layout/action	ChangeName(Object, Number) 

	{
		tmp = $3
		if ($3 ~ "/a/[0-9]*::$") {
			# li:a = {}
			sub("/[0-9]*::$", "", tmp)
		}
		parent=tmp
		sub("/[^/]*$","", parent)
		node=tmp
		sub("^.*/","", node)
	}


	(($1 == "data") && ($2 == "text")) {
		# simple text node: accel key
		if (node == "a") {
			seq=$0
			sub("[^\t]*[\t]*[^\t]*[\t]*[^\t]*[\t]*", "", seq)
			gsub(" ", "", seq)
			v = split(tolower(seq), S, "[;]")
			ktmp = ""
			for(n = 1; n <= v; n++) {
				if (S[n] ~ "<key>")
					split(S[n], K, "<key>")
				else
					split(S[n], K, "<char>")
				if (K[1] != "") {
					mods = ""
					if (K[1] ~ "alt")   mods = mods "-alt"
					if (K[1] ~ "ctrl")  mods = mods "-ctrl"
					if (K[1] ~ "shift") mods = mods "-shift"
				}
				else
					mods = ""
				if (ktmp == "")
					ktmp = K[2] mods
				else
					ktmp = ktmp ";" K[2] mods
			}
			if (KEY[parent] == "")
				KEY[parent] = ktmp
			else
				KEY[parent] = KEY[parent] SUBSEP ktmp
		}

		# simple text node: action
		if (node == "action")
			ACTION[parent] = $4

		# list item: action
		if ($3 ~ "/action/[0-9]+::$") {
			parent = $3
			sub("/action/[^/]*$", "", parent)
			if (ACTION[parent] != "")
				ACTION[parent] = ACTION[parent] ";" $4
			else
				ACTION[parent] = $4
		}
	}

	END {
		for(n in KEY) {
			menuname = n
			sub(".*::", "", menuname)
			v = split(KEY[n], K, "[" SUBSEP "]")
			for(i = 1; i <= v; i++)
				print K[i] "\t" fn "\t" ACTION[n] "\t" menuname
		}
	}
	'
}

# convert a "key src action" to a html table with rowspans for base keys
gen_html()
{
	$AWK -F '[\t]' '
	BEGIN {
		CLR[0] = "#FFFFFF"
		CLR[1] = "#DDFFFF"
		key_combos = 0
	}
	function to_base_key(combo)
	{
		sub("-.*", "", combo)
		return combo
	}

	function to_mods(combo)
	{
		if (!(combo ~ ".-"))
			return ""
		sub("^[^-]*[-]", "", combo)
		return combo
	}

	{
		k = $1
		if (last != k) {
			LIST[key_combos++] = k
#			ROWSPAN[to_base_key(k)]++
		}
		ACTION[$2, k] = $3
		MENUNAME[$2, k] = $4
		HIDS[$2]++
		last = k

		v = split(k, K, ";")
		p = ""
		for(n = 1; n <= v; n++) {
			p = p K[n] ";"
			if (($2, p) in PREFIX) {
				err = err "<br>" $2 ": " k " vs. " p
				ERR[$2, p]++
				ERR[$2, k]++
			}
		}
		p = k ";"
		PREFIX[$2, p]++
	}

	function cleanup(s)
	{
		gsub("\260", "\\&deg;", s)
		gsub("\\\\37777777660", "\\&deg;", s)
		gsub("\\\\057", "/", s)
		return s
	}

	END {
		q="\""
		print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
		print "<html>"
		print "<!-- This file is generated by util/keylist.sh. DO NOT EDIT. -->"
		print "<head>"
		print "\t<meta http-equiv=\"Content-Type\" content=\"text/html;charset=us-ascii\">"
		print "\t<title> Key to action bindings </title>"
		print "<!--AUTO head begin-->"
		print "<link rel=\"icon\" href=\"http://repo.hu/projects/pcb-rnd/resources/logo16.png\">"
		print "<!--AUTO head end-->"
		print "</head>"
		print "<body>"
		print "<h1> Key to action bindings </h1>"
		print "<table border=1 cellspacing=0>"
		printf("<tr><th> key")
		colspan = 2
		for(h in HIDS) {
			printf(" <th>%s", h)
			colspan++
		}
		print ""
		for(n = 0; n < key_combos; n++) {
			clr_cnt++
			key = LIST[n]
			print "<tr bgcolor=" q CLR[clr_cnt % 2] q ">"

			kv = split(key, K, ";")
			keystr = ""
			ind=""
			for(kn = 1; kn <= kv; kn++) {
				if (kn > 1)
					keystr = keystr "<br>"
				keystr = keystr ind K[kn]
				ind = ind "&nbsp;"
			}
			

			print "	<th align=left>" cleanup(keystr)
			for(h in HIDS) {
				mn = cleanup(MENUNAME[h, key])
				act = cleanup(ACTION[h, key])
				if ((act == "") && (mn == ""))
					act = "&nbsp;"
				else {
					gsub(");", "); ", act)
					if (mn != "")
						act = "<I>" mn "</I>" "<br>" act
				}
				print "	<td>", act
				if ((h, key) in ERR)
					print "<br> <b>Error: key prefix collision</b>"
			}
			last_base = base
		}
		print "</table>"
		print err
		print "</body></html>"
	}
	'
}

gen_list()
{
	local n
	for n in $res_files 
	do
		extract_from_lht "`basename $n`" < $n
	done | sort
}

# load node names and generate a dot drawing of the multikey bindings
gen_dot()
{
	$AWK -v "names=$nodenames"  '
		BEGIN {
			q="\""
			print "digraph keytree {"
			print "rankdir=LR"
			print "ranksep=0.2"
			while((getline < names) == 1) {
				path=$1
				$1=""
				desc = $0
				node(path, desc, "shape=box")
			}
			close(names)
			FS="[\t]"
		}

		function arrow_(curr)
		{
			if (!(curr in ARROW_SEEN)) {
				print curr
				ARROW_SEEN[curr]=1
			}
		}

		function arrow(path   ,n,v,P,last,curr)
		{
			sub("^[/;_]", "", path)
			sub("[/;_]$", "", path)
			v = split(path, P, "[/;_]")

#			curr = "root -> " q P[1] q
#			arrow_(curr)
			last = P[1]
			for(n = 2; n <= v; n++) {
				curr = q last q "->"  q last "_" P[n] q
				arrow_(curr)
				last = last "_" P[n]
			}
		}

		function cleanup(s)
		{
			gsub("\260", "\\&deg;", s)
			gsub("\\\\37777777660", "\\&deg;", s)
			gsub("\\\\057", "/", s)
			return s
		}

		function node(path, desc, shape,    orig)
		{
			orig=path
			desc=cleanup(desc)
			sub("^[/_;]", "", orig)
			sub("[/_;]$", "", orig)
			gsub("[/_;]", " ", orig)
			sub("^[/;]", "", path)
			sub("[/;]$", "", path)
			gsub("[/;]", "_", path)
			gsub("[\"]", "", desc)
			print q path q " [label=" q "{" orig "} \\n" desc q " " shape "]"
			arrow(path)
		}

		(($1 ~ "^[a-z]$") || ($1 ~ "^[a-z];[a-z];") || ($1 ~ "^[a-z];[a-z]$")) {
			node($1, $4, "")
		}

		END {
			print "}"
		}
	'
}

gen_boxed_html()
{
	$AWK -F '[\t]' '
		/^[a-z][;]/ {
			key=substr($0, 1, 1)
			TBL[key] = TBL[key] "\n<tr><td>" $1 "<td>" $4
		}
		END {
			print "<html><head><style>"
			print "div.key {"
			print "	margin: auto;"
			print "	background: #bbffff;"
			print "	float: left;"
			print "	margin: 15px;"
			print "}"
			print "</style></head>"
			print "<body><h1>pcb-rnd 2.x.x keys - cheat sheet </h1>"
			for(n = 0; n < 26; n++) {
				key = sprintf("%c", 97+n)
				print "<div class=\"key\"><table border=1 cellspacing=0 width=400px>"
				print TBL[key]
				print "</table></div>"
				print ""
			}
		}
	'
}

case "$cmd" in
	html) gen_list | gen_html ;;
	boxed) gen_list | gen_boxed_html ;;
	dot) gen_list | gen_dot ;;
	lst) gen_list ;;
esac

