#include "CmBitmapWriter.h"
#include "CmPixelData.h"

namespace CamelotEngine
{
#pragma pack(push, 2) // Align to 2byte boundary so we don't get extra 2 bytes for this struct

	struct BMP_HEADER
	{
		UINT16 BM;
		UINT32 size_of_file;
		UINT32 reserve;
		UINT32 offset_of_pixel_data;
		UINT32 size_of_header;
		UINT32 width;
		UINT32 hight;
		UINT16 num_of_color_plane;
		UINT16 num_of_bit_per_pix;
		UINT32 compression;
		UINT32 size_of_pix_data;
		UINT32 h_resolution;
		UINT32 v_resolution;
		UINT32 num_of_color_in_palette;
		UINT32 important_colors;

	};

#pragma pack(pop)

	void BitmapWriter::rawPixelsToBMP(const UINT8* input, UINT8* output, UINT32 width, UINT32 height, UINT32 bytesPerPixel)
	{
		UINT16 bmpBytesPerPixel = 3;
		if(bytesPerPixel >= 4)
			bmpBytesPerPixel = 4;

		UINT32 padding = (width * bmpBytesPerPixel) % 4;
		if(padding != 0)
			padding = 4 - padding;

		UINT32 rowPitch = (width * bmpBytesPerPixel) + padding;
		UINT32 dataSize = height * rowPitch;

		BMP_HEADER header;
		header.BM = 0x4d42;
		header.size_of_file =  sizeof(header) + dataSize;
		header.reserve = 0000; 
		header.offset_of_pixel_data = 54;
		header.size_of_header = 40;
		header.width = width;
		header.hight = height;
		header.num_of_color_plane = 1;
		header.num_of_bit_per_pix = bmpBytesPerPixel * 8;
		header.compression = 0;
		header.size_of_pix_data = dataSize;
		header.h_resolution = 2835;
		header.v_resolution = 2835;
		header.num_of_color_in_palette = 0;
		header.important_colors = 0;

		// Write header
		memcpy(output, &header, sizeof(header));
		output += sizeof(header);

		// Write bytes
		UINT32 widthBytes = width * bytesPerPixel;
		 
		// BPP matches so we can just copy directly
		if(bmpBytesPerPixel == bytesPerPixel)
		{
			for(INT32 y = height - 1; y >= 0 ; y--)
			{
				UINT8* outputPtr = output + y * rowPitch;

				memcpy(outputPtr, input, widthBytes);
				memset(outputPtr + widthBytes, 0, padding);

				input += widthBytes;
			}
		}
		else if(bmpBytesPerPixel < bytesPerPixel) // More bytes in source than supported in BMP, just truncate excess data
		{
			for(INT32 y = height - 1; y >= 0 ; y--)
			{
				UINT8* outputPtr = output + y * rowPitch;

				for(UINT32 x = 0; x < width; x++)
				{
					memcpy(outputPtr, input, bmpBytesPerPixel);
					outputPtr += bmpBytesPerPixel;
					input += bytesPerPixel;
				}

				memset(outputPtr, 0, padding);
			}
		}
		else // More bytes in BMP than in source (BMP must be 24bit minimum)
		{
			for(INT32 y = height - 1; y >= 0 ; y--)
			{
				UINT8* outputPtr = output + y * rowPitch;

				for(UINT32 x = 0; x < width; x++)
				{
					memcpy(outputPtr, input, bytesPerPixel);

					// Fill the empty bytes with the last available byte from input
					UINT32 remainingBytes = bmpBytesPerPixel - bytesPerPixel;
					while(remainingBytes > 0)
					{
						memcpy(outputPtr + (bmpBytesPerPixel - remainingBytes), input, 1);
						remainingBytes--;
					}

					outputPtr += bmpBytesPerPixel;
					input += bytesPerPixel;
				}

				memset(outputPtr, 0, padding);
			}
		}
	}

	UINT32 BitmapWriter::getBMPSize(UINT32 width, UINT32 height, UINT32 bytesPerPixel)
	{
		UINT16 bmpBytesPerPixel = 3;
		if(bytesPerPixel >= 4)
			bmpBytesPerPixel = 4;

		UINT32 padding = (width * bmpBytesPerPixel) % 4;
		if(padding != 0)
			padding = 4 - padding;

		UINT32 rowPitch = (width * bmpBytesPerPixel) + padding;
		UINT32 dataSize = height * rowPitch;

		return sizeof(BMP_HEADER) + dataSize;
	}
}