#import "AUSonogramView.h"


#define NSRectToCGRect(r) CGRectMake(r.origin.x, r.origin.y, r.size.width, r.size.height)

typedef struct{
	float red;
	float green;
	float blue;
} ColorTriplet;

@implementation AUSonogramView

#pragma mark ___Coloing___
- (void) createColormap: (UInt32) mapsize
{
	colormapSize = mapsize;
	if (colormap) free(colormap);
	colormap = (Float32*)calloc(3*mapsize, sizeof(Float32));	
	
	Float32 bRed = [backgroundColor redComponent];
	Float32 bGreen = [backgroundColor greenComponent];
	Float32 bBlue = [backgroundColor blueComponent];

	Float32 fRed = [lineColor redComponent];
	Float32 fGreen = [lineColor greenComponent];
	Float32 fBlue = [lineColor blueComponent];
		
	Float32 incrRed = (fRed - bRed)/mapsize;
	Float32 incrGreen = (fGreen - bGreen)/mapsize;
	Float32 incrBlue = (fBlue - bBlue)/mapsize;
		
	UInt32 index;
	for (UInt32 i = 0; i < mapsize; i++) {
		index = i*3;
		colormap[index] = bRed;
		colormap[index+1] = bGreen;
		colormap[index+2] = bBlue;
	  
		bRed += incrRed;
		bGreen += incrGreen;
		bBlue += incrBlue;		
			  	  
	 // printf("colormap[%d]: %lf %lf %lf\n", i,  colormap[index], colormap[index+1], colormap[index+2] );
    }	
}

- (UInt32) skewColor: (Float32) x
{
	UInt32 pos = (UInt32)(x * colormapSize);
	//printf("pos: %ld\n", pos);
	
	if (pos > colormapSize) 
		return (colormapSize - 1);

	if (pos > 0)
		return (pos - 1);

	return(0);
}

- (UInt32)	getIndex: (Float32) value 
			withMin: (Float32) minD 
			andMax: (Float32) maxD 
			andScale: (Float32) scale
{	
	if (maxD < .001) return 0;
	
	value /= scale;
	
	//printf("value: %lf\n", value);
	Float32 maxDB = 20.0 * log10(1.0 + maxD); 	
	Float32 minDB = 20.0 * log10(1.0 + minD); 	
	Float32 inDB = 20.0 * log10(1.0 + value);
	//printf("inDB: %lf\n", inDB);
	
	Float32 db = (value <= minDB) ? minDB : inDB;
	//printf("db: %lf\n", db);
	Float32 ratio = (db/maxDB);	
	UInt32 j = [self skewColor: ratio];
	
	return j;
}

- (ColorTriplet) getColorWithIndex: (UInt32) j 
{	
	ColorTriplet c;
	UInt32 index = j*3;
	c.red =  colormap[index];
	c.green = colormap[index+1];
	c.blue = colormap[index+2];
	return c;
}



#pragma mark ___Initialize___

- (void) InitializeBuffers
{
	colormap = NULL;
	[self createColormap: mNumBins];	
														
	UInt32 numBytesPerFrame = mNumBins*4*sizeof(unsigned char);
	mCapacitySlices = (UInt32) ([self frame].size.width);
	
	if (mRingBuffer) delete(mRingBuffer);
	mRingBuffer = new SonogramRingBuffer();
	mRingBuffer->Allocate(1, numBytesPerFrame, mCapacitySlices);
			
	mRingBufferCounter = 0;	
	mRenderCounter = 0;
	
	UInt32 numTotalBytes = numBytesPerFrame*mCapacitySlices;
	
	CAStreamBasicDescription	bufClientDesc;		
	bufClientDesc.SetCanonical(1, false);
	if(mFetchingBufferList) delete(mFetchingBufferList);
	mFetchingBufferList = CABufferList::New("fetch buffer", bufClientDesc );

	if(mStoringBufferList) delete(mStoringBufferList);
	mStoringBufferList = CABufferList::New("store buffer", bufClientDesc );

	AudioBufferList* fetchBufferList = &mFetchingBufferList->GetModifiableBufferList(); 
	AudioBufferList* storingBufferList = &mStoringBufferList->GetModifiableBufferList(); 
	
	for (UInt32 i=0; i<fetchBufferList->mNumberBuffers; i++){
		fetchBufferList->mBuffers[i].mDataByteSize = numTotalBytes;
		fetchBufferList->mBuffers[i].mData = (unsigned char*) malloc (numTotalBytes);

		storingBufferList->mBuffers[i].mDataByteSize = numTotalBytes;
		storingBufferList->mBuffers[i].mData = (unsigned char*) malloc (numTotalBytes);
	}	
	
	storing = false;
	
}

- (void) Initialize
{
	backgroundColor = [NSColor colorWithCalibratedRed: 0 green: 0 blue: 0 alpha:1];
	lineColor = [NSColor colorWithCalibratedRed: 1 green: 1 blue: 1 alpha:1];
	
	[myBackgroundWell setColor:backgroundColor];		
	[myLineWell setColor: lineColor];
	
	[self InitializeBuffers];
		
	mNumBins = 0;
}

- (id)initWithFrame:(NSRect)frame 
{
    self = [super initWithFrame:frame];
    if (self) {
		
   }
    return self;
}

- (void)awakeFromNib 
{	
	[self Initialize];
}

- (void) dealloc
{
	[backgroundColor release];
	[lineColor release];	
	
	if (colormap) free(colormap);;
	
	if (mRingBuffer) delete(mRingBuffer);
	if(mFetchingBufferList) delete(mFetchingBufferList);
	if(mStoringBufferList) delete(mStoringBufferList);

	[super dealloc];
}

#pragma mark ___IBActions___

- (IBAction) changeBackgroundColor: (id) sender
{
	backgroundColor = [sender color];
	[self createColormap: mNumBins];	

	[self setNeedsDisplay: YES];
}

- (IBAction) changeLineColor: (id) sender
{
	lineColor = [sender color];
	[self createColormap: mNumBins];	

	[self setNeedsDisplay: YES];
}

- (IBAction) changeDistortion: (id) sender
{
	[self setNeedsDisplay: YES];
}

#pragma mark ___Reading___

- (bool) storing
{ 
	//printf("ask am i storing: %d\n", storing);	
	return storing;
}


#pragma mark ___Drawing___
- (void) storeSlices: (SonogramOverview) data
{	
	if([myHoldButton state] == NSOnState) return;
	
	storing = true;
	//printf("store am i storing: %d\n", storing);	
	
	if (mNumBins != data.mNumBins){
		mNumBins =  data.mNumBins;
		[self InitializeBuffers];
	}
	
	mNumSlices = data.mNumSlices;
	
	Float32 linmin = data.mMinAmp;
	Float32 linmax = data.mMaxAmp;
													
	UInt32 numBytesPerRow = mNumBins*4*sizeof(unsigned char);
									
//	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	AudioBufferList* mStoringList = &mStoringBufferList->GetModifiableBufferList(); 
	unsigned char* bitmapImageSliceBits = (unsigned char*) mStoringList->mBuffers[0].mData;
	
	
	Float32 linmag;	
	UInt32  index, index1, index2;
	ColorTriplet c;
	//printf("\n\n\nstoring: %ld\n", mNumSlices);

	for (UInt32 j = 0; j < mNumSlices; j++) {	// for each frame	
		for (UInt32 i = 0; i < mNumBins; i++) {		// for each frequency
		
			index1 = i + j*mNumBins;
			linmag	=  data.mOverview[index1];			
			
			//printf("i: %ld j: %ld recovering overview bit from %ld which is: %lf\n",i,j, index1, linmag);
			index = [self getIndex: linmag withMin: linmin andMax: linmax andScale: 1.0];
			c = [self getColorWithIndex: index];
			
			//printf("red: %lf green: %lf blue: %lf\n", c.red, c.green, c.blue);
				
			index2 = i*4 + j*numBytesPerRow;
			//printf("i: %ld j: %ld  writing overview bit to %ld\n", i,j,index2);		
			//printf("pixel[%lu][%lu]- linmag: %lf ratio: %u \n", i,j, linmag, ratio);
			
			bitmapImageSliceBits[index2  ] = 255;
			bitmapImageSliceBits[index2+1] = 255*c.red;
			bitmapImageSliceBits[index2+2] = 255*c.green;
			bitmapImageSliceBits[index2+3] = 255*c.blue; 		
			
			//printf("bitmap store[%ld][%ld]: %u %u %u %u\n", i,j, 
			//bitmapImageSliceBits[index2], bitmapImageSliceBits[index2+1], 
			//bitmapImageSliceBits[index2+2], bitmapImageSliceBits[index2+3]);
		}
	}	 
	
	//store
	mRingBuffer->Store(mStoringList, mNumSlices, mRingBufferCounter);
	mRingBufferCounter += mNumSlices;
	//printf("adding %lld to make start %lld\n", mNumSlices, mRingBufferCounter);
	
	[self setNeedsDisplay: YES];
//	[pool release];	
	
	storing = false;
	
}


- (void)drawRect:(NSRect)rect
{	
	if (!mRingBuffer) return;
	AudioBufferList* fetchBufferList = &mFetchingBufferList->GetModifiableBufferList(); 
	
	// fetch	
	SInt64 newNum = mRingBufferCounter-mRenderCounter;
	//printf("fetching from %lld to get %ld\n", mRenderCounter, mCapacitySlices);
	SInt64 num = mRingBuffer->Fetch(fetchBufferList, mCapacitySlices, mRenderCounter);
	mRenderCounter += newNum;
	
	unsigned char* bitmapImageBits = (unsigned char*) fetchBufferList->mBuffers[0].mData;
	
	UInt32 numBytesPerRow = mNumBins*4; 
	UInt32 numTotalBytes = mCapacitySlices*numBytesPerRow;

	NSData* bitmapData = [[[NSData alloc] initWithBytesNoCopy:bitmapImageBits length:numTotalBytes freeWhenDone:NO] autorelease];															
	CIImage* mSpectrumImage = [[CIImage alloc] initWithBitmapData : bitmapData
													bytesPerRow : numBytesPerRow 
														size :  CGSizeMake((Float32) mNumBins, (Float32) mCapacitySlices)
														format : kCIFormatARGB8
													colorSpace : CGColorSpaceCreateDeviceRGB()];

	// affine transform
	NSPoint origin = rect.origin;
	NSRect sonogramImageRect = NSMakeRect(origin.x, origin.y, mNumBins, mCapacitySlices);	

	NSAffineTransform* yFlip = [NSAffineTransform transform];
	[yFlip rotateByDegrees: 90];
	[yFlip translateXBy: 0.0 yBy: -sonogramImageRect.size.height];

	CIFilter* flipYTransform = [CIFilter filterWithName: @"CIAffineTransform"];	
    [flipYTransform setValue: yFlip forKey: @"inputTransform"];	
    [flipYTransform setValue: mSpectrumImage forKey: @"inputImage"];	
	CIImage* affineOutputImage = [flipYTransform valueForKey: @"outputImage"];
	
	if ([myDistortionButton selectedRow] == 1){		
		CIFilter* bumpDistortion = [CIFilter filterWithName: @"CIBumpDistortion"];
		[bumpDistortion setValue: affineOutputImage forKey: @"inputImage"];
		[bumpDistortion setValue: [CIVector vectorWithX:512 Y:0] forKey: @"inputCenter"];
		[bumpDistortion setValue: [NSNumber numberWithFloat: 512] forKey: @"inputRadius"];
		[bumpDistortion setValue: [NSNumber numberWithFloat: 0.60] forKey: @"inputScale"];
		CIImage* bumpOutputImage = [bumpDistortion valueForKey: @"outputImage"];
	 
		[bumpOutputImage drawInRect: rect 
							fromRect:  sonogramImageRect
							operation: NSCompositeCopy
							fraction: 1.0];
	}
	else if ([myDistortionButton selectedRow] == 2){
		CIFilter* bumpDistortion = [CIFilter filterWithName: @"LinearBumpDistortion"];
		[bumpDistortion setValue: affineOutputImage forKey: @"inputImage"];
		[bumpDistortion setValue: [CIVector vectorWithX:512 Y:0] forKey: @"inputCenter"];
		[bumpDistortion setValue: [NSNumber numberWithFloat: 1024] forKey: @"inputRadius"];
		[bumpDistortion setValue: [NSNumber numberWithFloat: 0.30] forKey: @"inputScale"];
		[bumpDistortion setValue: [NSNumber numberWithFloat: M_PI * 0.5] forKey: @"inputAngle"];
		CIImage* bumpOutputImage = [bumpDistortion valueForKey: @"outputImage"];
		
		[bumpOutputImage drawInRect: rect 
							fromRect:  sonogramImageRect
							operation: NSCompositeCopy
							fraction: 1.0];	
	}
	else {
		[affineOutputImage drawInRect: rect 
							fromRect:  sonogramImageRect
							operation: NSCompositeCopy
							fraction: 1.0];
	}
}



@end
