//
//  TKPXMResource.m
//  ThemeKit
//
//  Created by David Watson on Mon Dec 08 2003.
//  Copyright (c) 2003 Carpe Stellarem. All rights reserved.
//
//  Updated by Colin Cornaby on Mon Dec 08 2003.
//  Copyright (c) 2003 Carpe Stellarem. All rights reserved.
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.

//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#import "TKPXMResource.h"
#include <ApplicationServices/ApplicationServices.h>
//#include <QD/QDOffscreen.h>

SInt32 GetPixMapRowBytes(Rect inRect,SInt16 inPixelDepth);
SInt32 GetMaskSize(Rect inRect);
SInt32 GetImageSize(Rect inRect,SInt16 inDepth);

UInt8 *newPtr=0;

@implementation TKPXMResource

-(void)dealloc
{
	//if(newPtr)
	//	free(newPtr);
	[super dealloc];
}

- (void)awakeFromFetch
{
	[self parseHeader];
}

SInt32 GetPixMapRowBytes(Rect inRect,SInt16 inPixelDepth)
{
	SInt16 width = inRect.right - inRect.left;
	return (((width * inPixelDepth + 15) / 16) * 2);
}

SInt32 GetPixMapSize(Rect inRect, const SInt16 inPixelDepth)
{
	SInt16 height = inRect.bottom - inRect.top;
	return GetPixMapRowBytes(inRect,inPixelDepth) * height;
}

SInt32 GetMaskSize(Rect inRect)
{
	return GetPixMapSize(inRect, 1);
}

SInt32 GetImageSize(Rect inRect,SInt16 inDepth)
{
	return GetPixMapSize(inRect,inDepth);
}


- (id)init
{
	self = [super init];
	//[super initWithEntity:(NSEntityDescription*)entity insertIntoManagedObjectContext:(NSManagedObjectContext*)context];

    pxmHeader.mMaskCount = 0;
    pxmHeader.mCTSeedValue = pxmHeader.mImageCount = 0; 
    pxmHeader.mPixelDepth = 1;
    pxmHeader.mBounds.left = pxmHeader.mBounds.top = 0;
    pxmHeader.mBounds.right = pxmHeader.mBounds.bottom = 32;
        
    return self;
}

+(id)pxmWithData:(NSData *)inData
{
	return [[[TKPXMResource alloc] initWithData:inData] autorelease];
}

-(id)initWithData:(NSData *)inData
{
	[self setResourceData:inData];
	[self parseHeader];
	return self;
}

-(void)setResourceData:(NSData *)data
{
	[super setResourceData:data];
	[self parseHeader];
	return;
}

-(int)numImages
{
	return (int)pxmHeader.mImageCount;
}

-(NSString *)humanReadableType
{
	return @"Widget Image";
}

-(void)parseHeader
{
    UInt8   *thePtr;
    SInt16  maskMode;
    
    // First, we set the pointer to the beginning of the data object
    thePtr=(UInt8 *)[[NSMutableData dataWithData:[self resourceData]] bytes];
    
    // Get the image count with a +22 offset
    // this needs to be obtained before the mask
   // pxmHeader.mImageCount=NSSwapBigShortToHost(*((SInt16*)(thePtr+22)));
	pxmHeader.mImageCount=swapShort(*((SInt16*)(thePtr+22)),[self endianness], kNativeEndian);
    // Get the mask mode which is offset +2 from the beginning
    // the mask mode will either be 4,5,12 or 13
    // 4 & 12 mean that there will be an individual mask for each image while
    // 5 & 13 mean that there is a single mask for all images in the pxm#
    maskMode=swapShort(*((SInt16*)(thePtr+2)),[self endianness], kNativeEndian);

    if((maskMode==4)||(maskMode==12))
	{
        pxmHeader.mMaskCount=pxmHeader.mImageCount;
    }
	else
	{
        pxmHeader.mMaskCount=1;
	} 
	

    // Get the bounds of the rect.  We use a standard rect rather than an NSRect
    // to allow us to use the simpler code base
    pxmHeader.mBounds.bottom=swapShort(*((SInt16*)(thePtr+8)),[self endianness], kNativeEndian);
    pxmHeader.mBounds.right=swapShort(*((SInt16*)(thePtr+10)),[self endianness], kNativeEndian);
    
    // Get the pixel depth for the images
    pxmHeader.mPixelDepth=swapShort(*((SInt16*)(thePtr+12)),[self endianness], kNativeEndian);

	// Get the CLUT id number if the PXM uses one
	pxmHeader.mCTSeedValue=swapShort(*((SInt16*)(thePtr+20)),[self endianness], kNativeEndian);
} 

-(NSData *)resourceDataForPlatform:(int)platform
{
	if(resolveEndianness([self endianness]==resolveEndianness(platform)))
		return [self resourceData];
	NSMutableData *dataToReturn = [NSMutableData dataWithData:[self resourceData]];
	//swap out the entire header, short by short
	int i;
	for(i=0;i<24;i=i+2){
		short bytes = swapShort(*((SInt16*)([dataToReturn bytes]+i)),[self endianness], platform);
		[dataToReturn replaceBytesInRange:NSMakeRange(i, sizeof(bytes)) withBytes:&bytes];
	}
	return dataToReturn;
}

-(void)setImage:(NSImage *)inImage atIndex:(int)index type:(int)inType
{
	UInt8				*thePtr=(UInt8 *)[[NSMutableData dataWithData:[self resourceData]] bytes],*newPtr,*tempPtr;
	SInt32				theMaskSize,theImageSize,maskIndex;
	NSBitmapImageRep	*theRep;
	int					counter,counter1;

	// Set two temporary variables that store the image and mask sizes for offsets
	theMaskSize = GetMaskSize(pxmHeader.mBounds);
	theImageSize = GetImageSize(pxmHeader.mBounds,pxmHeader.mPixelDepth);
	
	// Fast forward past the header
	thePtr+=24;
	
	// Switch based on the image type
	switch(inType)
	{
		case CSPXMMask:
			if(pxmHeader.mMaskCount==1)
				maskIndex=0;
			else
				maskIndex=index;
			thePtr+=maskIndex*theMaskSize;
			theRep=[[NSBitmapImageRep alloc] initWithData:[inImage TIFFRepresentation]];
			newPtr=[theRep bitmapData];
			// Create a temporary holding spot to store the mask
			tempPtr=malloc(theMaskSize);
			// Zero out tempPtr
			for(counter=0;counter<theMaskSize;counter++)
				tempPtr[counter]=0;
			for(counter1=0;counter1<pxmHeader.mBounds.bottom;counter1++)
				for(counter=0;counter<[theRep bytesPerRow];counter++)
					tempPtr[(counter1*GetPixMapRowBytes(pxmHeader.mBounds,1))+counter]=
						NSSwapHostShortToBig(newPtr[(counter1*[theRep bytesPerRow])+counter]);
			[[NSMutableData dataWithData:[self resourceData]] replaceBytesInRange:NSMakeRange(24+(maskIndex*theMaskSize),theMaskSize) withBytes:tempPtr];
			free(tempPtr);
			break;
		case CSPXMTransparent:
			if(pxmHeader.mMaskCount==1)
				thePtr+=theMaskSize;
			else
				thePtr+=theMaskSize*pxmHeader.mMaskCount;
			thePtr+=index*theImageSize;
			
			theRep=[[NSBitmapImageRep alloc] initWithData:[inImage TIFFRepresentation]];
			newPtr=[theRep bitmapData];
			// Create a temporary holding spot to store the transparency
			tempPtr=malloc(theImageSize);
			// Initialize tempPtr with the current contents of the image
			for(counter=0;counter<theImageSize;counter++)
				tempPtr[counter]=thePtr[counter];
			// We only need the last byte of the incoming image and store it into the temporary location
			for(counter=0;counter<theImageSize/4;counter++)
				tempPtr[counter*4+3]=newPtr[counter];
			[[NSMutableData dataWithData:[self resourceData]] replaceBytesInRange:NSMakeRange(24+(pxmHeader.mMaskCount*theMaskSize)+theImageSize*index,theImageSize) withBytes:tempPtr];
			free(tempPtr);
			break;
		case CSPXMImage:
		case CSPXMComposite:			
			thePtr+=theMaskSize*pxmHeader.mMaskCount;
			thePtr+=index*theImageSize;
			theRep=[[NSBitmapImageRep alloc] initWithData:[inImage TIFFRepresentation]];
			newPtr=[theRep bitmapData];
			
			/* Determine if we want 32-bit or 16-bit data */
			if(pxmHeader.mPixelDepth == 32)
			{
				tempPtr=malloc(theImageSize);
				if([theRep samplesPerPixel] == 4)
				{
					for(counter=0;counter<theImageSize;counter+=4)
					{
						tempPtr[counter]=newPtr[counter];
						tempPtr[counter + 1]=newPtr[counter + 1];
						tempPtr[counter + 2]=newPtr[counter + 2];
						tempPtr[counter + 3]=thePtr[counter + 3];
					}
				}
				else
				{
					for(counter=0;counter<theImageSize*3/4;counter+=3)
					{
						tempPtr[counter + counter/3]=newPtr[counter];
						tempPtr[counter + counter/3 + 1]=newPtr[counter+1];
						tempPtr[counter + counter/3 + 2]=newPtr[counter+2];
						tempPtr[counter + counter/3 + 3]=thePtr[counter + counter/3 + 3];
					}
				}
				NSMutableData *newResourceData = [NSMutableData dataWithData:[self resourceData]];
				[newResourceData replaceBytesInRange:NSMakeRange(24 + (pxmHeader.mMaskCount*theMaskSize) + theImageSize*index,theImageSize) withBytes:tempPtr];
				[self setResourceData:newResourceData];
				free(tempPtr);
				break;
			}
			
			/* Obviously we now have only 16-bit to do */
			tempPtr = malloc(theImageSize/2);
			theImageSize /= 2;
			int tctr = 0;
			
			/* Allocate the pointer memory */
			tempPtr=malloc(theImageSize);
			if([theRep samplesPerPixel] == 4)
			{
				for(counter=0;counter<theImageSize*2;counter+=4)
				{
					/* Convert the 32-bit data to 16-bit */
					unsigned int color = *(unsigned *)(newPtr + counter);
					unsigned short r = (color & 0xFF000000) >> 24;
					unsigned short g = (color & 0x00FF0000) >> 16;
					unsigned short b = (color & 0x0000FF00) >> 8;
					unsigned short a = (color & 0x000000FF) >> 0;
					r = (int)((((double)r) / (double)0xFF) * (double)0x1F);
					g = (int)((((double)g) / (double)0xFF) * (double)0x1F);
					b = (int)((((double)b) / (double)0xFF) * (double)0x1F);
					a = (a == 0) ? 0 : 1;
					unsigned short lower2 = (a << 15) | (r << 10) | (g << 5) | (b << 0);
					tempPtr[tctr++] = *((unsigned char *)&lower2);
					tempPtr[tctr++] = *((unsigned char *)&lower2) + 1;
				}
			}
			else
			{
				for(counter=0;counter<theImageSize*6/4;counter+=3)
				{
					/* Convert the 32-bit data to 16-bit */
					unsigned short r = newPtr[counter];
					unsigned short g = newPtr[counter + 1];
					unsigned short b = newPtr[counter + 2];
					r = (int)((((double)r) / (double)0xFF) * (double)0x1F);
					g = (int)((((double)g) / (double)0xFF) * (double)0x1F);
					b = (int)((((double)b) / (double)0xFF) * (double)0x1F);
					unsigned short lower = (r << 10) | (g << 5) | (b << 0);
					tempPtr[tctr++] = *(unsigned char *)&lower;
					tempPtr[tctr++] = *(unsigned char *)(&lower + 1);
				}
			}
			
			NSMutableData *newResourceData = [NSMutableData dataWithData:[self resourceData]];
			[newResourceData replaceBytesInRange:NSMakeRange(24 + (pxmHeader.mMaskCount*theMaskSize) + theImageSize*index,theImageSize) withBytes:tempPtr];
			[self setResourceData:newResourceData];
			free(tempPtr);
			break;
	}	
}

-(NSImage *)getImage:(int)index type:(int)inType
{	
	free(newPtr);
	UInt8				*thePtr=(UInt8 *)[[NSMutableData dataWithData:[self resourceData]] bytes];
	SInt32				theMaskSize,theImageSize,maskIndex;
	NSImage				*theImage;
	NSBitmapImageRep	*theRep=nil;
	void				*theArray[1];
	int					counter;

	theMaskSize = GetMaskSize(pxmHeader.mBounds);
	theImageSize = GetImageSize(pxmHeader.mBounds,32);
	
	// Fast forward past the header
	thePtr+=24;
        
	switch(inType)
	{
		case CSPXMMask:
			if(pxmHeader.mMaskCount==1)
				maskIndex=0;
			else
				maskIndex=index;
			thePtr+=maskIndex*theMaskSize;
			theArray[0]=thePtr;
			theRep=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&theArray
			pixelsWide:pxmHeader.mBounds.right pixelsHigh:pxmHeader.mBounds.bottom bitsPerSample:1
				samplesPerPixel:1 hasAlpha:NO isPlanar:NO
				colorSpaceName:NSDeviceBlackColorSpace bytesPerRow:GetPixMapRowBytes(pxmHeader.mBounds,1)
				bitsPerPixel:1];
			break;
		case CSPXMComposite:
			thePtr+=theMaskSize*pxmHeader.mMaskCount;
                            
			thePtr+=index*theImageSize;
			newPtr=malloc(theImageSize);
			for(counter=0;counter<theImageSize;counter++)
				newPtr[counter]=thePtr[counter];
			theArray[0]=newPtr;
			theRep=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&theArray
				pixelsWide:pxmHeader.mBounds.right pixelsHigh:pxmHeader.mBounds.bottom bitsPerSample:8
				samplesPerPixel:4 hasAlpha:YES isPlanar:NO
				colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:GetPixMapRowBytes(pxmHeader.mBounds,32)
				bitsPerPixel:32];
			break;
		case CSPXMTransparent:
			thePtr+=theMaskSize*pxmHeader.mMaskCount;
                            
			thePtr+=index*theImageSize;
			newPtr=malloc(theImageSize/4);
			for(counter=0;counter<theImageSize/4;counter++)
				newPtr[counter]=thePtr[(counter*4)+3];
			theArray[0]=newPtr;
			theRep=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&theArray
				pixelsWide:pxmHeader.mBounds.right pixelsHigh:pxmHeader.mBounds.bottom bitsPerSample:8
				samplesPerPixel:1 hasAlpha:NO isPlanar:NO
				colorSpaceName:NSDeviceBlackColorSpace bytesPerRow:pxmHeader.mBounds.right
				bitsPerPixel:8];
			break;
		case CSPXMImage:
			thePtr+=theMaskSize*pxmHeader.mMaskCount;
                            
			thePtr+=index*theImageSize;
			newPtr=calloc(1, theImageSize*3/4);
			for(counter=0;counter<(theImageSize/4);counter++)
			{
				newPtr[counter*3]=thePtr[counter*4];
				newPtr[(counter*3)+1]=thePtr[(counter*4)+1];
				newPtr[(counter*3)+2]=thePtr[(counter*4)+2];
			}
			theArray[0]=newPtr;
	   
			theRep=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&theArray
				pixelsWide:pxmHeader.mBounds.right pixelsHigh:pxmHeader.mBounds.bottom bitsPerSample:8
				samplesPerPixel:3 hasAlpha:NO isPlanar:NO
				colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:pxmHeader.mBounds.right*3
				bitsPerPixel:24];
			break;
	}
	
	theImage=[[NSImage alloc] initWithSize:NSMakeSize(pxmHeader.mBounds.right,pxmHeader.mBounds.bottom)];
	[theImage addRepresentation:[theRep autorelease]];
	return [theImage autorelease];
}

@end
