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

int nflag;

static void
resample(Memimage *dst, Rectangle r, Memimage *src, Rectangle sr)
{
	Point sp, dp;
	Point _sp, qp;
	Point ssize, dsize;
	uchar *pdst0, *pdst, *psrc0, *psrc;
	ulong s00, s01, s10, s11;
	int tx, ty, bpp, bpl;

	ssize = subpt(subpt(sr.max, sr.min), Pt(1,1));
	dsize = subpt(subpt(r.max, r.min), Pt(1,1));
	pdst0 = byteaddr(dst, r.min);
	bpp = src->depth/8;
	bpl = src->width*sizeof(int);

	qp = Pt(0, 0);
	if(dsize.x > 0)
		qp.x = (ssize.x<<12)/dsize.x;
	if(dsize.y > 0)
		qp.y = (ssize.y<<12)/dsize.y;

	_sp.y = sr.min.y<<12;
	for(dp.y=0; dp.y<=dsize.y; dp.y++){
		sp.y = _sp.y>>12;
		ty = _sp.y&0xFFF;
		if(nflag)
			ty = ty << 1 & 0x1000;
		pdst = pdst0;
		sp.x = sr.min.x;
		psrc0 = byteaddr(src, sp);
		_sp.x = 0;
		for(dp.x=0; dp.x<=dsize.x; dp.x++){
			sp.x = _sp.x>>12;
			tx = _sp.x&0xFFF;
			if(nflag)
				tx = tx << 1 & 0x1000;
			psrc = psrc0 + sp.x*bpp;
			s00 = (0x1000-tx)*(0x1000-ty);
			s01 = tx*(0x1000-ty);
			s10 = (0x1000-tx)*ty;
			s11 = tx*ty;
			switch(bpp){
			case 4:
				pdst[3] = (s11*psrc[bpl+bpp+3] + 
					   s10*psrc[bpl+3] + 
					   s01*psrc[bpp+3] +
					   s00*psrc[3]) >>24;
			case 3:
				pdst[2] = (s11*psrc[bpl+bpp+2] + 
					   s10*psrc[bpl+2] + 
					   s01*psrc[bpp+2] +
					   s00*psrc[2]) >>24;
				pdst[1] = (s11*psrc[bpl+bpp+1] + 
					   s10*psrc[bpl+1] + 
					   s01*psrc[bpp+1] +
					   s00*psrc[1]) >>24;
			case 1:
				pdst[0] = (s11*psrc[bpl+bpp] + 
					   s10*psrc[bpl] + 
					   s01*psrc[bpp] +
					   s00*psrc[0]) >>24;
			}
			pdst += bpp;
			_sp.x += qp.x;
		}
		pdst0 += dst->width*sizeof(int);
		_sp.y += qp.y;
	}
}

enum {
	PERCENT = 0x80000000,
};

static int
getsize(char *s)
{
	int v;

	v = strtol(s, &s, 10) & ~PERCENT;
	if(*s == '%')
		v |= PERCENT;
	return v;
}

void
usage(void)
{
	fprint(2, "Usage: %s [ -x width ] [ -y height ] [ file ]\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	int fd, xsize, ysize;
	Memimage *im, *nim;
	ulong ochan, tchan;

	xsize = ysize = 0;
	ARGBEGIN{
	case 'a':
		xsize = ysize = getsize(EARGF(usage()));
		break;
	case 'x':
		xsize = getsize(EARGF(usage()));
		break;
	case 'y':
		ysize = getsize(EARGF(usage()));
		break;
	case 'n':
		nflag++;
		break;
	default:
		usage();
	}ARGEND
	fd = 0;
	if(*argv){
		fd = open(*argv, OREAD);
		if(fd < 0)
			sysfatal("open: %r");
	}
	memimageinit();
	if((im = readmemimage(fd)) == nil)
		sysfatal("readmemimage: %r");
	if(xsize & PERCENT)
		xsize = ((xsize & ~PERCENT) * Dx(im->r)) / 100;
	if(ysize & PERCENT)
		ysize = ((ysize & ~PERCENT) * Dy(im->r)) / 100;
	if(xsize || ysize){
		if(ysize == 0)
			ysize = (xsize * Dy(im->r)) / Dx(im->r);
		if(xsize == 0)
			xsize = (ysize * Dx(im->r)) / Dy(im->r);
		ochan = im->chan;
		switch(ochan){
		default:
			for(tchan = ochan; tchan; tchan >>= 8)
				if(TYPE(tchan) == CAlpha){
					tchan = RGBA32;
					break;
				}
			if(tchan == 0)
				tchan = RGB24;
			break;
		case GREY8:
		case RGB24:
		case RGBA32:
		case ARGB32:
		case XRGB32:
			tchan = ochan;
			break;
		case GREY1:
		case GREY2:
		case GREY4:
			tchan = GREY8;
			break;
		}
		if(tchan != ochan){
			if((nim = allocmemimage(im->r, tchan)) == nil)
				sysfatal("allocimage: %r");
			memimagedraw(nim, nim->r, im, im->r.min, nil, ZP, S);
			freememimage(im);
			im = nim;
		}
		if((nim = allocmemimage(
			Rect(im->r.min.x, im->r.min.y, im->r.min.x+xsize, im->r.min.y+ysize), 
			tchan)) == nil)
			sysfatal("allocmemimage: %r");
		resample(nim, nim->r, im, im->r);
		freememimage(im);
		im = nim;
		if(tchan != ochan){
			if((im = allocmemimage(nim->r, ochan)) == nil)
				sysfatal("allocimage: %r");
			memimagedraw(im, im->r, nim, nim->r.min, nil, ZP, S);
			freememimage(nim);
		}
	}
	if(writememimage(1, im) < 0)
		sysfatal("writememimage: %r");
	exits(0);
}
