#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();
       return strtol(s, 0, 0);
}

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);
}