# User callable action; only the first argument should be passed
function textautorotate(scope    ,list,oid,addr,mirx,miry,rot,fixit)
{

	# create a list for finding target objects
	list = idplist("alloc");

	# build the list depending on scope; deal with only text objects, and only
	# those that are not locked in any way (e.g. symbol's non-floaters are locked)
	if ((scope == "") || (scope == "selected"))
		query("append", list, "(@.type == TEXT) && !(@.anylocked) && (@.p.selected)");
	else if (scope == "all")
		query("append", list, "(@.type == TEXT) && !(@.anylocked)");
	else {
		idplist("free", list);
		message("ERROR", "TextAutoRotate: first argument must be all or selected");
		return;
	}

	# batch all operations together into a single undo serial so they are
	# undone atomically: do not increase undo serial while doing rotations
	undo("FreezeSerial");

	# iterate over the list of text objects found, always moving the first
	# into oidp
	for(oidp = idplist("pop", list); oidp != ""; oidp = idplist("pop", list)) {

		# retrieve installed mirror and rotation state; installed means
		# how the object really ended up, also considering parent transformations
		addr = "object:" @ idp("print", oidp);
		mirx = propget(addr, "p/text/inst/mirx");
		miry = propget(addr, "p/text/inst/miry");
		rot = propget(addr, "p/text/inst/rot");

		# figure if we need to fix it, looking at the combination of mirrors
		# and rotation
		fixit = 0;
		if ((mirx + miry == 1) && (rot > 45) && (rot < 225))
			fixit = 1;
		if ((mirx + miry != 1) && (rot > 135))
			fixit = 1;

		# When need to fix, do a 2*90 deg rotation around the bounding box center
		# of the object. It's safer to use rotate90 with 2 steps than an arbitrary
		# rotate with 180 degree: rotate90 doesn't do any sin() or cos() so it is
		# harder to introduce rounding errors in coords
		if (fixit) {
			cx = propget(addr, "p/bbox/cx");
			cy = propget(addr, "p/bbox/cy");
			rotate90("idpath", 2, oidp, cx, cy);
		}

		# need to free oid path to avoid memory leak
		idp("free", oidp);
	}

	# need to free empty list to avoid memory leak
	idplist("free", list);

	# batch all operations together into a single undo serial so they are
	# undone atomically: allow increasing serials again, increase once so
	# the whole batch has its own unique serial number
	undo("UnFreezeSerial");
	undo("IncSerial");
}

# Runs once when the script is ran
function main(ARGV)
{
	# register action called directly by the user
	fgw_func_reg(textautorotate);

	# create a menu with the hotkey {p s r}
	cookie = scriptcookie();
	createmenu("/main_menu/Plugins/script/auto rotate selected text for readability", "TextAutoRotate(selected)", "Rotate selected text so they are always readable from bottom and right", cookie, "<key>p;<key>s;<key>r");
}
