/*
 * Simple X11 sprite handling
 * Implementation file
 *
 * $Id: xsprite.cc,v 1.5 2003/03/13 02:25:17 hsteoh Exp hsteoh $
 */

#include <X11/xpm.h>
#include "exception.h"
#include "xsprite.h"


xsprite_engine::xsprite_engine(xconnection *connection, Drawable drawable,
                               int color_closeness) : conn(connection) {
  Display *disp = conn->display();
  XGCValues gcvals;

  // Init
  d = drawable;
  closeness = color_closeness;

  // Create sprite GC's
  gcvals.function = GXcopy;
  copygc = XCreateGC(disp, d, GCFunction, &gcvals);
  gcvals.function = GXand;
  maskgc = XCreateGC(disp, d, GCFunction, &gcvals);
  gcvals.function = GXor;
  drawgc = XCreateGC(disp, d, GCFunction, &gcvals);
}

xsprite_engine::~xsprite_engine() {
  Display *disp = conn->display();
  XFreeGC(disp, copygc);
  XFreeGC(disp, maskgc);
  XFreeGC(disp, drawgc);
}

xflatsprite::xflatsprite(xsprite_engine *engine, char *xpmfile) : eng(engine) {
  Display *disp = eng->display();
  Drawable d = eng->get_drawable();
  XpmAttributes attr;
  XImage *img, *msk;			// raw image & mask
  int depth = eng->scrn_depth();

  // Init
  eng = engine;

  // XPM creation attributes
  attr.colormap = DefaultColormap(disp, eng->screen());
  attr.closeness = eng->get_closeness();
  attr.valuemask = XpmColormap | XpmCloseness;

  // (the following is to catch the case when no hotspot is set in the file)
  attr.x_hotspot = attr.y_hotspot = 0;

  if (XpmReadFileToImage(disp, xpmfile, &img, &msk, &attr)
      != XpmSuccess) {
    throw exception("@Error while loading pixmap file: %s", xpmfile);
  }

  // Store XPM info returned by libXpm
  wd = attr.width;
  ht = attr.height;
  ox = attr.x_hotspot;
  oy = attr.y_hotspot;

  // Upload sprite image to server
  image = XCreatePixmap(disp, d, wd, ht, depth);
  XPutImage(disp, image, eng->get_copygc(), img, 0,0, 0,0, wd,ht);

  // Create sprite mask with the correct color depth for this display.
  // Notes:
  // - The XPM shape mask is depth 1, which usually isn't what we want; so
  //   we'll need to convert it pixel by pixel into something we can AND with.
  // - here, we reuse *img since it is conveniently of the right dimensions
  //   and depth already. No need to manually create another XImage.
  for (int y=0; y<ht; y++) {
    for (int x=0; x<wd; x++) {
      unsigned long pixel = XGetPixel(msk, x,y);
      XPutPixel(img, x,y, pixel ? eng->black() : eng->white());
    }
  }

  // Upload converted mask to server
  mask = XCreatePixmap(disp, d, wd, ht, depth);
  XPutImage(disp, mask, eng->get_copygc(), img, 0,0, 0,0, wd,ht);

  // Cleanup
  XDestroyImage(img);
  XDestroyImage(msk);
}

xflatsprite::~xflatsprite() {
  Display *disp = eng->display();
  XFreePixmap(disp, image);
  XFreePixmap(disp, mask);
}

void xflatsprite::draw(Drawable d, int x, int y) {
  Display *disp = eng->display();

  // Mask out area first
  XCopyArea(disp, mask, d, eng->get_maskgc(), 0,0, wd,ht, x-ox,y-oy);

  // OR image into hole
  XCopyArea(disp, image, d, eng->get_drawgc(), 0,0, wd,ht, x-ox,y-oy);
}


xsavebuf::xsavebuf(xsprite_engine *engine, Drawable d, int max_width,
                   int max_height) :
	eng(engine), wmax(max_width), hmax(max_height) {

  // Storage area for background
  bckgnd = XCreatePixmap(eng->display(), eng->get_drawable(), wmax, hmax,
                         eng->scrn_depth());
  last_x = last_y = 0;
  last_wd = last_ht = 0;
  saved = 0;
}

xsavebuf::~xsavebuf() {
  XFreePixmap(eng->display(), bckgnd);
}

void xsavebuf::save(Drawable d, int x, int y, int wd, int ht) {
  // Sanity guards
  if (wd<0 || ht<0) {
    saved=0;				// invalidate saved background
    return;				// nothing more to do
  }
  if (wd>wmax || ht>hmax)
    throw exception("xsavebuf: requested save area exceeds buffer capacity");

  XCopyArea(eng->display(), d, bckgnd, eng->get_copygc(), x,y, wd,ht, 0,0);
  bckgnd_d = d;
  last_x = x;
  last_y = y;
  last_wd = wd;
  last_ht = ht;
  saved = 1;
}

void xsavebuf::restore() {
  if (saved) {
    XCopyArea(eng->display(), bckgnd, bckgnd_d, eng->get_copygc(), 0,0,
              last_wd, last_ht, last_x, last_y);
    saved = 0;
  }
}


xsprite::xsprite(xsprite_engine *engine, xflatsprite *sprite, char *xpmfile) {
}

xsprite::~xsprite() {
}

void xsprite::sdraw(Drawable d, int x, int y) {
}

void xsprite::erase() {
}

