#import "Controller.h"
#import "OBJModel.h"
#import "TaskWrapper.h"
#import "DryRollingFriction.h"

#define _NEWTON_USE_LIB 1
#import "newton.h"

OBJModel *model = 0;

//physics

NewtonWorld* nWorld;
NewtonBody* ballBody;

static void GLCheckError(void)
{
    GLenum glErr;
    glErr = glGetError();
    if (glErr != GL_NO_ERROR) {
        fprintf(stderr, "OpenGL Error: 0x%04x -- %s\n", (int)glErr,  gluErrorString(glErr));
    }
}

static void _LogContextInfo(const char *name)
{
#if 0
    fprintf(stderr, "%s:\n", name);
    fprintf(stderr, "Vendor:     %s\n", glGetString(GL_VENDOR));
    fprintf(stderr, "Renderer:   %s\n", glGetString(GL_RENDERER));
    fprintf(stderr, "Version:    %s\n", glGetString(GL_VERSION));
    fprintf(stderr, "Extensions: %s\n", glGetString(GL_EXTENSIONS));
#endif
}

static void _LogPixelFormat(NSOpenGLPixelFormat *pf)
{
    long colorBits, alphaBits, depthBits, stencilBits, accelerated;

    [pf getValues: &colorBits forAttribute: NSOpenGLPFAColorSize forVirtualScreen: 0];
    [pf getValues: &alphaBits forAttribute: NSOpenGLPFAAlphaSize forVirtualScreen: 0];
    [pf getValues: &depthBits forAttribute: NSOpenGLPFADepthSize forVirtualScreen: 0];
    [pf getValues: &stencilBits forAttribute: NSOpenGLPFAStencilSize forVirtualScreen: 0];
    [pf getValues: &accelerated forAttribute: NSOpenGLPFAAccelerated forVirtualScreen: 0];

#if 1
    fprintf(stderr, "Pixel format:\n");
    fprintf(stderr, "    Color bits: %ld\n", colorBits);
    fprintf(stderr, "    Alpha bits: %ld\n", alphaBits);
    fprintf(stderr, "    Depth bits: %ld\n", depthBits);
    fprintf(stderr, "  Stencil bits: %ld\n", stencilBits);
    fprintf(stderr, "   Accelerated: %ld\n", accelerated);
#endif
}


typedef struct
{
	float x,y,z;
}XYZ;

NSPoint center = {-1,-1};

OBJModel *sphereModel;

void cross(float*a, float*b, float*c)
{
	c[0] = a[1]*b[2] - a[2]*b[1];
	c[1] = a[2]*b[0] - a[0]*b[2];
	c[2] = a[0]*b[1] - a[1]*b[0];
}
void unit(float*a)
{
	float len = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
	a[0] /= len;
	a[1] /= len;
	a[2] /= len;
}
void scale(float*a, float s)
{
	a[0] *= s;
	a[1] *= s;
	a[2] *= s;
}

@implementation Controller

- (void) applicationDidFinishLaunching: (NSNotification *) note;
{
    [self setupGL];
	[self initScene];
}

- (void) setupGL
{
    /*
     Clean up any previous setup
     */
    if ([NSOpenGLContext currentContext])
        [NSOpenGLContext clearCurrentContext];

    [primaryContext clearDrawable];
    [primaryContext release];
    primaryContext = nil;
    
    [surfaceContext clearDrawable];
    [surfaceContext release];
    surfaceContext = nil;

    surfaceTextureTarget = GL_NONE;
    surfaceTexture       = 0;
    
        
    /*
     Create the primary GL context.  This is the one that would normally be seen.
     */
    {
        NSOpenGLPixelFormatAttribute attributes[] = {
            NSOpenGLPFAAccelerated,
            NSOpenGLPFADoubleBuffer,
            NSOpenGLPFAColorSize, 24,
            NSOpenGLPFADepthSize, 16,
            NSOpenGLPFAStencilSize, 1,
            0
        };
        NSOpenGLPixelFormat *pixelFormat;
        pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: attributes];
        _LogPixelFormat(pixelFormat);
        primaryContext = [[NSOpenGLContext alloc] initWithFormat: pixelFormat shareContext: shareContext];
        [pixelFormat release];
        [primaryContext setView: [primaryWindow contentView]];
        [primaryContext makeCurrentContext];
        primaryContextSize = [[primaryWindow contentView] frame].size;
        _LogContextInfo("Primary");
    }
    
    /*
     Create a the surface context.  If there is an independent share context, use that, otherwise use the main context.
     */
    {
        NSOpenGLPixelFormatAttribute attributes[] = {
            NSOpenGLPFAAccelerated,
            NSOpenGLPFAColorSize, 24,
            NSOpenGLPFADepthSize, 24,
            0
        };
        NSOpenGLPixelFormat *pixelFormat;
        pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: attributes];
        _LogPixelFormat(pixelFormat);
        surfaceContext = [[NSOpenGLContext alloc] initWithFormat: pixelFormat
                                                    shareContext: primaryContext];
        [pixelFormat release];
        [surfaceContext setView: [surfaceWindow contentView]];
        [surfaceContext makeCurrentContext];
        surfaceContextSize = [[surfaceWindow contentView] frame].size;
		NSLog(@"surface window size = %@", NSStringFromSize(surfaceContextSize));
        _LogContextInfo("Surface");
    }

    /*
     Now, create a texture in the primary context that references the surface context!
     Unclear what, if any, requirement there is on the current context at this point.
     */
    [primaryContext makeCurrentContext];
    glGenTextures(1, &surfaceTexture);
    surfaceTextureTarget = GL_TEXTURE_2D;//*/GL_TEXTURE_RECTANGLE_EXT;
    glBindTexture(surfaceTextureTarget, surfaceTexture);
    [primaryContext createTexture: surfaceTextureTarget fromView: [surfaceWindow contentView] internalFormat: GL_RGBA];
    glTexParameteri(surfaceTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(surfaceTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	//clamp so that the edges of the ball which tend to infinite points on the texture map look ok
	glTexParameteri(surfaceTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(surfaceTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP);
	
    {
        // Make sure we got what we expect

        GLuint boundTexture = 0;
        glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
        fprintf(stderr, "surfaceTexture = %ld\n", surfaceTexture);
        fprintf(stderr, "boundTexture = %ld\n", boundTexture);

        //glBindTexture(GL_TEXTURE_2D, surfaceTexture);
        GLint width = -1;
        glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
        fprintf(stderr, "boundTexture.width = %ld\n", width);
    }
	
	//clear reflect
	[surfaceContext makeCurrentContext];
    {
		glClearColor (0.0, 0.0, 0.0, 0.0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
		glFlush();
    }
    GLCheckError();
    glFinish();
    glBindTexture(GL_TEXTURE_2D, 0);
    [surfaceContext flushBuffer];
    GLCheckError();


    /*
     Finally, set up an animation timer
     */
    startTime = [NSDate timeIntervalSinceReferenceDate];
    timer = [[NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES] retain];
}

- (void) timerFired: (NSTimer *) timer;
{
    NSTimeInterval currentTime, deltaTime;
    float rotationDegrees;

    currentTime = [NSDate timeIntervalSinceReferenceDate];
    deltaTime = startTime - currentTime;
    rotationDegrees = fmod(deltaTime, 1.0) * 360;

    /*
     Switch to the surface context and render a rotated square covered with the checker texture modulated by the diffuse color.
     */
	if([reflectCheck state] == NSOnState)
	{
		[surfaceContext makeCurrentContext];
		{
			[self renderReflection];
		}
		GLCheckError();
		glFinish();
		glBindTexture(GL_TEXTURE_2D, 0);
		[surfaceContext flushBuffer];
		GLCheckError();
	}


    /*
     Draw something in the primary context using the surface texture
     */
    [primaryContext makeCurrentContext];
    {
		[self render];
	}
    GLCheckError();
    [primaryContext flushBuffer];
    GLCheckError();
	
	//physics
	[self doPhysics];
}

float radius = .4;
float camera[3]={0,0,-10};
float ball[3]={0,0,0.3};
float gravity[3];
float reflectionAngle = 160;

- (void)renderReflection
{
	if(!model)
		model = [[OBJModel alloc] initWithFile:[[NSBundle mainBundle] pathForResource:@"puzzle2" ofType:@"obj"]];
	
	glClearColor (0.0, 0.0, 0.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
	
	glEnable(GL_TEXTURE_2D);
	
	glViewport(0, 0, surfaceContextSize.width, surfaceContextSize.height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(reflectionAngle, (GLfloat) surfaceContextSize.width/(GLfloat) surfaceContextSize.height, 0.001, 10.0);
	//glTranslatef(ball[0],ball[1],ball[2]);
	gluLookAt(ball[0],ball[1],ball[2],  camera[0], camera[1], camera[2],  0,0,1);
	//glTranslatef(0.0, -5.0, 0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	//enable lighting
	{
		static GLfloat light_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
		static GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
		static GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
		static GLfloat light_position[] = { -.5, 1.0, 2.0, 0.0 }; 
		glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient);
		glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
		glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular);
		glLightfv(GL_LIGHT0, GL_POSITION, light_position); 
		glColor3f (1.0, 1.0, 1.0);
		
		glEnable (GL_LIGHTING);
		glEnable (GL_LIGHT0);
	}
	glEnable( GL_CULL_FACE );

	[model render];
}

- (void)render
{
	static float	time=0;

	//time+=0.2;
	
	//ball[0] += (mouse.x-center.x)/100;
	//ball[1] += (mouse.y-center.y)/100;

	if(!model)
		model = [[OBJModel alloc] initWithFile:[[NSBundle mainBundle] pathForResource:@"puzzle2" ofType:@"obj"]];
	if(!sphereModel)
		sphereModel = [[OBJModel alloc] initWithFile:[[NSBundle mainBundle] pathForResource:@"sphere" ofType:@"obj"]];
	
	glClearColor (0.0, 0.0, 0.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
	
	glEnable(GL_TEXTURE_2D);
	
	glViewport(0, 0, primaryContextSize.width, primaryContextSize.height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60, (GLfloat) primaryContextSize.width/(GLfloat) primaryContextSize.height, 1, 30.0);
	
	//rotate the camera if the ams is not on
	if([amsCheck state] != NSOnState)
	{
		float xx[3]={1,0,0};
		float right[3];
		float up[3], gcopy[3]={gravity[0],gravity[1],gravity[2]};
		
		cross(xx, gcopy, right);
		cross(gcopy, right, xx);
		
		float camPos[3]={0.5,-10,-10};
				
		unit(xx); scale(xx, camPos[0]);
		unit(right); scale(right, camPos[1]);
		unit(gcopy); scale(gcopy, camPos[2]);
		
		gluLookAt(xx[0] + right[0] + gcopy[0],
					xx[1] + right[1] + gcopy[1],
					xx[2] + right[2] + gcopy[2], 0,0,0, -gravity[0],-gravity[1],-gravity[2]);
	}
	else
	{
		gluLookAt(0.5,-10,10, 0,0,0, 0,0,1);
	}
	
	float mmatrix[16];
	glGetFloatv( GL_PROJECTION_MATRIX, mmatrix );
	camera[0] = -mmatrix[3]*mmatrix[15];
	camera[1] = -mmatrix[7]*mmatrix[15];
	camera[2] = -mmatrix[11]*mmatrix[15];
	//NSLog(@"%f,%f,%f-%f",camera[0],camera[1],camera[2],mmatrix[15]);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	//enable lighting
	{
		static GLfloat light_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
		static GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
		static GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
		static GLfloat light_position[] = { -.5, 1.0, 2.0, 0.0 }; 
		glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient);
		glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
		glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular);
		glLightfv(GL_LIGHT0, GL_POSITION, light_position); 
		glColor3f (1.0, 1.0, 1.0);
		
		glEnable (GL_LIGHTING);
		glEnable (GL_LIGHT0);
	}
	glEnable( GL_CULL_FACE );

	glEnable(GL_BLEND);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,  GL_MODULATE);
	[model render];
	
	//render ball
	[self renderBall];

	if([shadowCheck state] == NSOnState)
	{
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		[OBJModel startShadows];
		[model castShadow];
		
		glTranslatef(ball[0], ball[1], ball[2]);
		glScalef(radius, radius, radius);
		[sphereModel castShadow];
		[OBJModel finishShadows];
	}
}

- (void)renderBall
{
	glEnable(surfaceTextureTarget);
	glBindTexture(surfaceTextureTarget, surfaceTexture);

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,  GL_DECAL);
	float balldiff[3]={0.3,0.3,0.3};
	float ballspec[3]={0.8,0.8,0.8};
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, balldiff);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, ballspec);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 25);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	//glTranslatef(ball[0], ball[1], ball[2]);
	
	float look[3] = {camera[0]-ball[0],camera[1]-ball[1],camera[2]-ball[2]};
	float up[3]={0,0,1};
	float right[3];
	
	cross(look, up, right);
	cross(look, right, up);
	
	unit(look);
	unit(right);
	unit(up);

	float m[16]={right[0], right[1], right[2], 0,
				up[0], up[1], up[2], 0,
				look[0], look[1], look[2], 0,
				ball[0],ball[1],ball[2],1};
	
	glMultMatrixf(m);
	
	static GLuint ballList = 0;
	
	if(!ballList)
	{
		ballList = glGenLists(1);
		glNewList(ballList, GL_COMPILE);
		
		float r = radius;
		int n = 16;
		
		float zeye = 0.5 / tan(reflectionAngle/2*M_PI/180);

		int i,j;
		double theta1,theta2,theta3;
		XYZ e,p;

		if (r < 0)
		  r = -r;
		if (n < 0)
		  n = -n;

		for (j=0;j<n/2;j++)
		{
		  theta1 = j * 2*M_PI / n - M_PI_2;
		  theta2 = (j + 1) * 2*M_PI / n - M_PI_2;

		  glBegin(GL_QUAD_STRIP);
		  for (i=0;i<=n/2;i++)
		  {
			 theta3 = i * 2*M_PI / n;

			 e.x = cos(theta1) * cos(theta3);
			 e.y = sin(theta1);
			 e.z = cos(theta1) * sin(theta3);
			 p.x = r * e.x;
			 p.y = r * e.y;
			 p.z = r * e.z;
			 
			 glNormal3f(e.x,e.y,e.z);
			 //glTexCoord2f((.5-i/(double)n)*surfaceContextSize.width*2, 2*j/(double)n*surfaceContextSize.height);
			 glTexCoord2f((0.5-tan(theta3-M_PI/2)*zeye), (0.5+tan(theta1)*zeye));
			 glVertex3f(p.x,p.y,p.z);

			 e.x = cos(theta2) * cos(theta3);
			 e.y = sin(theta2);
			 e.z = cos(theta2) * sin(theta3);
			 p.x = r * e.x;
			 p.y = r * e.y;
			 p.z = r * e.z;

			 glNormal3f(e.x,e.y,e.z);
			 //glTexCoord2f((.5-i/(double)n)*surfaceContextSize.width*2, 2*(j+1)/(double)n*surfaceContextSize.height);
			 glTexCoord2f((0.5-tan(theta3-M_PI/2)*zeye), (0.5+tan(theta2)*zeye));
			 glVertex3f(p.x,p.y,p.z);
		  }
		  glEnd();
		}
		glEndList();
	}
	else
	{
		glCallList(ballList);
	}
	glDisable(surfaceTextureTarget);
}

#pragma mark Physics

void  PhysicsApplyForceAndTorque (const NewtonBody* body);
void  PhysicsSetTransform (const NewtonBody* body, const float* matrix);
int  GenericContactProcess (const NewtonMaterial* material, const NewtonContact* contact);


NSTimeInterval diff, world_time;
TaskWrapper *tw = 0;
float amsX, amsY;
float fps=100;

- (void)processStarted
{
}

- (void)processFinished
{
}

- (void)appendOutput:(NSString *)output
{
	NSScanner *scan = [NSScanner scannerWithString:output];
	
	[scan scanFloat:&amsX];
	[scan scanFloat:&amsY];
	[scan scanFloat:NULL];
}

- (void)doPhysics
{	
	if(world_time == 0)
		world_time = [NSDate timeIntervalSinceReferenceDate];
		
	diff = [NSDate timeIntervalSinceReferenceDate] - world_time;
	
	fps += (1.0/diff - fps)/20;
	[fpsText setStringValue:[NSString stringWithFormat:@"%0.2f fps", fps]];
	
	// updtate the Newton physics world
	NewtonUpdate (nWorld, [speedSlider floatValue]*diff);
	
	world_time = [NSDate timeIntervalSinceReferenceDate];
	
	
	NSPoint mouse = [NSEvent mouseLocation];
	
	if(center.x == -1)
	{
		NSRect f = [[NSScreen mainScreen] visibleFrame];
		center.x = NSMidX(f);
		center.y = NSMidY(f);
	}
	
	float sensitivity = 110-[sensitivitySlider floatValue];
	
	float dx,dy;
	if([amsCheck state] == NSOnState)
	{
		if(!tw)
		{
			tw = [[TaskWrapper alloc] initWithController:self
				arguments:[NSArray arrayWithObjects:[[NSBundle mainBundle] pathForResource:@"amstracker" ofType:@""], @"-su 0.1" ,nil]];
			[tw startProcess];
		}
		dx = -10*amsX/sensitivity;
		dy = 10*amsY/sensitivity;
	}
	else
	{
		dx = (mouse.x - center.x)/sensitivity;
		dy = (mouse.y - center.y)/sensitivity;
	}
	
	if(dx > 3) dx = 3;
	if(dy > 3) dy = 3;
	if(dx < -3) dx = -3;
	if(dy < -3) dy = -3;
		
	gravity[0] += (dx-gravity[0])/4;
	gravity[1] += (dy-gravity[1])/4;
	gravity[2] = -2*9.8;
	
	unit(gravity); scale(gravity, 2*9.8);
	
	//if dropped
	float matrix[16];
	NewtonBodyGetMatrix(ballBody, matrix);
	if(matrix[14] < -5)
	{
		// set the trasformation matrix
		float matrix[16]={1,0,0,0,
						  0,1,0,0,
						  0,0,1,0,
						  -4,4,2,1};
		NewtonBodySetMatrix (ballBody, matrix);
	}
}

- (void)initScene
{
	// create the newton world
	nWorld = NewtonCreate (NULL, NULL);

	//create the world from an obj file
	if(!model)
		model = [[OBJModel alloc] initWithFile:[[NSBundle mainBundle] pathForResource:@"puzzle2" ofType:@"obj"]];

	NewtonCollision *collision = (NewtonCollision*)[model newtonCollisionInWorld:nWorld withCallback:NULL];
	NewtonBody *floorBody = NewtonCreateBody (nWorld, collision);
	NewtonReleaseCollision (nWorld, collision);
	

	// create the collsion shape
	collision = NewtonCreateSphere (nWorld, radius, radius, radius, NULL); 

	// create the ridid body
	ballBody = NewtonCreateBody (nWorld, collision);

	// Get Rid of the collision
	NewtonReleaseCollision (nWorld, collision);
	
	float mass = 4;
	float Ixx = 0.4f * mass * radius * radius; 
	float Iyy = Ixx; 
	float Izz = Ixx; 
	NewtonBodySetMassMatrix (ballBody, mass, Ixx, Iyy, Izz);

	// set the trasformation matrix
	float matrix[16]={1,0,0,0,
					  0,1,0,0,
					  0,0,1,0,
					  -4,4,2,1};
	NewtonBodySetMatrix (ballBody, matrix);
	
	// set the force and torque call back funtion
	NewtonBodySetForceAndTorqueCallback (ballBody, PhysicsApplyForceAndTorque);
	
	// set the tranform call back function
	NewtonBodySetTransformCallback (ballBody, PhysicsSetTransform);
	
	//don't freeze
	NewtonBodySetAutoFreeze (ballBody, 0);
	NewtonWorldUnfreezeBody (nWorld, ballBody);
	
	
	
	//materials
	int defaultID = NewtonMaterialGetDefaultGroupID (nWorld);
	NewtonMaterialSetDefaultFriction (nWorld, defaultID, defaultID, 2, 0.5f);
	NewtonMaterialSetDefaultElasticity (nWorld, defaultID, defaultID, 0.2f);
	NewtonMaterialSetDefaultSoftness (nWorld, defaultID, defaultID, 0.8f);
	NewtonMaterialSetCollisionCallback (nWorld, defaultID, defaultID, 
		1, NULL, GenericContactProcess, NULL);
	
	//dry rolling friction
	new DryRollingFriction (ballBody, radius, 5.2f);
	
	//no damping
	float damp[3]; 
	damp[0] = 0.0f; 
	damp[1] = 0.0f; 
	damp[2] = 0.0f; 
	NewtonBodySetLinearDamping (ballBody, 0.0f); 
	NewtonBodySetAngularDamping (ballBody, damp);
}

#pragma mark Callbacks

// set the tranformation of a rigid body
void  PhysicsApplyForceAndTorque (const NewtonBody* body)
{
	float mass, Ixx, Iyy, Izz;

	NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz);
	float force[3] = {mass*gravity[0], mass*gravity[1], mass*gravity[2]};
	NewtonBodyAddForce (body, force);
}

// add force and torque to rigid body
void  PhysicsSetTransform (const NewtonBody* body, const float* matrix)
{
	ball[0] = matrix[12];
	ball[1] = matrix[13];
	ball[2] = matrix[14];
}

float contactSpeed = 0.1;

NSMovieView *mv = NULL;

// this callback is called for every contact between the two bodies
int  GenericContactProcess (const NewtonMaterial* material, const NewtonContact* contact)
{
	float speed0;
	float speed1;
	
	if(!mv)
	{
		mv = [[NSMovieView alloc] initWithFrame:NSZeroRect];
		NSMovie *m = [[NSMovie alloc] initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"Bop" ofType:@"aif"]] byReference:NO];
		[mv setMovie:m];
	}

	// Get the maximun normal speed of this impact. this can be used for particels of playing collision sound
	speed0 = NewtonMaterialGetContactNormalSpeed (material, contact);
	if (speed0 > contactSpeed && (![mv isPlaying] || speed0 > [mv volume]))
	{
		// save the position of the contact (for 3d sound of particles effects)
		//contactSpeed = speed0;
		//if(speed0 > 1) speed0 = 1;
		[mv setVolume:speed0/10];
		[mv gotoBeginning:NULL];
		[mv start:NULL];
		//[[NSSound soundNamed:@"Bop"] play];
		//NewtonMaterialGetContactPositionAndNormal (material, &g_currectEffect->m_position.m_x, &normal.m_x);
	}

	// return one to tell Newton we want to accept this contact
	return 1;
}

@end
