#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/draw.h>
#include <9pm/memdraw.h>

#include "xmem.h"

void xfillcolor(Memimage*, Rectangle, ulong);
static int xdraw(Memdrawparam*);

int	xgcfillcolor = 0;
int	xgcfillcolor0 = 0;
int	xgczeropm = 0;
int	xgczeropm0 = 0;
int	xgcsimplecolor = 0;
int	xgcsimplecolor0 = 0;
int	xgcsimplepm = 0; 
int	xgcsimplepm0 = 0;
int	xgcreplsrctile = 0;
int	xgcreplsrctile0 = 0;

void
memimageinit(void)
{
	static int didinit = 0;
	
	if(didinit)
		return;

	didinit = 1;
	_memimageinit();
	
	xfillcolor(memblack, memblack->r, 0);
	xfillcolor(memwhite, memwhite->r, 1);
}

void
memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp)
{
	int didx;
	Rectangle dr, mr, sr;
	Memdrawparam *par;
	
	if((par = _memimagedrawsetup(dst, r, src, sp, mask, mp)) == nil)
		return;

	if(xdraw(par))
		return;

	if((par->state & (Simplemask|Fullmask))!=(Simplemask|Fullmask))
		getXdata(dst, par->r);
	getXdata(src, par->sr);
	getXdata(par->mask, par->mr);
	
	_memimagedraw(par);
	
	putXdata(dst, par->r);
}

void
xfillcolor(Memimage *m, Rectangle r, ulong v)
{
	GC gc;
	Xmem *dxm;

	dxm = m->X;
	assert(dxm != nil);
	r = rectsubpt(r, m->r.min);
		
	if(m->chan == GREY1){
		gc = xgcfill0;
		if(xgcfillcolor0 != v){
			XSetForeground(xdisplay, gc, v);
			xgcfillcolor0 = v;
		}
	}else{
		if(m->chan == CMAP8 && xtblbit)
			v = plan9tox11[v];
				
		gc = xgcfill;
		if(xgcfillcolor != v){
			XSetForeground(xdisplay, gc, v);
			xgcfillcolor = v;
		}
	}
	XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, Dx(r), Dy(r));
}

static int
xdraw(Memdrawparam *par)
{
	int dy, dx;
	unsigned m;
	Memimage *src, *dst, *mask;
	Xmem *dxm, *sxm, *mxm;
	GC gc;
	Rectangle r, sr, mr;
	ulong sdval;

	dx = Dx(par->r);
	dy = Dy(par->r);
	src = par->src;
	dst = par->dst;
	mask = par->mask;
	r = par->r;
	sr = par->sr;
	mr = par->mr;
	sdval = par->sdval;

	if((dxm = dst->X) == nil)
		return 0;

	/*
	 * If we have an opaque mask and source is one opaque pixel we can convert to the
	 * destination format and just XFillRectangle.
	 */
	m = Simplesrc|Simplemask|Fullmask;
	if((par->state&m)==m){
		xfillcolor(dst, r, sdval);
		dirtyXdata(dst, par->r);
		return 1;
	}

	/*
	 * If no source alpha, an opaque mask, we can just copy the
	 * source onto the destination.  If the channels are the same and
	 * the source is not replicated, XCopyArea suffices.
	 */
	m = Simplemask|Fullmask;
	if((par->state&(m|Replsrc))==m && src->chan == dst->chan && src->X){
		sxm = src->X;
		r = rectsubpt(r, dst->r.min);		
		sr = rectsubpt(sr, src->r.min);
		if(dst->chan == GREY1)
			gc = xgccopy0;
		else
			gc = xgccopy;
		XCopyArea(xdisplay, sxm->pmid, dxm->pmid, gc, 
			sr.min.x, sr.min.y, dx, dy, r.min.x, r.min.y);
		dirtyXdata(dst, par->r);
		return 1;
	}
	
	/*
	 * If no source alpha, a 1-bit mask, and a simple source
	 * we can just copy through the mask onto the destination.
	 */
	if(dst->X && mask->X && !(mask->flags&Frepl)
	&& mask->chan == GREY1 && (par->state&Simplesrc)){
		Point p;

		mxm = mask->X;
		r = rectsubpt(r, dst->r.min);		
		mr = rectsubpt(mr, mask->r.min);
		p = subpt(r.min, mr.min);
		if(dst->chan == GREY1){
			gc = xgcsimplesrc0;
			if(xgcsimplecolor0 != sdval){
				XSetForeground(xdisplay, gc, sdval);
				xgcsimplecolor0 = sdval;
			}
			if(xgcsimplepm0 != mxm->pmid){
				XSetStipple(xdisplay, gc, mxm->pmid);
				xgcsimplepm0 = mxm->pmid;
			}
		}else{
			gc = xgcsimplesrc;
			if(dst->chan == CMAP8 && xtblbit)
				sdval = plan9tox11[sdval];
				
			if(xgcsimplecolor != sdval){
				XSetForeground(xdisplay, gc, sdval);
				xgcsimplecolor = sdval;
			}
			if(xgcsimplepm != mxm->pmid){
				XSetStipple(xdisplay, gc, mxm->pmid);
				xgcsimplepm = mxm->pmid;
			}
		}
		XSetTSOrigin(xdisplay, gc, p.x, p.y);
		XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, dx, dy);
		dirtyXdata(dst, par->r);
		return 1;
	}
	return 0;
}

ulong
pixelbits(Memimage *m, Point p)
{
	Xmem *xm;
	if(m->X)
		getXdata(m, Rect(p.x, p.y, p.x+1, p.y+1));
	return _pixelbits(m, p);
}
