#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <png.h>

static void paint(gui *_gui, widget *_w)
{
  struct gui *g = _gui;
  struct image_widget *w = _w;
  LOGD("PAINT image %p\n", w);
  x_draw_image(g->x, g->xwin, w->x, w->common.x, w->common.y);
}

static void hints(gui *_gui, widget *_w, int *width, int *height)
{
  struct image_widget *w = _w;
  LOGD("HINTS image %p\n", w);
  *width = w->width;
  *height = w->height;
}

struct png_reader {
  unsigned char *data;
  int size;
  int pos;
};

static void png_readfn(png_structp png_ptr, png_bytep data, png_size_t length)
{
  struct png_reader *r = png_get_io_ptr(png_ptr);
  if (length > r->size - r->pos) png_error(png_ptr, "bad png image");
  memcpy(data, r->data + r->pos, length);
  r->pos += length;
}

static void load_image(struct gui *g, struct image_widget *w,
    unsigned char *data, int length)
{
  png_structp png_ptr;
  png_infop info_ptr;
  png_bytepp image;
  int width, height, bit_depth, color_type, channels;
  unsigned char *img_data;
  struct png_reader r;
  int i;

  /* unpack PNG data */

  r.data = data;
  r.size = length;
  r.pos = 0;

  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (png_ptr == NULL) abort();

  info_ptr = png_create_info_struct(png_ptr);
  if (info_ptr == NULL) abort();

  if (setjmp(png_jmpbuf(png_ptr))) abort();

  png_set_read_fn(png_ptr, &r, png_readfn);

  png_read_png(png_ptr, info_ptr,
      PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_PACKING |
      PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_BGR, NULL);

  image = png_get_rows(png_ptr, info_ptr);

  width = png_get_image_width(png_ptr, info_ptr);
  height = png_get_image_height(png_ptr, info_ptr);
  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
  color_type = png_get_color_type(png_ptr, info_ptr);
  channels = png_get_channels(png_ptr, info_ptr);

  if (width < 1 || width > 1000 || height < 1 || height > 1000 ||
      bit_depth != 8 || color_type != PNG_COLOR_TYPE_RGBA || channels != 4)
    { printf("bad image\n"); abort(); }

  img_data = malloc(4 * width * height); if (img_data == NULL) abort();
  for (i = 0; i < height; i++)
    memcpy(img_data+i*4*width, image[i], width*4);

  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

  /* create the X image */
  w->x = x_create_image(g->x, img_data, width, height);

  free(img_data);

  w->width = width;
  w->height = height;
}

widget *new_image(gui *_gui, unsigned char *data, int length)
{
  struct gui *g = _gui;
  struct image_widget *w;

  glock(g);

  w = new_widget(g, IMAGE, sizeof(struct image_widget));

  load_image(g, w, data, length);

  w->common.paint = paint;
  w->common.hints = hints;

  gunlock(g);

  return w;
}
