/* -LICENSE-START-
** Copyright (c) 2018 Blackmagic Design
**  
** Permission is hereby granted, free of charge, to any person or organization 
** obtaining a copy of the software and accompanying documentation (the 
** "Software") to use, reproduce, display, distribute, sub-license, execute, 
** and transmit the Software, and to prepare derivative works of the Software, 
** and to permit third-parties to whom the Software is furnished to do so, in 
** accordance with:
** 
** (1) if the Software is obtained from Blackmagic Design, the End User License 
** Agreement for the Software Development Kit (“EULA”) available at 
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
** 
** (2) if the Software is obtained from any third party, such licensing terms 
** as notified by that third party,
** 
** and all subject to the following:
** 
** (3) the copyright notices in the Software and this entire statement, 
** including the above license grant, this restriction and the following 
** disclaimer, must be included in all copies of the Software, in whole or in 
** part, and all derivative works of the Software, unless such copies or 
** derivative works are solely in the form of machine-executable object code 
** generated by a source language processor.
** 
** (4) 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT 
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
** DEALINGS IN THE SOFTWARE.
** 
** A copy of the Software is available free of charge at 
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
** 
** -LICENSE-END-
*/

#include <png.h>
#include <sys/stat.h>
#include <iomanip>
#include <sstream>
#include "ImageWriter.h"

static const uint32_t kPNGSignatureLength = 8;

HRESULT ImageWriter::GetNextFilenameWithPrefix(const std::string& path, const std::string& filenamePrefix, std::string& nextFileName)
{
	HRESULT	result = E_FAIL;
	static int idx = 0;
	struct stat buf;

	while (idx < 10000)
	{
		std::stringstream pngFilenameStream;
		pngFilenameStream << path << '/' << filenamePrefix << std::setfill('0') << std::setw(4) << idx++ << ".png";
		nextFileName = pngFilenameStream.str();

		// If file does not exist, return S_OK
		if (stat(nextFileName.c_str(), &buf) != 0)
		{
			result = S_OK;
			break;
		}
	}
	
	return result;
}

HRESULT ImageWriter::WriteBgra32VideoFrameToPNG(IDeckLinkVideoFrame* bgra32VideoFrame, const std::string& pngFilename)
{
	bool        result         = E_FAIL;
	png_structp pngDataPtr     = nullptr;
	png_infop   pngInfoPtr     = nullptr;
	png_bytep   deckLinkBuffer = nullptr;
	png_bytepp  rowPtrs        = nullptr;

	// Ensure video frame has expected pixel format
	if (bgra32VideoFrame->GetPixelFormat() != bmdFormat8BitBGRA)
	{
		fprintf(stderr, "Video frame is not in 8-Bit BGRA pixel format\n");
		return E_FAIL;
	}
	
	FILE *pngFile = fopen(pngFilename.c_str(), "wb");
	if (!pngFile)
	{
		fprintf(stderr, "Could not open PNG file %s for writing\n", pngFilename.c_str());
		return false;
	}

	pngDataPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!pngDataPtr)
	{
		fprintf(stderr, "Could not create PNG write struct\n");
		goto bail;
	}

	pngInfoPtr = png_create_info_struct(pngDataPtr);
	if (!pngInfoPtr)
	{
		fprintf(stderr, "Could not create PNG info struct\n");
		goto bail;
	}

	if (setjmp(png_jmpbuf(pngDataPtr)))
	{
		fprintf(stderr, "Failed PNG write initialization\n");
		goto bail;
	}

	png_init_io(pngDataPtr, pngFile);

	png_set_IHDR(pngDataPtr, pngInfoPtr, bgra32VideoFrame->GetWidth(), bgra32VideoFrame->GetHeight(),
					8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, 
					PNG_FILTER_TYPE_BASE);
	png_write_info(pngDataPtr, pngInfoPtr);

	png_set_bgr(pngDataPtr);

	if (bgra32VideoFrame->GetBytes((void**)&deckLinkBuffer) != S_OK)
	{
		fprintf(stderr, "Could not get DeckLinkVideoFrame buffer pointer\n");
		goto bail;
	}

	rowPtrs = (png_bytep*)malloc(sizeof(png_bytep) * bgra32VideoFrame->GetHeight());

	// Set row pointers from the buffer
	for (uint32_t row = 0; row < bgra32VideoFrame->GetHeight(); ++row)
		rowPtrs[row] = &deckLinkBuffer[row * bgra32VideoFrame->GetRowBytes()];

	png_write_image(pngDataPtr, rowPtrs);
	png_write_end(pngDataPtr, NULL);

	result = S_OK;

bail:
	fclose(pngFile);
	png_destroy_write_struct(&pngDataPtr, &pngInfoPtr);
	free(rowPtrs);
	
	return result;
}
