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

struct Draw
{
       Point   deltas;
       Point   deltam;
       Memlayer                *dstlayer;
       Memimage        *src;
       Memimage        *mask;
       int     op;
};

static
void
ldrawop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave)
{
       struct Draw *d;
       Point p0, p1;
       Rectangle oclipr, srcr, r, mr;
       int ok;

       d = etc;
       if(insave && d->dstlayer->save==nil)
               return;

       p0 = addpt(screenr.min, d->deltas);
       p1 = addpt(screenr.min, d->deltam);

       if(insave){
               r = rectsubpt(screenr, d->dstlayer->delta);
               clipr = rectsubpt(clipr, d->dstlayer->delta);
       }else
               r = screenr;

       /* now in logical coordinates */

       /* clipr may have narrowed what we should draw on, so clip if necessary */
       if(!rectinrect(r, clipr)){
               oclipr = dst->clipr;
               dst->clipr = clipr;
               ok = drawclipnorepl(dst, &r, d->src, &p0, d->mask, &p1, &srcr, &mr);
               dst->clipr = oclipr;
               if(!ok)
                       return;
       }
       memdraw(dst, r, d->src, p0, d->mask, p1, d->op);
}

void
memdraw(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op)
{
       struct Draw d;
       Rectangle srcr, tr, mr;
       Memlayer *dl, *sl;

       if(mask == nil)
               mask = memopaque;

       if(mask->layer)
               return; /* too hard, at least for now */

   Top:
       if(dst->layer==nil && src->layer==nil){
               memimagedraw(dst, r, src, p0, mask, p1, op);
               return;
       }

       if(drawclipnorepl(dst, &r, src, &p0, mask, &p1, &srcr, &mr) == 0)
               return;

       /*
        * Convert to screen coordinates.
        */
       dl = dst->layer;
       if(dl != nil){
               r.min.x += dl->delta.x;
               r.min.y += dl->delta.y;
               r.max.x += dl->delta.x;
               r.max.y += dl->delta.y;
       }
   Clearlayer:
       if(dl!=nil && dl->clear){
               if(src == dst){
                       p0.x += dl->delta.x;
                       p0.y += dl->delta.y;
                       src = dl->screen->image;
               }
               dst = dl->screen->image;
               goto Top;
       }

       sl = src->layer;
       if(sl != nil){
               p0.x += sl->delta.x;
               p0.y += sl->delta.y;
               srcr.min.x += sl->delta.x;
               srcr.min.y += sl->delta.y;
               srcr.max.x += sl->delta.x;
               srcr.max.y += sl->delta.y;
       }

       /*
        * Now everything is in screen coordinates.
        * mask is an image.  dst and src are images or obscured layers.
        */

       /*
        * if dst and src are the same layer, just draw in save area and expose.
        */
       if(dl!=nil && dst==src){
               if(dl->save == nil)
                       return; /* refresh function makes this case unworkable */
               if(rectXrect(r, srcr)){
                       tr = r;
                       if(srcr.min.x < tr.min.x){
                               p1.x += tr.min.x - srcr.min.x;
                               tr.min.x = srcr.min.x;
                       }
                       if(srcr.min.y < tr.min.y){
                               p1.y += tr.min.x - srcr.min.x;
                               tr.min.y = srcr.min.y;
                       }
                       if(srcr.max.x > tr.max.x)
                               tr.max.x = srcr.max.x;
                       if(srcr.max.y > tr.max.y)
                               tr.max.y = srcr.max.y;
                       memlhide(dst, tr);
               }else{
                       memlhide(dst, r);
                       memlhide(dst, srcr);
               }
               memdraw(dl->save, rectsubpt(r, dl->delta), dl->save,
                       subpt(srcr.min, src->layer->delta), mask, p1, op);
               memlexpose(dst, r);
               return;
       }

       if(sl){
               if(sl->clear){
                       src = sl->screen->image;
                       if(dl != nil){
                               r.min.x -= dl->delta.x;
                               r.min.y -= dl->delta.y;
                               r.max.x -= dl->delta.x;
                               r.max.y -= dl->delta.y;
                       }
                       goto Top;
               }
               /* relatively rare case; use save area */
               if(sl->save == nil)
                       return; /* refresh function makes this case unworkable */
               memlhide(src, srcr);
               /* convert back to logical coordinates */
               p0.x -= sl->delta.x;
               p0.y -= sl->delta.y;
               srcr.min.x -= sl->delta.x;
               srcr.min.y -= sl->delta.y;
               srcr.max.x -= sl->delta.x;
               srcr.max.y -= sl->delta.y;
               src = src->layer->save;
       }

       /*
        * src is now an image.  dst may be an image or a clear layer
        */
       if(dst->layer==nil)
               goto Top;
       if(dst->layer->clear)
               goto Clearlayer;

       /*
        * dst is an obscured layer
        */
       d.deltas = subpt(p0, r.min);
       d.deltam = subpt(p1, r.min);
       d.dstlayer = dl;
       d.src = src;
       d.op = op;
       d.mask = mask;
       _memlayerop(ldrawop, dst, r, r, &d);
}