#!/bin/zsh
emulate -L zsh
zmodload zsh/mathfunc

setopt errexit errreturn
#setopt xtrace


find-supported-ratio() {
	local x=$1 y=$2 default=$3; shift 3
	[ $# -gt 0 ] || set -- 16:10 16:9 4:3 5:4  # supported already in edid.S

	local -A vals=()
	local r
	for r in "$@"; do
		local -i rx=${r%%:*} ry=${r#*:}
		local v=$(( abs( $x / $rx - $y / $ry ) ))
		vals[$v]=$r
	done

	local best=${vals[0]}
	[[ -n $best ]] || best=$default

	echo $best
}


template-S() {
	[[ ${(L)1} = mode(|line) ]] || return 1
	echo "-- Found modeline: $@"
	shift 1

	local name="${1//\"}"
	[[ -z "$name" ]] && echo "Could not parse modeline: $@" >&2 && return 1
	[[ "${#name}" -gt 12 ]] && echo "Name cannot be longer than 12 characters: $name is ${#name} characters long" >&2 && return 1
	local fn="${name}.S"

	local -F pixel_clock_mhz=$2
	local -i pixel_clock_khz=$((pixel_clock_mhz * 1000))
	shift 2

	local -i hdisp="$1" hsyncstart="$2" hsyncend="$3" htotal="$4"; shift 4
	local -i vdisp="$1" vsyncstart="$2" vsyncend="$3" vtotal="$4"; shift 4

	local -i hsync_polarity=0 vsync_polarity=0 dpi=96 vfreq_hz=60
	local edid_version="1.3" ratio="compute"

	local arg
	for arg in "$@"; do
		case "${(L)arg}" in
			[-+]hsync) [[ "${arg:0:1}" == "-" ]] || hsync_polarity=1 ;;
			[-+]vsync) [[ "${arg:0:1}" == "-" ]] || vsync_polarity=1 ;;
			ratio=*|xy_ratio=*) ratio="${arg#*=}" ;;
			dpi=*) dpi="${arg#*=}" ;;
			edid_version=*) edid_version="${arg#*=}" ;;
			vfreq=*|vfreq_hz=*) vfreq_hz="${arg#*=}" ;;
			*) echo "Ignoring unknown modeline option passed: '$arg'" >&2 ;;
		esac
	done

	case $ratio in
	compute)
		ratio=$(find-supported-ratio $hdisp $vdisp 'UNKNOWN')
		printf 'Computed ratio: %s\n' $ratio
		[[ $ratio != 'UNKNOWN' ]] || return 1
		;;
	esac

	local -A defines
	defines=(
		TIMING_NAME "${(qqq)name}"

		CLOCK	"$pixel_clock_khz"
		XPIX	"$hdisp"
		XBLANK	"$((htotal - hdisp))"
		XOFFSET "$((hsyncstart - hdisp))"
		XPULSE	"$((hsyncend - hsyncstart))"

		YPIX	"$vdisp"
		YBLANK	"$((vtotal - vdisp))"
		YOFFSET "(63+$((vsyncstart - vdisp)))"
		YPULSE	"(63+$((vsyncend - vsyncstart)))"

		VERSION "${edid_version%%.*}"
		REVISION "${edid_version#*.}"

		XY_RATIO "XY_RATIO_${(U)ratio//:/_}"
		DPI "$dpi"
		VFREQ "$vfreq_hz"
		HSYNC_POL "$hsync_polarity"
		VSYNC_POL "$vsync_polarity"
	)

	local -a lines=('/* '"$name: $REPLY"' */')
	local k
	for k in ${(k)defines}; do
		lines+=("#define $k ${defines[$k]}")
	done
	lines+=('#include "edid.S"')

	echo "${(j:\n:)lines[@]}" > "$fn"
	echo "Wrote $fn"
}

local f=${1:-'-'}
[[ $f != '-' ]] || f="/dev/stdin"

if [[ -z "$f" || "$f" == "-h" ]]; then
	self=${0:t}
	cat >&2 <<-EOF
	${0:t}, version forever 0.0.1
	Help:
	  $self -h
	Parse modelines from stdin:
	  $self
	  $self -
	Parse modelines from a file (eg xorg.conf)
	  $self FILENAME
	EOF
	exit 1
fi

echo "Searching for modelines in '$f'"
retcode=0
while read; do
	# trim
	REPLY=($=REPLY)
	[[ -n "$REPLY" ]] || continue
	if ! template-S ${(@)REPLY} ; then
		retcode=1
	fi
done < $f
exit $retcode
