
# License GNU GPLv3
# Copyright (C) 2019 step
# Part of checksum script

# Usage:
# For mawk add option -W interactive
# awk -v TMPS="$TMPS" -v MAX_HASH=$MAX_HASH -v FONT="$WIN_FONT" \
# -v FIFO_OUT="$PROGRESS_FIFOIN"

# This script computes and pretty-prints the checksums itself or hands off
# the computation to the progress server. It also deals with in-band commands.

# FS         "\b"
# $1         "\thash"<hash set> | "\treload"{"-end"}? | <filepath \
# $2..$NF    filepath-continued>

BEGIN {
	FS = "\b"
}
/^[[:space:]]*$/ { next }

# Command: change the hash set
$1 ~ "\thash" {
	hash = substr($1, length("hash")+2)
	# export current hash set to file
	print hash > TMPS".hash"; close(TMPS".hash")

	delete H
	nH = split(hash, H, /\/|\|/)
	next
}

# Command: reload state: pass through output lines between "\treload" and "\treload-end"
$1 == "\treload" {
	while(0 < getline) {
		if($1 == "\treload-end") { fflush("/dev/stdout"); next }
		print
	}
}

# Response: feed computation result to yad --list on stdout
$0 ~ /^[[:digit:]]+\t/ {
	rs_file($0)
	next
}

# Hash set computation for incoming file
{
	path = $0; sub("^file://", "", path) # yad dnd adds this
	if("" == path) { next }              # when script started without arguments
	name = path; sub(".*/", "", name)

	# assert: i and nH are even numbers
	if(is_enabled("SHOW_PROGRESS_DIALOG_1")) {
		# with progress dialog, parallel computation
		for(i = 2; i <= nH; i += 2) {
			rq_file(path, name, H[i], H[i -1], i/2)
		}
	} else {
		# without progress dialog, serial computation
		for(i = 2; i <= nH; i += 2) {
			do_file(path, name, H[i], H[i -1], i/2)
		}
	}
}

function is_enabled(option_name,   feat) { #{{{1
	if(0 < (getline feat < (TMPS".feat"))) {
		close(TMPS".feat")
		# by convention bool-option names end with "_"<number>, where <number> is
		# the option number listed in variable $FATDOG_CHECKSUM_BOOL_OPTIONS.
		gsub(/^.*_/, "", option_name) # keep the number only
		gsub("/", ",", feat)
		return 0 < index(","feat",", ","option_name",")
	}
	return 0
}

function rq_file(path, name, cmd, hash, hash_num,   rq_id, c, realpath, checksum) { #{{{1
	# make a unique id
	rq_id = (MAX_HASH * (NR -1) + hash_num)
	# the progress server wants real paths
	c = "realpath -- \""path"\" 2>\""TMPS"\".fail"
	c | getline realpath
	close(c)
	if("" != realpath) {
		# request computing task
		print rq_id "\n" "" "\n" realpath "\n" hash" "name "\n" cmd > FIFO_OUT
		fflush(FIFO_OUT)
	} else {
		# print error to the main window - see below for column description
		if (0 < (getline c < (TMPS".fail"))) {
			close(TMPS".fail")
			if ("" != c) {
				print "0\n" c "\n" name "\n" tolower(hash) "\n" rq_id "\n" path  "\n" FONT
				fflush("/dev/stdout")
			}
		}
	}

	# pass realpath and output record template to rs_file
	# the template comprises yad --list columns: checkbox, CHECKSUM, NAME, HASH, SERIAL, PATH, @font@
	RQ_STUFF[rq_id] = realpath"\t" "0\n{{checksum}}\n" name "\n" tolower(hash) "\n" rq_id "\n" path  "\n" FONT
}

function rs_file(data,   p, idx, stream, rq_id, buf, realpath, template, result) { #{{{1
	# <data 1>    ::= <idx> "\t" ( "O" | "E" ) <rq_id> "@rq_id@"
	# <data 2>... ::= <idx> "\t" ( "O" <checksum> <realpath> ) | ( "E" <error line> )
	# <checksum>  ::= ( <hex digits> " "+ )+

	# Parse record index
	p = index(data, "\t")
	idx = substr(data, 1, p -1)
	stream = substr(data, p +1, 1)
	data = substr(data, p +2)

	# extract request id
	p = index(data, "@rq_id@")
	if(p) { # this is the first line of a response record

		# RS_ID holds all response record ids
		RS_ID[idx] = substr(data, 1, p -1)

	} else { # lines 2,3,... of a response record

		# For domain `checksum` expect one line of computation stdout (stream "O")
		# or one line of computation stderr (stream "E").

		# recall realpath and template arguments, which rq_file saved previously
		rq_id = RS_ID[idx]
		buf   = RQ_STUFF[rq_id]
		delete RS_ID[idx]
		delete RQ_STUFF[rq_id]
		p = index(buf, "\t")
		realpath = substr(buf, 1, p -1)
		template = substr(buf, p +1)

		# extract checksum or error from response data
		if (stream == "O") {
			result = substr(data, 1, length(data) - length(realpath))
			sub(/ +$/, "", data)
		} else {
			result = data
		}

		# fill template
		gsub("{{checksum}}", data, template)

		# print filled template to the main window
		print template
		fflush("/dev/stdout")
	}
}

function do_file(path, name, cmd, hash, hash_num,   serial, checksum) { #{{{1
# do_file is like rq_file+rs_file without involving the progress server
	# serial is unique
	serial = (MAX_HASH * (NR -1) + hash_num)

	cmd = cmd" -- \""path"\" 2>&1"
	cmd | getline checksum # digits and spaces <path>
	# If non-zero then path was a directory or the checksum command failed.
	if(0 == close(cmd)) {
		checksum = substr(checksum, 1, length(checksum) - length(path))
		sub(/ +$/, "", checksum)
	}
	# checkbox, CHECKSUM, NAME, HASH, SERIAL, PATH, @font@
	print "0\n" checksum "\n" name "\n" tolower(hash) "\n" serial "\n" path  "\n" FONT
	fflush("/dev/stdout")
}
