#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <csetjmp>

#include "libpng/png.h"
#include "readpng.h"
#include "writepng.h"
#include "loadpng.h"

namespace ogl2d
{
namespace Utilities
{

#ifndef NULL
#   define NULL 0
#endif

#ifndef TRUE
#   define TRUE 1
#endif

#ifndef FALSE
#   define FALSE 0
#endif

#ifndef min
#   define min(a,b) ((a)>(b)?(b):(a))
#endif

#ifndef max
#   define max(a,b) ((a)>(b)?(a):(b))
#endif

int DecodePngFileToGeneralBuffer(
    const char* lpszPathName,
    unsigned short* width,
    unsigned short* height,
    unsigned short* nchannels,
    unsigned char** buffer)
{
    FILE *fp = fopen(lpszPathName, "rb");
    if (fp == NULL)
        return FALSE;

    ulg w, h;
    if (::readpng_init(fp, &w, &h) != 0)
    {
        return FALSE;
    }

    double display_exponent = 2.2f;
    // int readpng_get_bgcolor(uch *bg_red, uch *bg_green, uch *bg_blue);

    int channel;
    ulg rowbytes;
    uch *buf = ::readpng_get_image(display_exponent, &channel, &rowbytes);

    if (buf == NULL)
        return FALSE;

    fclose(fp);

    *width = (unsigned short)w;
    *height = (unsigned short)h;
    *nchannels = channel;
    *buffer = buf;

    return TRUE;
}

/// CleanupPngGeneralBuffer
/// @note 释放由DecodePnGFileToGeneralBuffer分配的内存空间
void CleanupPngGeneralBuffer()
{
    readpng_cleanup(TRUE);
} // CleanupPngGeneralBuffer()

int DecodePngBufferToGeneralBuffer(
    const unsigned char* pngdata,
    unsigned long pngdatasize,
    unsigned short* width,
    unsigned short* height,
    unsigned short* nchannels,
    unsigned char** buffer)
{
    ulg w, h;
    if (::readpng_init_buffer(const_cast< unsigned char* >(pngdata), pngdatasize, &w, &h) != 0)
        return FALSE;

    double display_exponent = 2.2f;
    // int readpng_get_bgcolor(uch *bg_red, uch *bg_green, uch *bg_blue);

    int channel;
    ulg rowbytes;
    uch *buf = ::readpng_get_image(display_exponent, &channel, &rowbytes);

    if (buf == NULL)
        return FALSE;

    *width = (unsigned short)w;
    *height = (unsigned short)h;
    *nchannels = channel;
    *buffer = buf;

    return TRUE;
}
#if 0
int EncodeGeneralBufferToPngBuffer(
    const unsigned char* buffer,
    unsigned short width,
    unsigned short height,
    unsigned short nchannels,
    unsigned char** pngbuffer,
    unsigned long *pngdatasize)
{
    ulg w, h;
    mainprog_info mp_info;
    mp_info.author = "ogl2dlib";
    mp_info.bFileIo = 0;
    mp_info.buffer.buffer = buffer;
    mp_info.buffer.curpos = 0;
    mp_info.buffer.size = 0;
    if (::writepng_init(&mp_info) != 0)
        return FALSE;

 
    if (wpng_info.interlaced) {
        long i;
        ulg bytes;
        ulg image_bytes = rowbytes * wpng_info.height;   /* overflow? */

        wpng_info.image_data = (uch *)malloc(image_bytes);
        wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
        if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
            fprintf(stderr, PROGNAME ":  insufficient memory for image data\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(5);
        }
        for (i = 0;  i < wpng_info.height;  ++i)
            wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
        bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
        if (bytes != image_bytes) {
            fprintf(stderr, PROGNAME ":  expected %lu bytes, got %lu bytes\n",
              image_bytes, bytes);
            fprintf(stderr, "  (continuing anyway)\n");
        }
        if (writepng_encode_image(&wpng_info) != 0) {
            fprintf(stderr, PROGNAME
              ":  libpng problem (longjmp) while writing image data\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(2);
        }

    } else /* not interlaced:  write progressively (row by row) */ {
        long j;
        ulg bytes;

        wpng_info.image_data = (uch *)malloc(rowbytes);
        if (wpng_info.image_data == NULL) {
            fprintf(stderr, PROGNAME ":  insufficient memory for row data\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(5);
        }
        error = 0;
        for (j = wpng_info.height;  j > 0L;  --j) {
            bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
            if (bytes != rowbytes) {
                fprintf(stderr, PROGNAME
                  ":  expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
                  bytes, wpng_info.height-j);
                ++error;
                break;
            }
            if (writepng_encode_row(&wpng_info) != 0) {
                fprintf(stderr, PROGNAME
                  ":  libpng problem (longjmp) while writing row %ld\n",
                  wpng_info.height-j);
                ++error;
                break;
            }
        }
        if (error) {
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(2);
        }
        if (writepng_encode_finish(&wpng_info) != 0) {
            fprintf(stderr, PROGNAME ":  error on final libpng call\n");
            writepng_cleanup(&wpng_info);
            wpng_cleanup();
            exit(2);
        }
    }



    return TRUE;
}
#endif

} // Utilities
} // ogl2d