/* Tk-specific functions for image families in Xconq.
   Copyright (C) 1998 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

#include "config.h"
#include "misc.h"
#include "lisp.h"
#include "module.h"
#include "system.h"
#include "imf.h"

#include <tcl.h>
#include <tk.h>

#include "tkimf.h"

extern Tcl_Interp *interp;

int tmp_valid;

Tk_Window tmp_root_window;

extern char *imflib;

static Pixmap tk_load_bitmap(Tk_Window rootwin, char *name, char *ext,
			     int *w, int *h);
static void tk_interp_image(ImageFamily *imf, Image *img, int force);
static void tk_make_color_pixmap(Tk_Window rootwin, ImageFamily *imf,
				 Image *img);

TkImage *
init_tk_image(Image *img)
{
    TkImage *tkimg;

    tkimg = (TkImage *) xmalloc(sizeof(TkImage));
    /* Point to the generic image. */
    tkimg->generic = img;
    tkimg->mono = None;
    tkimg->mask = None;
    tkimg->colr = None;
    tkimg->solid = NULL;
    return tkimg;
}

TkImage *
get_tk_image(Image *img)
{
    TkImage *tkimg;

    if (img->hook)
      return (TkImage *) img->hook;
    tkimg = init_tk_image(img);
    img->hook = (char *) tkimg;
    return tkimg;
}

/* This tries to fill in the given image family by looking for and loading
   standard TK bitmap files. */

ImageFamily *
tk_load_imf(ImageFamily *imf)
{
    int w, h;
    Pixmap pic;
    Image *img;
    Tk_Window rootwin = None;
    TkImageFamily *tkimf;
    TkImage *tkimg;

    /* If no imf or no name, don't even try. */
    if (imf == NULL || imf->name == NULL)
      return NULL;
    if (strcmp(imf->name, "none") == 0)
      return imf;
    if (tmp_valid) {
	if (imf->hook == NULL) {
	    /* Make a copy of the imf that will be specific to the
	       current root Tk_Window. */
	    imf = clone_imf(imf);
	    imf->hook = xmalloc(sizeof(TkImageFamily));
	    tkimf = (TkImageFamily *) imf->hook;
	    tkimf->rootwin = tmp_root_window;
	}
    }
    tkimf = (TkImageFamily *) imf->hook;
    if (tkimf) {
	rootwin = tkimf->rootwin;
    }
    /* Grab at plausibly-named bitmaps. */
    pic = tk_load_bitmap(rootwin, imf->name, "b", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h, 0);
	tkimg = get_tk_image(img);
	tkimg->mono = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "m", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h, 0);
	tkimg = get_tk_image(img);
	tkimg->mask = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "8.b", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h, 0);
	tkimg = get_tk_image(img);
	tkimg->mono = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "8.m", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h, 0);
	tkimg = get_tk_image(img);
	tkimg->mask = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "32.b", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h, 0);
	tkimg = get_tk_image(img);
	tkimg->mono = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "32.m", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h, 0);
	tkimg = get_tk_image(img);
	tkimg->mask = pic;
    }
    return imf;
}

/* Try to load a bitmap of the given name, looking in both the current dir
   and the library dir. */

static Pixmap
tk_load_bitmap(rootwin, name, ext, wp, hp)
Tk_Window rootwin;
char *name, *ext;
int *wp, *hp;
{
#if 0
    int hotx, hoty;
    unsigned int w, h;
    Pixmap rslt;
    static char sbuf[1000];

    if (ext != NULL) {
	make_pathname(NULL, name, ext, sbuf);
	if (XReadBitmapFile(dpy, rootwin, sbuf,
			    &w, &h, &rslt, &hotx, &hoty) == BitmapSuccess) {
	    DGprintf("Loaded bitmap \"%s\"\n", sbuf);
	    *wp = w;  *hp = h;
	    return rslt;
	}
	make_pathname(imflib, name, ext, sbuf);
	if (XReadBitmapFile(dpy, rootwin, sbuf,
			    &w, &h, &rslt, &hotx, &hoty) == BitmapSuccess) {
	    DGprintf("Loaded bitmap \"%s\"\n", sbuf);
	    *wp = w;  *hp = h;
	    return rslt;
	}
    }
#endif
    return None;
}

/* if (force): prefer data over rawdata; always re-create pixmaps */

ImageFamily *
tk_interp_imf(ImageFamily *imf, Image *img, int force)
{
    TkImageFamily *tkimf;

    if (tmp_valid) {
	if (imf->hook == NULL) {
	    /* Make a copy of the imf that will be specific to the
	       current display and root window. */
	    imf = clone_imf(imf);
	    imf->hook = xmalloc(sizeof(TkImageFamily));
	    tkimf = (TkImageFamily *) imf->hook;
	    tkimf->rootwin = tmp_root_window;
	}
    }
    if (img == NULL) {
	for_all_images(imf, img) {
	    tk_interp_image(imf, img, force);
	}
    } else {
	tk_interp_image(imf, img, force);
    }
    return imf;
}

static void
tk_interp_image(ImageFamily *imf, Image *img, int force)
{
    char namebuf[BUFSIZE];
    int w, h, numbytes;
    int dummy, red, grn, blu;
    TkImageFamily *tkimf;
    TkImage *tkimg;
    Tk_Window rootwin;
    Tk_Uid bitmapid;
    XColor col;

    w = img->w;  h = img->h;
    tkimg = get_tk_image(img);
    tkimf = (TkImageFamily *) imf->hook;
    if (tkimf == NULL)
      return;
    rootwin = tkimf->rootwin;

    /* A 1x1 image is just a color - make it into a solid color. */
    if (w == 1 && h == 1 && img->palette != lispnil) {
	/* force this? */
	img->numcolors = 1;
	parse_lisp_palette_entry(car(img->palette), &dummy, &red, &grn, &blu);
	col.red = red;  col.green = grn;  col.blue = blu;
	tkimg->solid = Tk_GetColorByValue(rootwin, &col);
	if (tkimg->solid == NULL)
	  init_warning("Cannot get color #%2.2x%2.2x%2.2x for %s",
		       col.red, col.green, col.blue, imf->name);
    }
    if (img->monodata != lispnil && (img->rawmonodata == NULL || force)) {
	numbytes = h * computed_rowbytes(w, 1);
	img->rawmonodata = xmalloc(numbytes);
	interp_bytes(img->monodata, numbytes, img->rawmonodata, 0);
    }
    if (img->rawmonodata && (tkimg->mono == None || force)) {
	numbytes = h * computed_rowbytes(w, 1);
	reverse_bit_endianness(img->rawmonodata, numbytes);
	sprintf(namebuf, "%s.%d.%d.mono", imf->name, w, h);
	bitmapid = Tk_GetUid(namebuf);
	Tk_DefineBitmap(interp, bitmapid, img->rawmonodata, w, h);
	tkimg->mono = Tk_GetBitmap(interp, rootwin, bitmapid);
	reverse_bit_endianness(img->rawmonodata, numbytes);
    }
    if (img->maskdata != lispnil && (img->rawmaskdata == NULL || force)) {
	numbytes = h * computed_rowbytes(w, 1);
	img->rawmaskdata = xmalloc(numbytes);
	interp_bytes(img->maskdata, numbytes, img->rawmaskdata, 0);
    }
    if (img->rawmaskdata && (tkimg->mask == None || force)) {
	numbytes = h * computed_rowbytes(w, 1);
	reverse_bit_endianness(img->rawmaskdata, numbytes);
	sprintf(namebuf, "%s.%d.%d.mask", imf->name, w, h);
	bitmapid = Tk_GetUid(namebuf);
	Tk_DefineBitmap(interp, bitmapid, img->rawmaskdata, w, h);
	tkimg->mask = Tk_GetBitmap(interp, rootwin, bitmapid);
	reverse_bit_endianness(img->rawmaskdata, numbytes);
    }
    if (img->colrdata != lispnil && (img->rawcolrdata == NULL || force)) {
	numbytes = h * computed_rowbytes(w, img->pixelsize);
	img->rawcolrdata = xmalloc(numbytes);
	interp_bytes(img->colrdata, numbytes, img->rawcolrdata, 0);
    }
    if (img->rawcolrdata && (tkimg->colr == None || force)) {
	tk_make_color_pixmap(rootwin, imf, img);
    }
}

/* TK bitmaps are always in little-endian bit order, while IMF images
   are always big-endian in bit order, so we must reverse the bits
   in each byte individually. */

void
reverse_bit_endianness(char *rawdata, int numbytes)
{
    int i, j, byte, byte2;

    for (i = 0; i < numbytes; ++i) {
	byte = rawdata[i];
	byte2 = 0;
	for (j = 0; j < 8; ++j) {
	    byte2 = (byte2 << 1) | (byte & 1);
	    byte >>= 1;
	}
	rawdata[i] = byte2;
    }
}

static void
tk_make_color_pixmap(Tk_Window rootwin, ImageFamily *imf, Image *img)
{
    int ipal[4][256];
    int n, r, ri, rc, depth, c, ln, rsize, rmask;
    XColor col, *color;
    char *rp;
    Obj *pal;
    Colormap cmap;
    GC gc;
    TkImage *tkimg = (TkImage *) img->hook;
    Display *dpy = Tk_Display(rootwin);

    if (tkimg == NULL || img->rawcolrdata == NULL)
      return;
    /* Can't make color pixmaps if we don't have any colors. */
    if (img->palette == lispnil && img->rawpalette == NULL)
      return;
    if (img->rawpalette == NULL) {
	/* Parse the Lispified palette. */
        /* (should allocate and store directly instead of using ipal) */
	c = 0;
	for_all_list(img->palette, pal) {
	    parse_lisp_palette_entry(car(pal), &ipal[0][c],
				     &ipal[1][c], &ipal[2][c], &ipal[3][c]);
	    c++;
	}
	img->numcolors = c;
	if (c == 0)
	  return;
	/* store palette */
	img->rawpalette = (int *) xmalloc(img->numcolors * 4 * sizeof(int));
	for (c = 0; c < img->numcolors; c++) {
	    for (ln = 0; ln < 4; ln++) {
		img->rawpalette[4 * c + ln] = ipal[ln][c];
	    }
	}
    }
    Tk_MakeWindowExist(rootwin);
    depth = DefaultDepthOfScreen(Tk_Screen(rootwin));
    /* Don't bother if we can only muster a monochrome screen. */
    if (depth == 1)
      return;
    if (img->numcolors <= 0) {
	run_warning("No colors?");
	return;
    }
    /* Allocate colors. */
    tkimg->cmap = (XColor **) xmalloc(256 /*img->numcolors*/ * sizeof(XColor *));
    for (c = 0; c < img->numcolors; c++) {
	col.red   = img->rawpalette[4 * c + 1];
	col.green = img->rawpalette[4 * c + 2];
	col.blue  = img->rawpalette[4 * c + 3];
	color = Tk_GetColorByValue(rootwin, &col);
	if (color == NULL)
	  init_warning("Cannot get color #%2.2x%2.2x%2.2x for %s",
		       col.red, col.green, col.blue, imf->name);
	tkimg->cmap[img->rawpalette[4 * c + 0]] = color;
    }
    tkimg->colr = Tk_GetPixmap(dpy, Tk_WindowId(rootwin), img->w, img->h,
			       depth);
    /* (should freak out if we can't make pixmaps?) */
    if (tkimg->colr == None)
      return;
    /* Draw the image by plotting each point separately. */
    rsize = img->pixelsize;
    rmask = (1 << img->pixelsize) - 1;
    rp = img->rawcolrdata;
    gc = Tk_GetGC(rootwin, None, NULL);
    XSetClipMask(dpy, gc, None);
    XSetFillStyle(dpy, gc, FillSolid);
    for (r = 0; r < img->h; r++) {
	ri = 8 - img->pixelsize;
	for (c = 0; c < img->w; c++) {
	    /* imf decoding stuff */
	    rc = ((int) (*rp >> ri)) & rmask;
	    if (ri) {
		ri -= img->pixelsize;
	    } else {
		ri = 8 - img->pixelsize;
		rp++;
	    }
	    if (tkimg->cmap[rc] && rc < 256)
	      XSetForeground(dpy, gc, tkimg->cmap[rc]->pixel);
	    XFillRectangle(dpy, tkimg->colr, gc, c, r, 1, 1);
	}
	if ((img->pixelsize * img->w) % 8) {
	    rp++;
	}
    }
    Tk_FreeGC(dpy, gc);
}

void
make_generic_image_data(ImageFamily *imf)
{
    /* (should write impl?) */
}
