#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#include <png.h>                /* PNG library definitions */

#include "image.h"

Image *
image_load_png (const char *filename)
{
  int             i;            /* Looping var */
  int             bpp;          /* Bytes per pixel */
  int             num_passes;   /* Number of interlace passes in file */
  int             pass;         /* Current pass in file */
  FILE           *fp;           /* File pointer */
  png_structp     pp;           /* PNG read pointer */
  png_infop       info;         /* PNG info pointers */
  unsigned char **pixels;       /* Pixel rows */

  Image *volatile image;        /* Image */

  pp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  info = png_create_info_struct (pp);

  if (setjmp (pp->jmpbuf))
    {
      printf ("Error while reading '%s'. File corrupted?\n", filename);
      return image;
    }

  /* initialise image here, thus avoiding compiler warnings */
  image = NULL;

  /* Open the file and initialize the PNG read "engine"...  */
  fp = fopen (filename, "r");
  if (fp == NULL)
    {
      printf ("Could not open '%s' for reading: %s\n", filename, strerror (errno));
      return NULL;
    }
  png_init_io (pp, fp);
  png_read_info (pp, info);

  if (info->bit_depth == 16)
    png_set_strip_16 (pp);

  if (info->color_type == PNG_COLOR_TYPE_GRAY && info->bit_depth < 8)
    png_set_expand (pp);

  /* Expand G+tRNS to GA, RGB+tRNS to RGBA */

  if (info->valid & PNG_INFO_tRNS)
    png_set_expand (pp);

  /*
   * Turn on interlace handling... libpng returns just 1 (ie single pass)
   * if the image is not interlaced
   */

  num_passes = png_set_interlace_handling (pp);

  /* Update the info structures after the transformations take effect */
  png_read_update_info (pp, info);

  image = malloc (sizeof (Image));
  image->width  = info->width;
  image->height = info->height;

  switch (info->color_type)
    {
    case PNG_COLOR_TYPE_RGB:           /* RGB */
      bpp = 3;
      image->type = IMAGE_TYPE_RGB;
      break;

    case PNG_COLOR_TYPE_RGB_ALPHA:     /* RGBA */
      bpp = 4;
      image->type = IMAGE_TYPE_RGBA;
      break;

    case PNG_COLOR_TYPE_GRAY:          /* Grayscale */
      bpp = 1;
      image->type = IMAGE_TYPE_GRAY;
      break;

    case PNG_COLOR_TYPE_GRAY_ALPHA:    /* Grayscale + alpha */
      bpp = 2;
      image->type = IMAGE_TYPE_GRAYA;
      break;

    default:                   /* Aie! Unknown type */
      printf ("Unknown color model in PNG file '%s'.\n", filename);
      return NULL;
    }
  image->data = malloc (info->height * info->width * bpp);

  /* png decodes by row... */
  pixels = malloc (sizeof (unsigned char *) * info->height);

  for (i = 0; i < image->height; i++)
    pixels[i] = image->data + info->width * info->channels * i;

  for (pass = 0; pass < num_passes; pass++)
    png_read_rows (pp, pixels, NULL, image->height);

  png_read_end (pp, info);

  png_destroy_read_struct (&pp, &info, NULL);

  free (pixels);
  free (pp);
  free (info);

  fclose (fp);

  return image;
}

