//
//  HapticView.m
//  OffScreenHapticRenderer
//
//  Created by Luke Pullella.
//  Copyright 2006 Luke Pullella. All rights reserved.
//

#import "HapticView.h"

@implementation HapticView

- (id) init
{
	self = [super init];
	if (self != nil)
	{
		if (!([self initGLContext] && [self initGLWorkspace] &&
		      [self initHL]        && [self initHLWorkspace] ))
			return nil;
	}
	return self;
}

- (bool)initGLContext
{
	NSOpenGLPixelFormat *nsglFormat;

	NSOpenGLPixelFormatAttribute attr[] = 
		{
		NSOpenGLPFADoubleBuffer,
		NSOpenGLPFAAccelerated,
		NSOpenGLPFAColorSize, BITS_PER_PIXEL,
		NSOpenGLPFADepthSize, DEPTH_SIZE,
		0 
		};
	
	nsglFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
	
	if(!nsglFormat) { NSLog(@"Invalid format... terminating."); return NO; }
	
	openGLContext = [[NSOpenGLContext alloc] initWithFormat:nsglFormat shareContext:nil];
	[nsglFormat release];

	if(!self) { NSLog(@"Self not created... terminating."); return NO; }

	[openGLContext makeCurrentContext];
	
	return YES;
}

- (bool)initGLWorkspace
{
	/*glClearColor(0.0, 0.0, 0.0, 0.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
	
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_DEPTH_TEST);
	
	glViewport(0,0,250,250);*/
	
/*	static const GLfloat light_model_ambient[] = {0.3f, 0.3f, 0.3f, 1.0f};
    static const GLfloat light0_diffuse[] = {0.9f, 0.9f, 0.9f, 0.9f};   
    static const GLfloat light0_direction[] = {0.0f, -0.4f, 1.0f, 0.0f}; */   
    
    // Enable depth buffering for hidden surface removal.
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);
    
    // Cull back faces.
    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);
    
    // Setup other misc features.
    //glEnable(GL_LIGHTING);
    glEnable(GL_NORMALIZE);
    glShadeModel(GL_SMOOTH);
    
    // Setup lighting model.
	/*
    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);    
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_model_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light0_direction);
    glEnable(GL_LIGHT0);*/
	
	return YES;
}

- (bool)initHL
{
	HDErrorInfo error;
	
	myHHD = HD_INVALID_HANDLE;
	myHHLRC = 0;
	
	myHHD = hdInitDevice(HD_DEFAULT_DEVICE);
	if(HD_DEVICE_ERROR(error = hdGetError()))
	{
		NSLog(@"Couldn't Initialise Haptic Device");
		return NO;
	}
	
	myHHLRC = hlCreateContext(myHHD);
	hlMakeCurrent(myHHLRC);
	
	return YES;
}

- (bool)initHLWorkspace
{
	hlEnable(HL_HAPTIC_CAMERA_VIEW);
	
	hlTouchableFace(HL_FRONT);
	
	return YES;
}

- (void)stop
{
	[timer invalidate];
	[timer release];
	
	[hsMyShape release];
	
	[self stopHL];
	[self stopGL];
}

- (void)stopGL
{
	[openGLContext release];
	[glView release];
}

- (void)stopHL
{
    hlMakeCurrent(NULL);
    if (myHHLRC != NULL)
    {
        hlDeleteContext(myHHLRC);
    }
	
    if (myHHD != HD_INVALID_HANDLE)
    {
        hdDisableDevice(myHHD);
    }
}

- (void)resizeGL:(NSView *)view
{	
	static const double kPI = 3.1415926535897932384626433832795;
    static const double kFovY = 40;

    double nearDist, farDist, aspect;
	
	double width = [view frame].size.width;
	double height = [view frame].size.height;

    glViewport(0, 0, width, height);

    // Compute the viewing parameters based on a fixed fov and viewing
    // a canonical box centered at the origin.

    nearDist = 1.0 / tan((kFovY / 2.0) * kPI / 180.0);
    farDist = nearDist + 2.0;
	NSLog(@"Near Distance is %f and Far Distance is %f", nearDist, farDist);
    aspect = (double) width / height;
   
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(kFovY, aspect, nearDist, farDist);

    // Place the camera down the Z axis looking at the origin.
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();            
    gluLookAt(0, 0, nearDist + 1.0,
              0, 0, 0,
              0, 1, 0);
}

- (void)resizeHL
{
    GLdouble projection[16];
    glGetDoublev(GL_PROJECTION_MATRIX, projection);

    hlMatrixMode(HL_TOUCHWORKSPACE);
    hlLoadIdentity();
    
    hluFitWorkspace(projection);
}

- (void)startDrawingOn:(NSView *)view with:(HapticShape *)shape
{
	glView = [view retain];
	[openGLContext setView:glView];
	[self resizeGL:glView];
	[self resizeHL];
	
	hsMyShape = [[shape init] retain];
	
	// Start a timer to continually redraw the view
	timer = [[NSTimer	scheduledTimerWithTimeInterval:REPEAT_INTERVAL
						target:self
						selector:@selector(drawScene)
						userInfo:nil
						repeats:YES] retain];
}

- (void)changeShapeTo:(HapticShape *)shape
{
	[hsMyShape release];
	hsMyShape = [[shape init] retain];
}

- (void)drawScene
{
	[hsMyShape drawShape];
	
	[openGLContext flushBuffer];
}

@end
