#import <Foundation/Foundation.h>
#import <mach/mach_init.h>

#import "Thumber.h"

#define BIT(x) (((unsigned char)1)<<(x))
#define BITS(from, to) ((((BIT((to)) - (unsigned char)1)<<1) + (unsigned char)1) & ~(BIT((from)) - (unsigned char)1))
#define FBITS(from, to) (BITS(7 - (to), 7 - (from)))

extern NSConditionLock *globalLock;
extern TransferData global;

@implementation Thumber

static int filterCoef[3] = {4,3,2};

static unsigned char _lkt1bit[2]  = {0x0,0xff};
static unsigned char _lkt2bit[4]  = {0x0,0x55,0xaa,0xff};
static unsigned char _lkt4bit[16] = {0x0,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff};

static unsigned char *lookupTable[7] = {
    	_lkt1bit,
        _lkt2bit,
        NULL,
        _lkt4bit,
        NULL,
        NULL,
        NULL
};

+ (void)connectWithPorts:(NSArray *)portArray
{
        NSAutoreleasePool *pool;
        NSConnection *clientConnection;
        Thumber *thumber;

        pool = [[NSAutoreleasePool alloc] init];

        clientConnection = [NSConnection
            connectionWithReceivePort:[portArray objectAtIndex:0]
            sendPort:[portArray objectAtIndex:1]];

        thumber = [[self alloc] initWithClient:(id)[clientConnection rootProxy]];
        [(id)[clientConnection rootProxy] setThumber:thumber];
        [thumber release];

        [[NSRunLoop currentRunLoop] run];
        [pool release];
        [NSThread exit];

        return;
}

- initWithClient:(id)aClient
{
        client = [aClient retain];
        [client setProtocolForProxy:@protocol(DisplayMethods)];
        return self;
}
           
- (oneway void)thumbWithSize:(float)thumbSize
{
	unsigned char **planes, **thumbplanes, **thumbSharpenedPlanes, **originalPlanes;
        NSZone *zone;
        int bitsPerPixel, samplesPerPixel, thumbSamplesPerPixel, numberOfPlanes, bytesPerPlane, bytesPerRow, bitsPerPixelAndSample;
        int b,i, j, k,l,d,h,pixelsHigh, pixelsWide, bitCount, byteCount, n, m, z, bitsPerRow, bitsRestPerRow, bitRowCount;
	int thumbHigh, thumbWide;
        BOOL isPlanar;
        unsigned char *data, *p, *q, *qs;
	unsigned char c,c1,c2;

        [client provideGlobalData];
        
	[globalLock lockWhenCondition:HAS_DATA];

        if(!global.full || (global.imageInfo.pixelsHigh < thumbSize && global.imageInfo.pixelsWide < thumbSize)
           || !global.imageData || !global.imagePlanes[0]){
            	global.thumbnailPlanes = NULL;
            	[globalLock unlock];
     		[client displayGlobalData];
		return;
        }
        
        zone = NSCreateZone(vm_page_size * 2, vm_page_size, YES);
        NSSetZoneName(zone, @"Scrapper");
        
        bitsPerPixel = global.imageInfo.bitsPerPixel;
        samplesPerPixel = global.imageInfo.samplesPerPixel;	
        bitsPerPixelAndSample = global.imageInfo.bitsPerSample;        
        isPlanar = global.imageInfo.isPlanar;
        numberOfPlanes = global.imageInfo.numberOfPlanes;
        bytesPerPlane = global.imageInfo.bytesPerPlane;
        bytesPerRow = global.imageInfo.bytesPerRow;
        pixelsHigh = global.imageInfo.pixelsHigh;
        pixelsWide = global.imageInfo.pixelsWide;


        data = global.imageData;        
        originalPlanes = global.imagePlanes;

        planes = NSZoneMalloc(zone, samplesPerPixel * sizeof(unsigned char *));

        for(i = 0;i < samplesPerPixel; i++)
            	planes[i] = NSZoneMalloc(zone, pixelsHigh * pixelsWide * sizeof(unsigned char));
	
        if(bitsPerPixelAndSample == 8){
            if(isPlanar){
                for(i = 0;i < samplesPerPixel; i++){
                    p = originalPlanes[i];
                    for(j = 0; j < pixelsHigh * pixelsWide; j++)
                        planes[i][j] = p[j];
                }
            } else {
                for(j = 0, p = data; j < pixelsHigh * pixelsWide; j++, p += samplesPerPixel)
                        for(i = 0;i < samplesPerPixel; i++)
                          	planes[i][j] = p[i];
            }    	
        } else {
            if(isPlanar){
		    bitsPerRow = pixelsWide * bitsPerPixelAndSample;
		    bitsRestPerRow = (bytesPerRow << 3) - bitsPerRow;
                    for(i = 0;i < samplesPerPixel; i++){
                            p = originalPlanes[i];	
                            bitCount = 0;
		            bitRowCount = 0;
                            for(j = 0; j < pixelsHigh * pixelsWide; j++){

                                    byteCount = bitCount >> 3;

                                    // planes[i][j] setzt sich zusammen aus
                                    // [bitCount, bitCount + bitsPerPixelAndSample) bits aus dem bitstrom

                                    if((bitCount + bitsPerPixelAndSample) <= ((byteCount + 1) << 3)){
                                            n = bitCount - (byteCount << 3);

                                            // planes[i][j] besteht aus [n,n + bitsPerPixelAndSample) von p[byteCount]
					
                                            c1 = FBITS(n, n + bitsPerPixelAndSample - 1);
                                            c = (p[byteCount] & c1) >> (8 - n - bitsPerPixelAndSample);

                                    } else {
                                            n =  bitCount + bitsPerPixelAndSample - ((byteCount + 1) << 3);

                                            // planes[i][j] besteht aus
                                            // MostSignificantPart: [0,n) bits von p[byteCount + 1]
                                            // LeastSignificantPart: [8 - (bitsPerPixelAndSample - n), 8) bits von p[byteCount]

                                            c1 = FBITS(0, n - 1);
                                            c1 = (p[byteCount + 1] & c1) >> (8 - n);
                                            c1 = c1 << (bitsPerPixelAndSample - n + 1);
                                            c2 = FBITS(8 - (bitsPerPixelAndSample - n), 7);
                                            c2 = (p[byteCount] & c2);

                                            c = c1 | c2;

                                    }	

                                    planes[i][j] = lookupTable[bitsPerPixelAndSample - 1][c];

                                    bitCount += bitsPerPixelAndSample;
				    bitRowCount += bitsPerPixelAndSample;
				    if(bitRowCount == bitsPerRow){
						bitCount += bitsRestPerRow;
						bitRowCount = 0;
				    }
                            }
                    }
            } else {
                    p = data;
                    bitCount = 0;
		    bitRowCount = 0;
		    bitsPerRow = pixelsWide * bitsPerPixel;
		    bitsRestPerRow = (bytesPerRow << 3) - bitsPerRow;
                    for(j = 0; j < pixelsHigh * pixelsWide; j++){
                            for(i = 0;i < samplesPerPixel; i++){
                                    byteCount = bitCount >> 3;
				    
                                    if((bitCount + bitsPerPixelAndSample) <= ((byteCount + 1) << 3)){
                                        n = bitCount - (byteCount << 3);

                                        // planes[i][j] besteht aus [n,n + bitsPerPixelAndSample) von p[byteCount]

                                        c1 = FBITS(n, n + bitsPerPixelAndSample - 1);
                                        c = (p[byteCount] & c1) >> (8 - n - bitsPerPixelAndSample);

                                    } else {
                                        n =  bitCount + bitsPerPixelAndSample - ((byteCount + 1) << 3);

                                        // planes[i][j] besteht aus
                                        // MostSignificantPart: [0,n) bits von p[byteCount + 1]
                                        // LeastSignificantPart: [8 - (bitsPerPixelAndSample - n), 8) bits von p[byteCount]

                                        c1 = FBITS(0, n - 1);
                                        c1 = (p[byteCount + 1] & c1) >> (8 - n);
                                        c1 = c1 << (bitsPerPixelAndSample - n + 1);
                                        c2 = FBITS(8 - (bitsPerPixelAndSample - n), 7);
                                        c2 = (p[byteCount] & c2);

                                        c = c1 | c2;

                                    }	

                                    planes[i][j] = lookupTable[bitsPerPixelAndSample - 1][c];
                                    bitCount += bitsPerPixelAndSample;
				    bitRowCount += bitsPerPixelAndSample;
				    if(bitRowCount == bitsPerRow){
						bitCount += bitsRestPerRow;
						bitRowCount = 0;
				    }
                            }
                    }
            }
        }
        
	if(global.imageInfo.hasAlpha)
		thumbSamplesPerPixel = samplesPerPixel;	
	else 
		thumbSamplesPerPixel = samplesPerPixel + 1;

	if(pixelsHigh > pixelsWide){
		thumbHigh = thumbSize;
		thumbWide = pixelsWide * thumbSize / pixelsHigh;
	} else {
		thumbWide = thumbSize;
		thumbHigh = pixelsHigh * thumbSize / pixelsWide;
	}
	
        m = (pixelsWide << 6) / thumbWide;

	thumbplanes = NSZoneMalloc(NSDefaultMallocZone(), thumbSamplesPerPixel * sizeof(unsigned char *));
	thumbSharpenedPlanes = NSZoneMalloc(NSDefaultMallocZone(), thumbSamplesPerPixel * sizeof(unsigned char *));

	for(i = 0;i < thumbSamplesPerPixel; i++){
 		thumbplanes[i] = NSZoneMalloc(NSDefaultMallocZone(), thumbHigh * thumbWide * sizeof(unsigned char));
		thumbSharpenedPlanes[i] = NSZoneMalloc(NSDefaultMallocZone(), thumbHigh * thumbWide * sizeof(unsigned char));
	}

	for(i = 0 ;i < samplesPerPixel; i++)
            for(j = 0, q = thumbplanes[i], qs = thumbSharpenedPlanes[i]; j < thumbHigh;j++, q += thumbWide, qs += thumbWide){
                	z = (j * m) >> 6;
                	p = planes[i] + z * pixelsWide;
                        for(k = 0; k < thumbWide;k++){
                            	h = (k * m) >> 6;
                                d = (int)p[h];
                                b = d << filterCoef[0];
                                for(l = 1;l < 3 && h - l >= 0;l++){
                                    d = (int)p[h - l];
                                    b += d << filterCoef[l];
                                }
                                for(l = 1;l < 3 && h + l < pixelsWide;l++){
                                    d = (int)p[h + l];
                                    b += d << filterCoef[l];
                                }
                                for(l = 1;l < 3 && z - l >= 0;l++){
                                    d = (int)(p - l * pixelsWide)[h];
                                    b += d << filterCoef[l];
                                }
                                for(l = 1;l < 3 && z + l < pixelsHigh;l++){
                                    d = (int)(p + l * pixelsWide)[h];
                                    b += d << filterCoef[l];
                                }                                
                    		q[k] = qs[k] = (unsigned char)(b >> 6);
                        }        
            }            

	for(i = 0 ;i < thumbSamplesPerPixel - 1; i++)
            for(j = 1, q = thumbplanes[i], qs = thumbSharpenedPlanes[i]; j < thumbHigh - 1;j++, q += thumbWide, qs += thumbWide){
                        for(k = 1; k < thumbWide - 1;k++){
                            	b = (int)q[k];
				b = (b << 2) + b;
                                b -= (int)q[k - 1];
				b -= (int)q[k + 1];
                                b -= (int)(q - thumbWide)[k];
                                b -= (int)(q + thumbWide)[k];
                                if(b >= 0 && b < 256)
                    			qs[k] = (unsigned char)(b);
                        }        
            }          

        for(i = 0 ;i < thumbSamplesPerPixel - 1; i++)
            for(j = 0, qs = thumbSharpenedPlanes[i]; j < thumbHigh;j++, qs += thumbWide){
                        for(k = 0; k < thumbWide;k++)
                                qs[k] >>= 1;
            }          

      	for(j = 0, qs = thumbSharpenedPlanes[thumbSamplesPerPixel - 1]; j < thumbHigh;j++, qs += thumbWide)
               for(k = 0; k < thumbWide;k++)
                    	qs[k] = 127;

	if(thumbSamplesPerPixel > samplesPerPixel)
		      	for(j = 0, q = thumbplanes[thumbSamplesPerPixel - 1]; j < thumbHigh;j++, q += thumbWide)
               			for(k = 0; k < thumbWide;k++)
                    			q[k] = 255;

	global.thumbnailPlanes = thumbplanes;
	global.thumbnailSharpenedPlanes = thumbSharpenedPlanes;
	global.thumbnailInfo.pixelsWide = thumbWide;
	global.thumbnailInfo.pixelsHigh = thumbHigh;
	global.thumbnailInfo.bitsPerSample = 8;
	global.thumbnailInfo.samplesPerPixel = thumbSamplesPerPixel;
	global.thumbnailInfo.isPlanar = YES;
	global.thumbnailInfo.hasAlpha = YES;
	global.thumbnailInfo.bytesPerRow = thumbWide;
	global.thumbnailInfo.bitsPerPixel = 8;
        global.thumbnailInfo.numberOfPlanes = thumbSamplesPerPixel;
        global.thumbnailInfo.bytesPerPlane = thumbWide * thumbHigh;

        for(i = 0;i < samplesPerPixel; i++)
                        NSZoneFree(zone,planes[i]);
         
        NSZoneFree(zone,planes);
        
        NSRecycleZone(zone);
	
	[globalLock unlock];

	[client displayGlobalData];
}
 
@end
