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

#include <png.h>

#include "simplewidget.h"
#include "_png.h"
#include "_types.h"

/* Enables or disables debug output */
#ifdef __DEBUG
#define __D(fmt, args...) fprintf(stderr, "PNG Debug: " fmt, ## args)
#else
#define __D(fmt, args...)
#endif

#define __E(fmt, args...) fprintf(stderr, "PNG Error: " fmt, ## args)


int png_create(char *filename, _simplepng *pngp)
{
    __u16 *dst;
    unsigned char *row_pointers[576];
    int r, g, b;
    int x, y;
    png_structp png_ptr;
    png_infop info_ptr;
    png_uint_32 width, height;
    int bit_depth, color_type, interlace_type;
    FILE *infile;
    int pos;

    infile = fopen(filename, "rb");

    if (infile == NULL) {
        __E("Failed to open image file [%s]\n", filename);
        return SIMPLEWIDGET_FAILURE;
    }

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

    if (png_ptr == NULL) {
        __E("Failed to create read struct\n");
        return SIMPLEWIDGET_FAILURE;
    }

    info_ptr = png_create_info_struct(png_ptr);

    if (info_ptr == NULL) {
        __E("Failed to create info struct\n");
        png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
        return SIMPLEWIDGET_FAILURE;
    }

    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
        fclose(infile);
        return SIMPLEWIDGET_FAILURE;
    }

    png_init_io(png_ptr, infile);

    png_read_info(png_ptr, info_ptr);
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
                 &interlace_type, int_p_NULL, int_p_NULL);

    png_set_strip_16(png_ptr);
    png_set_strip_alpha(png_ptr);
    png_set_packing(png_ptr);
    png_set_packswap(png_ptr);

    if (color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_palette_to_rgb(png_ptr);

    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
        png_set_gray_1_2_4_to_8(png_ptr);

    for (y=0; y<height; y++) {
        row_pointers[y] = png_malloc(png_ptr,
                                     png_get_rowbytes(png_ptr, info_ptr));
    }

    png_read_image(png_ptr, row_pointers);
    png_read_end(png_ptr, info_ptr);

    pngp->bufp = malloc(width * height * 2);
    pngp->w = width;
    pngp->h = height;

    if (pngp->bufp == NULL) {
        __E("Failed to allocate memory for png image\n");
        return SIMPLEWIDGET_FAILURE;
    }

    dst = pngp->bufp;

    for (y=0; y<height; y++) {
        for (x=0; x<width; x++) {
            pos = x * 3;
            r = row_pointers[y][pos + 0] * (1<<5) / (1<<8);
            g = row_pointers[y][pos + 1] * (1<<6) / (1<<8);
            b = row_pointers[y][pos + 2] * (1<<5) / (1<<8);

            dst[x] = b | (g << 5) | (r << 11);
        }

        dst += width;
    }

    for (y=0; y<height; y++) {
        png_free(png_ptr, row_pointers[y]);
    }

    png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

    fclose(infile);

    return SIMPLEWIDGET_SUCCESS;
}

int png_show(_simplepng *pngp, _simplescreen *scrp, int x, int y)
{
    __u16 *dst = scrp->bufp + y * scrp->w + x;
    __u16 *src = pngp->bufp;
    int i, j;

    for (j=0; j<scrp->h && j<pngp->h; j++) {
        for (i=0; i<scrp->w && i<pngp->w; i++) {
            dst[i] = src[i];
        }
        dst += scrp->w;
        src += pngp->w;
    }

    return SIMPLEWIDGET_SUCCESS;
}

int png_delete(_simplepng *pngp)
{
    free(pngp->bufp);

    return SIMPLEWIDGET_SUCCESS;
}
