#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>

enum
{
	None,
	Inset,	/* move border in or out uniformly */
	Insetxy,	/* move border in or out; different parameters for x and y */
	Set,		/* set rectangle to absolute values */
	Blank,	/* cut off blank region according to color value */
			/* Blank is not actually set as a mode; it can be combined with others */
};

void
usage(void)
{
	fprint(2, "usage: crop [-b rgb] [-c rgb] [-i ±inset | -r R | -x ±inset | -y ±inset] [-t tx ty] [imagefile]\n");
	fprint(2, "\twhere R is a rectangle: minx miny maxx maxy\n");
	fprint(2, "\twhere rgb is a color: red green blue\n");
	exits("usage");
}

int
getint(char *s)
{
	if(s == nil)
		usage();
	if(*s == '+')
		return atoi(s+1);
	if(*s == '-')
		return -atoi(s+1);
	return atoi(s);
}

Rectangle
crop(Memimage *m, ulong c)
{
	Memimage *n;
	int x, y, bpl, wpl;
	int left, right, top, bottom;
	ulong *buf;

	left = m->r.max.x;
	right = m->r.min.x;
	top = m->r.max.y;
	bottom = m->r.min.y;
	n = nil;
	if(m->chan != RGBA32){
		/* convert type for simplicity */
		n = allocmemimage(m->r, RGBA32);
		if(n == nil)
			sysfatal("can't allocate temporary image: %r");
		memimagedraw(n, n->r, m, m->r.min, nil, ZP, S);
		m = n;
	}
	wpl = wordsperline(m->r, m->depth);
	bpl = wpl*sizeof(ulong);
	buf = malloc(bpl);
	if(buf == nil)
		sysfatal("can't allocate buffer: %r");

	for(y=m->r.min.y; y<m->r.max.y; y++){
		x = unloadmemimage(m, Rect(m->r.min.x, y, m->r.max.x, y+1), (uchar*)buf, bpl);
		if(x != bpl)
			sysfatal("unloadmemimage");
		for(x=0; x<wpl; x++)
			if(buf[x] != c){
				if(x < left)
					left = x;
				if(x > right)
					right = x;
				if(y < top)
					top = y;
				bottom = y;
			}
	}
	
	if(n != nil)
		freememimage(n);
	return Rect(left, top, right+1, bottom+1);
}

void
main(int argc, char *argv[])
{
	int fd, mode, red, green, blue;
	Rectangle r, rparam;
	Point t;
	Memimage *m, *new;
	char *file;
	ulong bg, cropval;
	long dw;

	memimageinit();
	mode = None;
	bg = 0;
	cropval = 0;
	t = ZP;
	memset(&rparam, 0, sizeof rparam);

	ARGBEGIN{
	case 'b':
		if(bg != 0)
			usage();
		red = getint(ARGF())&0xFF;
		green = getint(ARGF())&0xFF;
		blue = getint(ARGF())&0xFF;
		bg = (red<<24)|(green<<16)|(blue<<8)|0xFF;
		break;
	case 'c':
		if(cropval != 0)
			usage();
		red = getint(ARGF())&0xFF;
		green = getint(ARGF())&0xFF;
		blue = getint(ARGF())&0xFF;
		cropval = (red<<24)|(green<<16)|(blue<<8)|0xFF;
		break;
	case 'i':
		if(mode != None)
			usage();
		mode = Inset;
		rparam.min.x = getint(ARGF());
		break;
	case 'x':
		if(mode != None && mode != Insetxy)
			usage();
		mode = Insetxy;
		rparam.min.x = getint(ARGF());
		break;
	case 'y':
		if(mode != None && mode != Insetxy)
			usage();
		mode = Insetxy;
		rparam.min.y = getint(ARGF());
		break;
	case 'r':
		if(mode != None)
			usage();
		mode = Set;
		rparam.min.x = getint(ARGF());
		rparam.min.y = getint(ARGF());
		rparam.max.x = getint(ARGF());
		rparam.max.y = getint(ARGF());
		break;
	case 't':
		t.x = getint(ARGF());
		t.y = getint(ARGF());
		break;
	default:
		usage();
	}ARGEND

	if(mode == None && cropval == 0 && eqpt(ZP, t))
		usage();

	file = "<stdin>";
	fd = 0;
	if(argc > 1)
		usage();
	else if(argc == 1){
		file = argv[0];
		fd = open(file, OREAD);
		if(fd < 0)
			sysfatal("can't open %s: %r", file);
	}

	m = readmemimage(fd);
	if(m == nil)
		sysfatal("can't read %s: %r", file);

	r = m->r;
	if(cropval != 0){
		r = crop(m, cropval);
		m->clipr = r;
	}

	switch(mode){
	case None:
		break;
	case Inset:
		r = insetrect(r, rparam.min.x);
		break;
	case Insetxy:
		r.min.x += rparam.min.x;
		r.max.x -= rparam.min.x;
		r.min.y += rparam.min.y;
		r.max.y -= rparam.min.y;
		break;
	case Set:
		r = rparam;
		break;
	}

	new = allocmemimage(r, m->chan);
	if(new == nil)
		sysfatal("can't allocate new image: %r");
	if(bg != 0)
		memfillcolor(new, bg);
	else
		memfillcolor(new, 0x000000FF);

	memimagedraw(new, m->clipr, m, m->clipr.min, nil, ZP, S);
	dw = byteaddr(new, ZP) - byteaddr(new, t);
	new->r = rectaddpt(new->r, t);
	new->zero += dw;
	if(writememimage(1, new) < 0)
		sysfatal("write error on output: %r");
	exits(nil);
}
