//	VirtualDub - Video processing and capture application
//	Modification for PNG support
//  Added by Cyrius, using libpng 1.2.4 and zlib 1.1.4
//
//	This program is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation; either version 2 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include <stdio.h>
#include <crtdbg.h>

#include "../VideoSource.h"

#include "../Error.h"
#include "../AVIOutput.h"
#include "../AVIOutputImages.h"
#include "AVIOutputImagesPNG.h"

class AVIOutputImages;

void png_write_data_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
	HANDLE h = (HANDLE)png_get_io_ptr(png_ptr);
	if(!h)
		png_error(png_ptr, "No file Handle provided");

	DWORD write = 0;
	BOOL success = WriteFile(h, data, length, &write, NULL);
	if(!success)
		throw 0;

	if(write != length)
		png_error(png_ptr, "Unable to write all the data to file");
}

void png_flush_data_fn(png_structp png_ptr) {
	// No flush to do
}

AVIVideoImagePNGOutputStream::AVIVideoImagePNGOutputStream(AVIOutput *out, const char *pszPrefix, const char *pszSuffix, int iDigits)
: AVIVideoImageOutputStream(out, pszPrefix, pszSuffix, iDigits,false)// pulco 04/11/2002 : TARGA is "false"
{
	png_ptr = NULL;
	info_ptr = NULL;
}

AVIVideoImagePNGOutputStream::~AVIVideoImagePNGOutputStream(void) {
	_reset();
}

void AVIVideoImagePNGOutputStream::_reset(void) {
	if(png_ptr || info_ptr)
		png_destroy_read_struct(
		(png_ptr ? &png_ptr : (png_structpp)NULL)
		, (info_ptr ? &info_ptr : (png_infopp)NULL)
		, NULL);
	png_ptr = NULL;
	info_ptr = NULL;
}

BOOL AVIVideoImagePNGOutputStream::write(LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer, LONG lSamples) {
	BITMAPFILEHEADER bfh;
	char szFileName[MAX_PATH];
	HANDLE hFile;
	DWORD dwActual;

	sprintf(szFileName, "%s%0*d%s", mpszPrefix, iDigits, dwFrame++, mpszSuffix);

	hFile = CreateFile(
				szFileName,
				GENERIC_WRITE,
				0,
				NULL,
				CREATE_ALWAYS,
				FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
				NULL
				);

	if (!hFile) return FALSE;

	_reset();

	//////////////////////////////////////////////////
	png_byte *pDiData = (png_byte *)lpBuffer;
	const int           ciBitDepth = 8;
	const int           ciChannels = 3;

	png_uint_32         ulRowBytes;
	static png_byte   **ppbRowPointers = NULL;
	int                 i;

	// open the PNG output file

	// prepare the standard PNG structures

	try {
		png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
			(png_error_ptr)png_error_fn, (png_error_ptr)png_warning_fn);
		if(!png_ptr)
			throw MyMemoryError();

		info_ptr = png_create_info_struct(png_ptr);
		if(!info_ptr)
			throw MyMemoryError();

		// initialize the png structure
        
		png_set_write_fn(png_ptr, (png_voidp)hFile, png_write_data_fn, png_flush_data_fn);
        
		int iWidth = getImageFormat()->biWidth;
		int iHeight = getImageFormat()->biHeight;

		int iBitCount = getImageFormat()->biBitCount;
		if(iBitCount == 16)
			throw MyError("16-bit output is not supported when saving to PNG");

		int iColorType = (iBitCount == 24 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA);

		png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth,
			iColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
			PNG_FILTER_TYPE_DEFAULT);
        
		// write the file header information
        
		png_write_info(png_ptr, info_ptr);

		// transparancy

		// swap the BGR pixels in the DiData structure to RGB
		png_set_bgr(png_ptr);
        
		// row_bytes is the width x number of channels
        
		//ulRowBytes = iWidth * ciChannels;
		// size of one line must be a multiple of 4 (>>5 <=> /32)
		ulRowBytes = ((iWidth*iBitCount+31)>>5)*4;
        
		// we can allocate memory for an array of row-pointers
        
		if((ppbRowPointers = (png_bytepp)malloc(iHeight * sizeof(png_bytep))) == NULL)
			throw MyMemoryError();
        
		// set the individual row-pointers to point at the correct offsets
    // set upside-down :)
		for(i = 0; i < iHeight; i++)
			// size of line already a multiple of 4 :)
			//ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2);
			ppbRowPointers[i] = pDiData + (iHeight - i - 1) * ulRowBytes;
        
		// write out the entire image data in one call
        
		png_write_image(png_ptr, ppbRowPointers);
        
		// write the additional chunks to the PNG file (not really needed)
        
		png_write_end(png_ptr, info_ptr);
        
		// and we're done
        
		free(ppbRowPointers);
		ppbRowPointers = NULL;
        
		// yepp, done
	} catch(int) {
		CloseHandle(hFile);
		if(ppbRowPointers)
			free(ppbRowPointers);
		throw MyWin32Error("Error writing image: %%s", GetLastError());
	} catch(...) {
		CloseHandle(hFile);
		if(ppbRowPointers)
			free(ppbRowPointers);
		throw;
	}

	if (!CloseHandle(hFile)) return FALSE;

	return TRUE;
}
