/*
 * (C) Copyright Ian D. Romanick 2006
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
 * AUTHORS, COPYRIGHT HOLDERS, AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <ctime> 
#include <string.h>

#ifdef __linux__
	#include <GL/glut.h>
	#include <GL/glext.h>
	#include <png.h>
	#include <pngconf.h>
	#include <zlib.h>
	#include <zconf.h>
#else
	#include "glut.h"
	#include "glext.h"
	#include "png.h"
	#include "pngconf.h"
	#include "zlib.h"
	#include "zconf.h"
#endif

#include "vector.h"
#include "load_image.h"
#include "data.h"

#if defined(_WIN32)
void fake_read_fn(png_structp png_ptr, png_bytep data, png_size_t count)
{
    FILE *fp = (FILE *) png_get_io_ptr(png_ptr);
    fread(data, 1, count, fp);
}
#endif

int LoadImage(GLenum target, GLuint level, GLenum internal_format, const char *filename)
{
    FILE *fp = fopen(filename, "rb");
    png_bytep *row_pointers;
    unsigned i;
    unsigned height;
    unsigned width;
    png_byte color_type;
    png_byte bit_depth;
    GLenum format;
    GLint gen_mipmap;


    if (fp == NULL) {
        return 0;
    }


    png_structp png_ptr = png_create_read_struct
        (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
        fclose(fp);
        return 0;
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        fclose(fp);
        return 0;
    }

    png_infop end_info = png_create_info_struct(png_ptr);
    if (!end_info) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        fclose(fp);
        return 0;
    }


#if defined(_WIN32)
    png_set_read_fn(png_ptr, (void *)fp, fake_read_fn);
#else
    png_init_io(png_ptr, fp);
#endif

    png_read_png(png_ptr, info_ptr, 0, NULL);

    row_pointers = png_get_rows(png_ptr, info_ptr);

    height = png_get_image_height(png_ptr, info_ptr);
    width = png_get_image_width(png_ptr, info_ptr);

    /* This should be made a LOT more robust.
     *
     * First of all, it should verify that the image has power-of-two
     * dimensions.  If not, the size of the texture should be rounded up.
     * 
     * Secondly, it should not assume that the incoming image is 8-bit per
     * channel RGBA.  Just like the dimensions are queried above, the image
     * format can be queried.  In fact, if the image format isn't quite what
     * we want, we can instruct libpng to massage it into a better format for
     * us while it loads.
     */
    color_type = png_get_color_type(png_ptr, info_ptr);
    switch (color_type) {
    case PNG_COLOR_TYPE_GRAY:
        format = GL_LUMINANCE;
        break;

    case PNG_COLOR_TYPE_RGB:
        format = GL_RGB;
        break;

    case PNG_COLOR_TYPE_RGB_ALPHA:
        format = GL_RGBA;
        break;

    case PNG_COLOR_TYPE_GRAY_ALPHA:
        format = GL_LUMINANCE_ALPHA;
        break;

    default:
        /* We can't currently handle palleted images.
         */
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        fclose(fp);
        return 0;
    }


    bit_depth = png_get_bit_depth(png_ptr, info_ptr);
//    printf("%s: %u bpp\n", filename, bit_depth);

    /* Since we call glTexSubImage2D many, many times, disable automatic
     * mipmap generate for every call except the last one.  Once we get to the
     * last line of the image, re-enable mipmap generation.  This prevents
     * silly drivers from regenerating the entire mipmap stack on every call
     * to glTexSubImage2D.
     */
    glGetTexParameteriv(target, GL_GENERATE_MIPMAP, &gen_mipmap);
    if (gen_mipmap) {
        glTexParameteri(target, GL_GENERATE_MIPMAP, GL_FALSE);
    }

    glTexImage2D(target, level, internal_format, width, height, 0,
                 format, GL_UNSIGNED_BYTE, NULL);

    for (i = 0; i < height; i++) {
        if (gen_mipmap && ((i + 1) == height)) {
            glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
        }

        glTexSubImage2D(target, level, 0, i,
                        width, 1, format, GL_UNSIGNED_BYTE,
                        row_pointers[i]);
    }

    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    fclose(fp);
    return 1;
}
