//  Created by Allen Ingling on Wed Mar 31 2004.
//  Copyright (c) 2004 New York University. All rights reserved.


#include <stdio.h>						//for HIPS
#include <hipl_format.h>				//for HIPS
#include "ExternHIPS.h"					//for HIPS
#include <mach/mach_init.h>				//for time constraint priority
#include <mach/task_policy.h>			//for time constraint priority
#include <mach/thread_act.h>			//for time constraint priority
#include <mach/thread_policy.h>			//for time constraint priority
#include <mach/mach_time.h>				//for time constraint priority
#include <sys/types.h>
#include <sys/sysctl.h>




#include <HipsHelpers.h>


//helper functions

/*
	FindMovieLuminanceBounds()
	
	Accept an bank of floats where the hips movie was stored after it was read from standard input.
	Iternate through the pixel values and return the maximum and minimum values within
*/
void FindMovieLuminanceBounds(float *movieMemory, unsigned long movieSizeFloatPixels, float *maxValue, float *minValue)
{
	unsigned long i;
	
	*maxValue=*minValue=0;
	
	for(i=0;i<movieSizeFloatPixels;i++){
		if(movieMemory[i] > *maxValue)
			*maxValue=movieMemory[i];
		if(movieMemory[i] < *minValue)
			*minValue=movieMemory[i];
	}
}


void FindScalerAndOffsetFromPixelRangeAndScalingMode(float maxValue, float minValue, int ScaleMode, float *scaleL, float *offsetL)
{
	#define		SCREEN_LUMINANCE_MEAN_VALUE		(float)127.5
	#define		SCREEN_MAX_LUMINANCE_VALUE		(float)255
	#define		SCREEN_MIN_LUMINANCE_VALUE		(float)0
	float		originalFullRange, originalPositiveRange, originalNegativeRange, positiveScaler, negativeScaler;
	

	if(ScaleMode != khpsNoScale){
		originalFullRange=maxValue-minValue;
		originalPositiveRange= maxValue > 0 ? maxValue : 0;
		originalNegativeRange= minValue > 0 ? 0 : minValue;
	}
	
	if(ScaleMode == khpsClampToZero){  //clamp zero to zero screen luminance.
		//clamping to zero means that we cut off values below zero. 
		*scaleL= SCREEN_MAX_LUMINANCE_VALUE / originalPositiveRange;
		*offsetL=0;
	}else if (ScaleMode == khpsClampToMean){  //clamp zero to the median screen luminance
		//we should scale symetrically around the mean without cuttof.  
		positiveScaler = originalPositiveRange == 0 ? 0 : (SCREEN_MAX_LUMINANCE_VALUE - SCREEN_LUMINANCE_MEAN_VALUE) / originalPositiveRange;
		negativeScaler = originalNegativeRange == 0 ? 0 : (SCREEN_LUMINANCE_MEAN_VALUE - SCREEN_MIN_LUMINANCE_VALUE) / originalNegativeRange;
		if(positiveScaler == 0 || (positiveScaler > negativeScaler && negativeScaler != 0))
			*scaleL = abs(negativeScaler);
		else
			*scaleL = positiveScaler;
		*offsetL= SCREEN_LUMINANCE_MEAN_VALUE;
	}else if (ScaleMode == khpsFloat){
		*scaleL= (maxValue-minValue) / SCREEN_MAX_LUMINANCE_VALUE;
		*offsetL= 0-minValue;
	}else if(ScaleMode == khpsNoScale){
		*scaleL=1;
		*offsetL=0;
	}
}




void CenterCarbonRectInRect(Rect *insideRect, Rect *outsideRect, Rect *newRect)
{
	short   outerWidth, outerHeight, innerWidth, innerHeight, leftOffset, topOffset;
	
	outerWidth=outsideRect->right - outsideRect->left;
	outerHeight=outsideRect->bottom - outsideRect->top;
	innerWidth=insideRect->right - insideRect->left;
	innerHeight=insideRect->bottom - insideRect->top;
	leftOffset = (outerWidth - innerWidth) / 2;
	topOffset = (outerHeight - innerHeight) / 2;
	newRect->left = outsideRect->left + leftOffset;
	newRect->right= newRect->left + innerWidth;
	newRect->top = outsideRect->top + topOffset;
	newRect->bottom = newRect->top + innerHeight;
}

NSRect CenterNSRectInNSRect(NSRect innerRect, NSRect outerRect)
{
	NSRect  newRect;
	float   leftOffset, topOffset;

	
	leftOffset = floor((outerRect.size.width - innerRect.size.width) / 2);
	topOffset = floor((outerRect.size.height - innerRect.size.height) / 2);
	newRect.origin.x = outerRect.origin.x + leftOffset;
	newRect.origin.y = outerRect.origin.y + topOffset;
	newRect.size.width=innerRect.size.width;
	newRect.size.height=innerRect.size.height;
		
	return(newRect);
}	

/* 
	Converts a Cocoa NSRect into a Carbon rect
	
	The caller is responsible for allocating the Carbon rect.
*/
void ConvertNSRectToQDRect(NSRect nsRect, Rect *rect)
{
	rect->left=		(float)nsRect.origin.x;
	rect->top=		(float)nsRect.origin.y;
	rect->right=	(float)nsRect.origin.x + nsRect.size.width;
	rect->bottom=   (float)nsRect.origin.y + nsRect.size.height;
}

void ConvertNSRectToCGRect(NSRect nsRect, CGRect *cgRect)
{
	*cgRect= CGRectMake(nsRect.origin.x, nsRect.origin.y, nsRect.size.width, nsRect.size.height);
}



double GetBusTicksFrequency(void)
{
    size_t  tempIntSize; 	
    int		mib[2], tempInt;

    mib[0]=CTL_HW;
	mib[1]=HW_BUS_FREQ;
    tempIntSize=sizeof(tempInt);
    sysctl(mib, 2, &tempInt, &tempIntSize, NULL, 0);
	return((double)tempInt);
}

//level is an integer in the closed interval 0,9 specifying the fraction of a frame period for which this thread is guaranteed %100.00 of CPU time.
//The frame period is assumed to be 120Hz.  If the actual frame period is less, then the allocation of CPU time to the thread might be more fraqmented, 
//then with a more accurate frame rate guess, but that is of no consequence.
BOOL SetRealtimePriority(int level)
{
	mach_port_t								threadID;
    struct thread_time_constraint_policy	threadPolicy;
	int										kernError;
	double									busClockFrequency;
	BOOL									error;
	
	
	threadID=mach_thread_self();
	busClockFrequency=GetBusTicksFrequency();
	if(level==0){
		kernError=thread_policy_set(threadID, THREAD_STANDARD_POLICY, (int*)&threadPolicy, THREAD_STANDARD_POLICY_COUNT);
		error=kernError!=KERN_SUCCESS;
	} else if(level > 0 || level < 10){
		threadPolicy.period=(uint32_t) ((1/(double)120) * (double)busClockFrequency);
		threadPolicy.computation=(uint32_t)( (level/(double)10) * (double)threadPolicy.period);
		threadPolicy.constraint= threadPolicy.period;
		threadPolicy.preemptible=TRUE;
		kernError=thread_policy_set(threadID, THREAD_TIME_CONSTRAINT_POLICY, (int*)&threadPolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
		error=kernError!=KERN_SUCCESS;
	}
	return(error);

}



void PackFloatColorTripletIntoInt(float r, float g, float b, GLuint *packedInt, float scaleL, float offsetL)
{
	r= r * scaleL + offsetL;
	g= g * scaleL + offsetL;
	b= b * scaleL + offsetL;
	
	//GL_BGRA reversed is ARGB
	*packedInt = ((((((GLuint)255 << 8) | (GLuint)r) << 8 ) | (GLuint)g ) << 8) | (GLuint)b;
}

void PackFloatColorMonoIntoInt(float rgb, GLuint *packedInt, float scaleL, float offsetL)
{
	//rgb= rgb * scaleL + offsetL;
	
	//GL_BGRA reversed is ARGB
	*packedInt = ((((((GLuint)255 << 8) | (GLuint)rgb) << 8 ) | (GLuint)rgb ) << 8) | (GLuint)rgb;
}


void PackFloatColorMonoIntoByte(float rgb, GLubyte *packedByte, float scaleL, float offsetL)
{
	//GLubyte		scaledByte;
	
	//scaledByte= (GLubyte)(rgb * scaleL + offsetL);
	*packedByte=(GLubyte)rgb;
}


/*
	ConvertNSRectToGLVertices2f()
	
	The caller allocates storage for the vertices. 
*/
void ConvertNSRectToGLVertices(NSRect nsRect, float *v1, float *v2, float *v3, float *v4)
{
	float rightBorder, topBorder;
	
	rightBorder=nsRect.origin.x + nsRect.size.width;
	topBorder=nsRect.origin.y + nsRect.size.height;
	//lower left
	v1[0]= nsRect.origin.x; 
	v1[1]= nsRect.origin.y;
	//upper left
	v2[0]= nsRect.origin.x;
	v2[1]= topBorder;
	//uppper right
	v3[0]= rightBorder;
	v3[1]= topBorder;
	//lower right
	v4[0]= rightBorder;
	v4[1]= nsRect.origin.y;
} 



