/*
 * Copyright 2012, Gerasim Troeglazov, 3dEyes@gmail.com. All rights reserved.
 * Distributed under the terms of the MIT License.
 */ 
 
#include "ICNSLoader.h"
#include "BaseTranslator.h"
 
 
static int compareTypes(const void *a, const void *b)
{
	icns_type_t typeItemA = *((icns_type_t*)(*((icns_type_t*)a)));
	icns_type_t typeItemB = *((icns_type_t*)(*((icns_type_t*)b)));
	
	icns_icon_info_t imageInfoA = icns_get_image_info_for_type(typeItemA);
	icns_icon_info_t imageInfoB = icns_get_image_info_for_type(typeItemB);
 
	return imageInfoB.iconWidth - imageInfoA.iconWidth;
}
 
 
icns_type_t
ICNSFormat(float width, float height, color_space colors)
{
	int imageWidth = (int)ceil(width);
	int imageHeight = (int)ceil(height);
	
	if (imageWidth != imageHeight)
		return ICNS_NULL_TYPE;
	
	//other colors depth not supported now
	if (colors != B_RGB32 && colors != B_RGBA32)
		return ICNS_NULL_TYPE;
		
	switch (imageWidth) {
		case 16:
			return ICNS_16x16_32BIT_DATA;
		case 32:
			return ICNS_32x32_32BIT_DATA;
		case 48:
			return ICNS_48x48_32BIT_DATA;
		case 128:
			return ICNS_128X128_32BIT_DATA;
		case 256:
			return ICNS_256x256_32BIT_ARGB_DATA;
		case 512:
			return ICNS_512x512_32BIT_ARGB_DATA;
		case 1024:
			return ICNS_1024x1024_32BIT_ARGB_DATA;
	}
	return ICNS_NULL_TYPE;
}
 
 
ICNSLoader::ICNSLoader(BPositionIO *stream)
{
	fLoaded = false;
	fIconsCount = 0;
	
	stream->Seek(0, SEEK_END);
	fStreamSize = stream->Position();
	stream->Seek(0, SEEK_SET);
	
	if (fStreamSize <= 0)
		return;
		
	uint8* icnsDataBuffer = new uint8[fStreamSize];
	size_t readedBytes = stream->Read(icnsDataBuffer,fStreamSize);
	
	fIconFamily = NULL;
	int status = icns_import_family_data(readedBytes, icnsDataBuffer,
		&fIconFamily);
	
	if (status != 0) {
		delete[] icnsDataBuffer;
		return;
	}
 
	icns_byte_t *dataPtr = (icns_byte_t*)fIconFamily;
	off_t dataOffset = sizeof(icns_type_t) + sizeof(icns_size_t);
 
	while ((dataOffset+8) < fIconFamily->resourceSize) {
		icns_element_t	 iconElement;
		icns_size_t      iconDataSize;
 
		memcpy(&iconElement, (dataPtr + dataOffset), 8);
		iconDataSize = iconElement.elementSize - 8;
 
		if (IS_SPUPPORTED_TYPE(iconElement.elementType)) {
				icns_type_t* newTypeItem = new icns_type_t;
				*newTypeItem = iconElement.elementType;
				fFormatList.AddItem(newTypeItem);
				fIconsCount++;
		}		
		dataOffset += iconElement.elementSize;
	}
			
	fFormatList.SortItems(compareTypes);
	
	delete[] icnsDataBuffer;
 
	fLoaded = true;
}
 
 
ICNSLoader::~ICNSLoader()
{
	if (fIconFamily != NULL)
		free(fIconFamily);
 
	icns_type_t* item;
	for (int32 i = 0; (item = (icns_type_t*)fFormatList.ItemAt(i)) != NULL; i++)
   		delete item;
   	fFormatList.MakeEmpty();
}
 
 
bool
ICNSLoader::IsLoaded(void)
{
	return fLoaded;
}
	
	
int
ICNSLoader::IconsCount(void)
{
	return fIconsCount;
}
 
 
int
ICNSLoader::GetIcon(BPositionIO *target, int index)
{
	if (index < 1 || index > fIconsCount || !fLoaded)
		return B_NO_TRANSLATOR;
		
	icns_image_t iconImage;
	memset(&iconImage, 0, sizeof(icns_image_t));
			
	icns_type_t typeItem = *((icns_type_t*)fFormatList.ItemAt(index - 1));	
	int status = icns_get_image32_with_mask_from_family(fIconFamily,
		typeItem, &iconImage);
		
	if (status != 0)
		return B_NO_TRANSLATOR;
											
	TranslatorBitmap bitsHeader;	
	bitsHeader.magic = B_TRANSLATOR_BITMAP;
	bitsHeader.bounds.left = 0;
	bitsHeader.bounds.top = 0;
	bitsHeader.bounds.right = iconImage.imageWidth - 1;
	bitsHeader.bounds.bottom = iconImage.imageHeight - 1;
	bitsHeader.rowBytes = sizeof(uint32) * iconImage.imageWidth;
	bitsHeader.colors = B_RGBA32;
	bitsHeader.dataSize = bitsHeader.rowBytes * iconImage.imageHeight;
	if (swap_data(B_UINT32_TYPE, &bitsHeader,
		sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
		icns_free_image(&iconImage);
		return B_NO_TRANSLATOR;
	}
	target->Write(&bitsHeader, sizeof(TranslatorBitmap));			
 
	uint8 *rowBuff = new uint8[iconImage.imageWidth * sizeof(uint32)];
	for (uint32 i = 0; i < iconImage.imageHeight; i++) {
		uint8 *rowData = iconImage.imageData 
			+ (i * iconImage.imageWidth * sizeof(uint32));
		uint8 *rowBuffPtr = rowBuff;
		for (uint32 j=0; j < iconImage.imageWidth; j++) {
			rowBuffPtr[0] = rowData[2];
			rowBuffPtr[1] = rowData[1];
			rowBuffPtr[2] = rowData[0];
			rowBuffPtr[3] = rowData[3];
			rowBuffPtr += sizeof(uint32);
			rowData += sizeof(uint32);
		}
		target->Write(rowBuff, iconImage.imageWidth * sizeof(uint32));
	}				
	delete[] rowBuff;	
	icns_free_image(&iconImage);
	
	return B_OK;
}
 
 
ICNSSaver::ICNSSaver(BPositionIO *stream, uint32 rowBytes, icns_type_t type)
{
	fCreated = false;	
 
	icns_icon_info_t imageTypeInfo = icns_get_image_info_for_type(type);	
	int iconWidth = imageTypeInfo.iconWidth;
	int iconHeight = imageTypeInfo.iconWidth;
	int bpp = 32;
 
	uint8 *bits = new uint8[iconWidth * iconHeight * sizeof(uint32)];
	
	uint8 *rowPtr = bits;
	for (int i = 0; i < iconHeight; i++) {
		stream->Read(rowPtr, rowBytes);
		uint8 *bytePtr = rowPtr;
		for (int j=0; j < iconWidth; j++) {
			uint8 temp = bytePtr[0];
			bytePtr[0] = bytePtr[2];
			bytePtr[2] = temp;
			bytePtr += sizeof(uint32);
		}
		rowPtr += iconWidth * sizeof(uint32);
	}
	
	icns_create_family(&fIconFamily);
	
	icns_image_t icnsImage;	
	icnsImage.imageWidth = iconWidth;
	icnsImage.imageHeight = iconHeight;
	icnsImage.imageChannels = 4;
	icnsImage.imagePixelDepth = 8;
	icnsImage.imageDataSize = iconWidth * iconHeight * 4;
	icnsImage.imageData = bits;		
 
	icns_icon_info_t iconInfo;
	iconInfo.isImage = 1;
	iconInfo.iconWidth = icnsImage.imageWidth;
	iconInfo.iconHeight = icnsImage.imageHeight;
	iconInfo.iconBitDepth = bpp;
	iconInfo.iconChannels = (bpp == 32 ? 4 : 1);
	iconInfo.iconPixelDepth = bpp / iconInfo.iconChannels;
 
	icns_type_t iconType = icns_get_type_from_image_info(iconInfo);
	
	if (iconType == ICNS_NULL_TYPE) {
		delete[] bits;
		free(fIconFamily);
		fIconFamily = NULL;
		return;
	}
 
	icns_element_t *iconElement = NULL;
	int icnsErr = icns_new_element_from_image(&icnsImage, iconType,
		&iconElement);
		
	if (iconElement != NULL) {
		if (icnsErr == ICNS_STATUS_OK) {
			icns_set_element_in_family(&fIconFamily, iconElement);
			fCreated = true;
		}
		free(iconElement);
	}
	
	if (iconType != ICNS_1024x1024_32BIT_ARGB_DATA
		&& iconType != ICNS_512x512_32BIT_ARGB_DATA
		&& iconType != ICNS_256x256_32BIT_ARGB_DATA) {
		icns_type_t maskType = 
			icns_get_mask_type_for_icon_type(iconType);
		
		icns_image_t icnsMask;
		icns_init_image_for_type(maskType, &icnsMask);
 
		uint32 iconDataOffset = 0;
		uint32 maskDataOffset = 0;
	
		while (iconDataOffset < icnsImage.imageDataSize 
			&& maskDataOffset < icnsMask.imageDataSize) {
			icnsMask.imageData[maskDataOffset] =
				icnsImage.imageData[iconDataOffset + 3];
			iconDataOffset += 4;
			maskDataOffset += 1;
		}
		
		icns_element_t *maskElement = NULL;
		icnsErr = icns_new_element_from_mask(&icnsMask, maskType,
			&maskElement);
 
		if (maskElement != NULL) {
			if (icnsErr == ICNS_STATUS_OK) {
				icns_set_element_in_family(&fIconFamily,
					maskElement);
			} else
				fCreated = false;
			free(maskElement);
		}		
		icns_free_image(&icnsMask);
	}	
		
	if (!fCreated) {
		free(fIconFamily);
		fIconFamily = NULL;
	}
	
	delete[] bits;
}
 
 
ICNSSaver::~ICNSSaver()
{
	if (fIconFamily != NULL)
		free(fIconFamily);	
}
 
 
int
ICNSSaver::SaveData(BPositionIO *target)
{
	icns_size_t dataSize;
	icns_byte_t *dataPtrOut;
	icns_export_family_data(fIconFamily, &dataSize, &dataPtrOut);
	if (dataSize != 0 && dataPtrOut != NULL) {
		if (target->Write(dataPtrOut, dataSize) == dataSize)
			return B_OK;
	}
	return B_ERROR;
}
 
 
bool
ICNSSaver::IsCreated(void)
{
	return fCreated;
}

V547 Expression 'bpp == 32' is always true.