#! /bin/sh
#
#
# Requirements:
#   - sh, sed
#   - imagemagick
#   - gocr
#   - curl, perl for weather
#
#
# Configure:
#   - see variables below
#
#
# Train:
#   TRAIN=yes DEBUG=yes $0 /path/to/single-ingress-mail
# or
#   TRAIN_yes DEBUG=yes $0 /path/to/screenshot.png
#
#
# procmailrc snippet:
#
# :0HBDfw: $HOME/.ingress.lock
# * ^From: Firstname Lastname <account@example.com>$
# * ^To: .*account@example.com
# * ^My #Ingress agent profile.$
# * ^Subject: $
# | $HOME/sh/ingress-stat.sh
#
#
# History:
#   2.2 - Documentation
#   2.1 - Fixes to FMI weather crawling
#   2.0 - OCR most of the image, use custom GOCR database
#   1.0 - Use imagemagick to crop individual lines and OCR those
#   0.1 - Proof of concept :-)

set -e


GOCR_DB=~/sh/ingress-stat.2.gocr/	# path to gocr database
INGRESS_STATS=~/txt/ingress		# output file
FMI_STATION=2978			# fmi.fi weather station

### end of configuration ###



test "$DEBUG" && set -x



debug() {
	echo "[DEBUG] $*" >&2
}


setup_tmp() {
	tmp1="$(mktemp)"
	tmp2="$(mktemp)"
	tmp3="$(mktemp)"
	echo "tmp $tmp1 + $tmp2 + $tmp3"
	trap "rm '$tmp1' '$tmp2' '$tmp3'" 0
}

run_gocr() {
	mode=274 # 2 + 16 + 256
	if test "$1" = "train"; then
		shift
		mode=$(expr $mode + 128)
	elif test "x$TRAIN" != "x"; then
		debug TRAIN run_gocr
		mode=$(expr $mode + 128)
	fi
	gocr -p "$GOCR_DB" -s 10 -m $mode -d 5 -a 99 $*
}

run_convert() {
	convert "$1" -crop "$2" -negate -dither None -monochrome "$3"
}



extract_and_format_number() {
	sed '
		s/^[^1-9-]*//
		s/ .*//;s/O/0/g
		s/,/_/g
		/^[1-9]_[0-9][0-9][0-9]$/{
			s/_//
		}
	'
}



crop_and_ocr() {
	_input="$1"
	_geom="$2"
	_match="$3"
	if ! test "$TRAIN"; then
		run_convert "$_input" "$_geom" | run_gocr
	else
		tmpg="$(mktemp)"
		run_convert "$_input" "$_geom" pnm:"$tmpg"
		debug TRAIN crop_and_ocr
		run_gocr train "$tmpg"
		debug TRAIN crop_and_ocr END
		rm "$tmpg"
	fi
}

ocr_multi() {
	_input="$1"
	_output="$2"
	if ! test "$TRAIN"; then
		run_convert "$_input" 720x5000+0+800 pnm:- | run_gocr | \
			tr 'A-Z' 'a-z' >"$_output"
	else
		tmpg="$(mktemp)"
		run_convert "$_input" 720x5000+0+800 pnm:"$tmpg"
		debug TRAIN ocr_multi
		run_gocr train "$tmpg" >"$_output"
		debug TRAIN ocr_multi END
		tr 'A-Z' 'a-z' <"$_output" >"$tmpg"
		cat "$tmpg" >"$_output" # mv would not preserve permissions
		rm "$tmpg"
	fi
}



setup_extract() {
	extract_input="$1"
}

extract() {
	_match="$(echo "$1" | sed 's/ / */' | tr 'A-Z' 'a-z')"
	sed "/$_match/ !d" <"$extract_input" | extract_and_format_number

}



setup_scan() {
	scan_input="$1"
	scan_tmp="$2"
}

scan() {
	convert "$scan_input" -crop "$1" -negate -dither None -monochrome \
			"pnm:$scan_tmp"

	if ! test "$TRAIN"; then
		run_gocr "$scan_tmp" | \
			tr '\t' ' ' | \
			sed "/$2/!{s/.*/-1/}" | \
			extract_and_format_number
	else
		tmpg="$(mktemp)"
		debug TRAIN scan
		ls -l "$scan_tmp" >&2
		file "$scan_tmp" >&2
		run_gocr train "$scan_tmp" >"$tmpg"
		debug TRAIN scan END
		_out="$(cat "$tmpg" | \
			tr '\t' ' ' | \
			sed "/$2/!{s/.*/-1/}" | \
			extract_and_format_number)"
		rm "$tmpg"
		echo $_out
	fi
}



fn_date_ts() {
	# /tmp/profile_20150126_223622_0.png
	date -d "$(echo "$1" | sed 's/.*profile_//; s/_[0-9].png//;
	s/\(20[1-9][0-9]\)\([01][0-9]\)\([0-3][0-9]\)_\([0-2][0-9]\)\([0-5][0-9]\)[0-5][0-9]/\1-\2-\3 \4:\5/')" \
		+"%Y-%m-%d-%H-%M %s"
}

ping_ts() {
	date -d "$(identify -verbose "$1" | \
			sed '/date:create/!d; s/.*create: //')" \
		+"%Y-%m-%d-%H-%M %s"
}

date_ts() {
	_fn="$1"
	test "$_fn" || _fn="$input"
	if echo "$_fn" | grep -q 'profile_201[5-9]'; then
		fn_date_ts "$_fn"
	else
		ping_ts "$_fn"
	fi
}



setup_temperature() {
	temperature_tmp="$1"
}

temperature() {
	test "x$FMI_STATION" = "x" && return

	# huh? perl cuts the pipe and curl gets upset!?
	curl "http://ilmatieteenlaitos.fi/saa/Helsinki?parameter=4&map=weathernow&station=$FMI_STATION" >"$temperature_tmp"
	cp "$temperature_tmp" /tmp/ingress-fmi.html
	perl -e 'undef $/; $_=<>; s/.*?apparent-temperature-cold//s;
			s/<\/span.*//s; s/.*<span>//s;
			s/[^0-9]+$//; s/[^0-9\-]*//;
			print' <"$temperature_tmp"
}



scan_level()		{ scan 140x50+180+140	'^LVL';		}
scan_ap()		{ scan 260x50+174+201	' AP$';		}
scan_uniq_v()		{ scan 720x45+0+850	'Visited';	}
scan_xm_coll()		{ scan 720x45+0+890	'Collected';	}
scan_walk_km()		{ scan 720x45+0+985	'Distance';	}
scan_res_dep()		{ scan 720x45+0+1077	'Resonators';	}
scan_link_cr()		{ scan 720x45+0+1120	'Links';	}
scan_field_cr()		{ scan 720x45+0+1158	'Control';	}
scan_mu_cap()		{ scan 720x45+0+1200	'Mind';		}
scan_link_km()		{ scan 720x45+0+1240	'Longest';	}
scan_field_max()	{ scan 720x45+0+1278	'Largest';	}
scan_xm_re()		{ scan 720x45+0+1318	'Recharged';	}
scan_p_cap()		{ scan 720x45+0+1360	'Captured';	}
scan_u_cap()		{ scan 720x45+0+1397	'Portals';	}
scan_mod_dep()		{ scan 720x45+0+1439	'Mods';		}
scan_res_dest()		{ scan 720x45+0+1533	'Resonators';	}
scan_por_dest()		{ scan 720x45+0+1570	'Portals';	}
scan_link_dest()	{ scan 720x45+0+1611	'Links';	}
scan_field_dest()	{ scan 720x45+0+1652	'[fF]ields';	}
scan_por_held()		{ scan 720x45+0+1746	'Portal';	}
scan_link_held()	{ scan 720x45+0+1784	'Link';		}
scan_link_time()	{ scan 720x45+0+1826	'Days';		}
scan_field_held()	{ scan 720x45+0+1866	'[fF]ield';	}
scan_field_time()	{ scan 720x45+0+1905	'Largest';	}
scan_hacks()		{ scan 720x45+0+2000	'Hacks';	}
scan_glyphs()		{ scan 720x45+0+2040	'Glyph';	}



level()		{ scan_level;					}
ap()		{ scan_ap;					}
uniq_v()	{ extract '^Unique Portals Visited';		}
xm_coll()	{ extract '^XM Collected';			}
walk_km()	{ extract '^Distance Walked';			}
res_dep()	{ extract '^Resonators Deployed';		}
link_cr()	{ extract '^Links Created';			}
field_cr()	{ extract '^Control Fields Created';		}
mu_cap()	{ extract '^Mind Units Captured';		}
link_km()	{ extract '^Longest Link Ever Created';		}
field_max()	{ extract '^Largest Control Field';		}
xm_re()		{ extract '^XM Recharged';			}
p_cap()		{ extract '^Portals Captured';			}
u_cap()		{ extract '^Unique Portals Captured';		}
mod_dep()	{ extract '^Mods Deployed';			}
res_dest()	{ extract '^Resonators Destroyed';		}
por_dest()	{ extract '^Portals Neutralized';		}
link_dest()	{ extract '^Enemy Links Destroyed';		}
field_dest()	{ extract '^Enemy Control Fields Destroyed';	}
por_held()	{ extract '^Max Time Portal Held';		}
link_held()	{ extract '^Max Time Link Maintained';		}
link_time()	{ extract '^Max Link Length x Days';		}
field_held()	{ extract '^Max Time Field Held';		}
field_time()	{ extract '^Largest Field MUs x Days';		}
hacks()		{ extract '^Hacks';				}
glyphs()	{ extract '^Glyph Hack Points';			}

print_stat_line() {
	ocr_multi "$1" "$tmp1"
	setup_extract "$tmp1"
	setup_scan "$1" "$tmp2"

	test "x$2" = "x" && fn="$1" || fn="$2"
	date_ts=$(date_ts "$fn")
	level=$(level)
	ap=$(ap)
	uniq_v=$(uniq_v)
	xm_coll=$(xm_coll)
	walk_km=$(walk_km)
	res_dep=$(res_dep)
	link_cr=$(link_cr)
	field_cr=$(field_cr)
	mu_cap=$(mu_cap)
	link_km=$(link_km)
	field_max=$(field_max)
	xm_re=$(xm_re)
	p_cap=$(p_cap)
	u_cap=$(u_cap)
	mod_dep=$(mod_dep)
	res_dest=$(res_dest)
	por_dest=$(por_dest)
	link_dest=$(link_dest)
	field_dest=$(field_dest)
	por_held=$(por_held)
	link_held=$(link_held)
	link_time=$(link_time)
	field_held=$(field_held)
	field_time=$(field_time)
	hacks=$(hacks)
	glyphs=$(glyphs)

	setup_temperature "$tmp2"
	temp=$(temperature)

	echo "$date_ts $level $ap $uniq_v $xm_coll $walk_km $res_dep $link_cr $field_cr $mu_cap $link_km $field_max $xm_re $p_cap $u_cap $mod_dep $res_dest $por_dest $link_dest $field_dest $por_held $link_held $link_time $field_held $field_time $hacks $glyphs $temp" | tr ' ' '\t'
}



mail_to_image() {
	_input="$1"
	_output="$2"

	grep -q "My #Ingress agent profile." "$_input" || exit 1
	grep -q "Content-Type: image/png" "$_input" || exit 1
	grep -q "Content-Transfer-Encoding: base64" "$_input" || exit 1
	if grep -q 'Content-Disposition' "$_input"; then
		m_fn="$(sed '/Content-Disposition/!d; /filename=/!d;
				s/.*filename="//; s/".*//' <"$_input")"
	fi
	sed -n '
		/Content-Transfer-Encoding: base64/ b a
		d

		:a
		n
		/^$/ b b
		b a

		:b
		n
		/^--/ q
		p
		b b
		' <"$_input" | \
	base64 -d >"$_output"
	echo "$m_fn"
}



setup_tmp # makes "tmp1", "tmp2" and "tmp3" vailable


m_input=
m_fn=
if test $# -eq 0; then
	cat >"$tmp1"
	m_fn="$(mail_to_image "$tmp1" "$tmp3")"
	m_input="$tmp3"
elif grep -q '^Subject: ' "$1"; then
	m_fn="$(mail_to_image "$1" "$tmp3")"
	m_input="$tmp3"
else
	m_fn="$1"
	m_input="$1"
fi

if test "x$DEBUG" = "x"; then
	print_stat_line "$m_input" "$m_fn" >>"$INGRESS_STATS"
else
	print_stat_line "$m_input" "$m_fn"
fi
