#!/bin/bash

trap "exit" INT

dirname=`dirname "$0"`

# Parse options
output_dir=
font_file=
face_index=0
hb_info=hb-info
hb_vector=hb-vector
hb_svg_compare=$dirname/hb-svg-compare
tolerance=0
vector_precision=
precision=
font_size=upem
features=
variations=
face_loader=ot
font_funcs1=ot
font_funcs2=fontations
clear1=false
clear2=false
unicodes=
glyphs=
quiet=false
help=false
max_procs=
while test $# -gt 0; do
	case "$1" in
		-o|--output-dir)
			shift
			output_dir=$1
			shift
			;;
		--font-file)
			shift
			font_file=$1
			shift
			;;
		--face-index)
			shift
			face_index=$1
			shift
			;;
		--hb-vector)
			shift
			hb_vector=$1
			shift
			;;
		--hb-info)
			shift
			hb_info=$1
			shift
			;;
		--hb-svg-compare)
			shift
			hb_svg_compare=$1
			shift
			;;
		--tolerance)
			shift
			tolerance=$1
			shift
			;;
		--precision)
			shift
			precision=$1
			shift
			;;
		--max-procs)
			shift
			max_procs=$1
			shift
			;;
		--font-size)
			shift
			font_size=$1
			shift
			;;
		--features)
			shift
			features=$1
			shift
			;;
		--variations)
			shift
			variations=$1
			shift
			;;
		--unicodes)
			shift
			unicodes=$1
			shift
			;;
		--face-loader)
			shift
			face_loader=$1
			shift
			;;
		--font-funcs1)
			shift
			font_funcs1=$1
			shift
			;;
		--font-funcs2)
			shift
			font_funcs2=$1
			shift
			;;
		--clear1)
			shift
			clear1=true
			;;
		--clear2)
			shift
			clear2=true
			;;
		--quiet)
			quiet=true
			shift
			;;
		--help)
			help=true
			shift
			;;
		*)
			if test "x$font_file" == x; then
				font_file=$1
				shift
			else
				glyphs="$glyphs $1"
				shift
			fi
			;;
	esac
done

if $help; then
	cmd=`basename "$0"`
	echo "Usage: $cmd [OPTIONS] FONTFILE [GLYPH...]"
	echo "Render a font with two font backends and compare the results."
	echo
	echo "Options:"
	echo "  -o, --output-dir DIR: Output in DIR"
	echo "  --font-file FONTFILE: Font file to render"
	echo "  --hb-vector HB_VECTOR: Path to hb-vector; default $hb_vector"
	echo "  --hb-info HB_INFO: Path to hb-info; default $hb_info"
	echo "  --hb-svg-compare HB_SVG_COMPARE: Path to hb-svg-compare; default $hb_svg_compare"
	echo "  --tolerance TOLERANCE: Tolerance for SVG comparison; default $tolerance"
	echo "  --precision N: hb-vector precision override; default 5"
	echo "  --max-procs N: Max parallel processes for render/compare; default auto"
	echo "  --font-size SIZE: Font size; default $font_size"
	echo "  --features FEATURES: Font features; default none"
	echo "  --variations VARIATIONS: Font variations; default none"
	echo "  --face-loader: Face loader; default $face_loader"
	echo "  --font-funcs1 font_funcs: First font-funcs; default $font_funcs1"
	echo "  --font-funcs2 font_funcs: Second font-funcs; default $font_funcs2"
	echo "  --clear1: Clear first font backend output if exists"
	echo "  --clear2: Clear second font backend output if exists"
	echo "  --unicodes CODES: Unicodes to render"
	echo "  --quiet: Quiet mode"
	echo "  --help: Print help"
	exit 0
fi

get_num_jobs() {
	n=`getconf _NPROCESSORS_ONLN 2>/dev/null || true`
	test "x$n" == x && n=`nproc 2>/dev/null || true`
	test "x$n" == x && n=`sysctl -n hw.ncpu 2>/dev/null || true`
	case "$n" in
		''|*[!0-9]*) n=1 ;;
	esac
	test "$n" -lt 1 && n=1
	echo "$n"
}

if test "x$max_procs" != x; then
	echo "$max_procs" | grep -Eq '^[0-9]+$' || {
		echo "Invalid max-procs '$max_procs'" >&2
		exit 2
	}
	test "$max_procs" -gt 0 || {
		echo "Invalid max-procs '$max_procs'" >&2
		exit 2
	}
fi

if test "x$font_file" == x; then
	echo "No font file specified." >&2
	exit 2
fi
if ! which "$hb_vector" 2>/dev/null >/dev/null; then
	echo "'$hb_vector' not found" >&2
	exit 2
fi
if ! which "$hb_info" 2>/dev/null >/dev/null; then
	echo "'$hb_info' not found" >&2
	exit 2
fi
if ! test -f "$font_file"; then
	echo "Font file '$font_file' not found" >&2
	exit 2
fi

# Sanity check Unicode values
if test "x$unicodes" != x; then
	echo "$unicodes" |
	tr ' ' '\n' |
	grep -v '^U[+][0-9a-fA-F]\+$' &&
	{
		echo "Invalid Unicode values" >&2
		exit 2
	}
fi

$quiet || echo "Comparing '$font_file' with '$font_funcs1' and '$font_funcs2' font backends. " >&2

if test "x$output_dir" == x; then
	output_dir=`mktemp -d`
	echo "Output in '$output_dir'" >&2
fi
mkdir -p "$output_dir" || exit 1

if test "x$precision" != x; then
	echo "$precision" | grep -Eq '^[0-9]+$' || {
		echo "Invalid precision '$precision'" >&2
		exit 2
	}
	vector_precision="$precision"
else
	vector_precision=5
fi
$quiet || echo "Using precision=$vector_precision for tolerance=$tolerance." >&2

# Populate glyphs file
glyphs_file="$output_dir/glyphs"
> "$glyphs_file"
echo "$unicodes" |
tr ' ' '\n' |
sed 's/^U+//' |
grep . |
while read unicode; do
	echo "uni$unicode"
done >> "$glyphs_file"
echo "$glyphs" |
tr ' ' '\n' |
grep . >> "$glyphs_file"
if test "x$unicodes" == x -a "x$glyphs" == x; then
	$quiet || echo "No unicodes or glyphs specified. Comparing all glyphs in the font." >&2
	num_glyphs=`$hb_info --quiet --show-glyph-count "$font_file"`
	$quiet || echo "Font has $num_glyphs glyphs." >&2
	seq 0 $((num_glyphs - 1)) |
	sed 's/^/gid/' >> "$glyphs_file"
fi
num_glyphs=`wc -l < "$glyphs_file" | tr -d '[:space:]'`
if test "$num_glyphs" -lt 1; then
	echo "No glyphs to compare." >&2
	exit 2
fi
base_jobs=`get_num_jobs`
if test "x$max_procs" != x; then
	if test "$base_jobs" -gt "$max_procs"; then
		base_jobs="$max_procs"
	fi
fi
compare_jobs="$base_jobs"
if test "$compare_jobs" -gt "$num_glyphs"; then
	compare_jobs="$num_glyphs"
fi
$quiet || test "$base_jobs" -le 1 || echo "Using up to $base_jobs shards." >&2

flavor=
if test "$features" != ""; then
	flavor="$flavor.features=$features"
fi
if test "$variations" != ""; then
	flavor="$flavor.variations=$variations"
fi

# Render with both font backends
if $clear1; then
	funcs_prefix="$output_dir/$font_funcs1$flavor"
	$quiet || echo "Clearing '$funcs_prefix'... " >&2
	rm -rf "$funcs_prefix"
fi
if $clear2; then
	funcs_prefix="$output_dir/$font_funcs2$flavor"
	$quiet || echo "Clearing '$funcs_prefix'... " >&2
	rm -rf "$funcs_prefix"
fi
for font_funcs in "$font_funcs1" "$font_funcs2"; do
	test "x$font_funcs" == x && continue
	$quiet || echo "Rendering with font backend '$font_funcs'..." >&2

	funcs_prefix="$output_dir/$font_funcs$flavor"
	mkdir -p "$funcs_prefix"
	render_cmds="$output_dir/render-cmds.$font_funcs$flavor"
	> "$render_cmds"
	count=0
	while read glyph; do
		dir="$funcs_prefix"
		svg="$dir/$glyph.svg"
		if test -f "$svg"; then
			continue
		fi
		count=$((count + 1))
		if test $((count % 100)) == 0; then
			$quiet || echo -n . >&2
		fi
		printf '%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s\n' \
			"--font-file=$font_file" \
			"--face-index=$face_index" \
			"--glyphs" \
			"--face-loader=$face_loader" \
			"--font-funcs=$font_funcs" \
			"--features=$features" \
			"--variations=$variations" \
			"--font-size=$font_size" \
			"--precision=$vector_precision" \
			"--output-file=$svg" \
			"$glyph" >> "$render_cmds"
	done < "$glyphs_file"

	render_cmd_count=`wc -l < "$render_cmds" | tr -d '[:space:]'`
	render_jobs="$base_jobs"
	if test "$render_jobs" -gt "$render_cmd_count"; then
		render_jobs="$render_cmd_count"
	fi
	if test "$render_cmd_count" -gt 0; then
		render_shard_dir="$output_dir/render-shards.$font_funcs$flavor"
		rm -rf "$render_shard_dir"
		mkdir -p "$render_shard_dir" || exit 1
		split -d -n l/"$render_jobs" "$render_cmds" "$render_shard_dir/cmds." || exit 1
		pids=
		for shard in "$render_shard_dir"/cmds.*; do
			"$hb_vector" --batch < "$shard" > "$shard.out" &
			pids="$pids $!"
		done
		for pid in $pids; do
			wait "$pid" || exit 1
		done
		cat "$render_shard_dir"/cmds.*.out | grep -v '^success$' || true
	fi
		if test "$count" -ge 100; then
			$quiet || echo >&2
		fi
done

test "x$font_funcs1" == x || test "x$font_funcs2" == x && exit 0

diff="$output_dir/diff$flavor"
rm -f "$diff"
> "$diff"
test "x$flavor" == x || ln -f -s "diff$flavor" "$output_dir/diff"
$quiet || echo "Comparing SVGs into '$diff'..." >&2
funcs1_prefix="$output_dir/$font_funcs1$flavor"
funcs2_prefix="$output_dir/$font_funcs2$flavor"
pairs="$output_dir/pairs$flavor"
> "$pairs"
count=0
while read glyph; do
	count=$((count + 1))
	if test $((count % 100)) == 0; then
		$quiet || echo -n . >&2
	fi

	svg1="$funcs1_prefix/$glyph.svg"
	svg2="$funcs2_prefix/$glyph.svg"

	if ! test -f "$svg1" || ! test -f "$svg2"; then
		echo -e "\n$glyph not rendered." >&2
		exit 1
	fi

	echo -e "$svg1\t$svg2" >> "$pairs"
done < "$glyphs_file"
if test "$count" -ge 100; then
	$quiet || echo >&2
fi

raw_diff="$output_dir/diff.raw$flavor"
shard_dir="$output_dir/compare-shards$flavor"
rm -rf "$shard_dir"
mkdir -p "$shard_dir" || exit 1
split -d -n l/"$compare_jobs" "$pairs" "$shard_dir/pairs." || exit 1
pids=
for shard in "$shard_dir"/pairs.*; do
	"$hb_svg_compare" "$tolerance" < "$shard" > "$shard.out" &
	pids="$pids $!"
done
for pid in $pids; do
	wait "$pid" || exit 1
done
cat "$shard_dir"/pairs.*.out > "$raw_diff" || exit 1

# Sort diff by error
python3 - "$raw_diff" "$diff" <<'PY' || exit 1
import math
import sys

src, dst = sys.argv[1], sys.argv[2]
rows = []

with open(src, encoding='utf-8') as f:
  for idx, line in enumerate(f):
    line = line.rstrip('\n')
    first = line.split('\t', 1)[0]
    try:
      value = float(first)
      key = (0, value, idx) if math.isfinite(value) else (1, float('inf'), idx)
    except ValueError:
      key = (1, float('inf'), idx)
    rows.append((key, line))

rows.sort(key=lambda row: row[0])

with open(dst, 'w', encoding='utf-8') as f:
  for _, line in rows:
    f.write(line + '\n')
PY

if ! $quiet; then
	while IFS= read -r line; do
		echo "$line"
	done < "$diff"
fi
# Count number of differences
num_diffs=`cat "$diff" | wc -l`
$quiet || echo -e "\nFound $num_diffs differences in $num_glyphs glyphs." >&2
