//
// Cocoa OpenGL example
//

#import	<OpenGL/gl.h>
#import	<OpenGL/glu.h>
#import	<stdlib.h>
#import	<memory.h>
#import "MyOpenGLView.h"
#import	"Vector3D.h"

Vector3D	eye   ( 7, 5, 7 );			// camera position
Vector3D	light ( 5, 0, 4 );			// light position
Vector3D	rot ( 0, 0, 0 );
NSPoint		mouseOld = { 0, 0 };
float	 	angle     = 0;

void    drawBox ( const Vector3D& pos, const Vector3D& size, unsigned texture, bool cull )
{
    float   x2 = pos.x + size.x;
    float   y2 = pos.y + size.y;
    float   z2 = pos.z + size.z;

    glBindTexture ( GL_TEXTURE_2D, texture );
    glEnable      ( GL_TEXTURE_2D );

    if ( cull )
    {
        glCullFace ( GL_BACK );
        glEnable   ( GL_CULL_FACE );
    }
    else
        glDisable ( GL_CULL_FACE );

    glBegin ( GL_QUADS );
                                    // front face
        glNormal3f   ( 0, 0, 1 );

        glTexCoord2f ( 0, 0 );
        glVertex3f   ( pos.x, pos.y, z2 );

        glTexCoord2f ( size.x, 0 );
        glVertex3f   ( x2, pos.y, z2 );

        glTexCoord2f ( size.x, size.y );
        glVertex3f   ( x2, y2, z2 );

        glTexCoord2f ( 0, size.y );
        glVertex3f   ( pos.x, y2, z2 );

                                    // back face
        glNormal3f ( 0, 0, -1 );

        glTexCoord2f ( size.x, 0 );
        glVertex3f   ( x2, pos.y, pos.z );

        glTexCoord2f ( 0, 0 );
        glVertex3f   ( pos.x, pos.y, pos.z );

        glTexCoord2f ( 0, size.y );
        glVertex3f   ( pos.x, y2, pos.z );

        glTexCoord2f ( size.x, size.y );
        glVertex3f   ( x2, y2, pos.z );

                                    // left face
        glNormal3f ( -1, 0, 0 );

        glTexCoord2f ( 0, 0 );
        glVertex3f   ( pos.x, pos.y, pos.z );

        glTexCoord2f ( 0, size.z );
        glVertex3f   ( pos.x, pos.y, z2 );

        glTexCoord2f ( size.y, size.z );
        glVertex3f   ( pos.x, y2, z2 );

        glTexCoord2f ( size.y, 0 );
        glVertex3f   ( pos.x, y2, pos.z );

                                    // right face
        glNormal3f ( 1, 0, 0 );

        glTexCoord2f ( 0, size.z );
        glVertex3f   ( x2, pos.y, z2 );

        glTexCoord2f ( 0, 0 );
        glVertex3f   ( x2, pos.y, pos.z );

        glTexCoord2f ( size.y, 0 );
        glVertex3f   ( x2, y2, pos.z );

        glTexCoord2f ( size.y, size.z );
        glVertex3f   ( x2, y2, z2 );

                                    // top face
        glNormal3f ( 0, 1, 0 );

        glTexCoord2f ( 0, size.z );
        glVertex3f   ( pos.x, y2, z2 );

        glTexCoord2f ( size.x, size.z );
        glVertex3f   ( x2, y2, z2 );

        glTexCoord2f ( size.x, 0 );
        glVertex3f   ( x2, y2, pos.z );

        glTexCoord2f ( 0, 0 );
        glVertex3f   ( pos.x, y2, pos.z );

                                    // bottom face
        glNormal3f ( 0, -1, 0 );

        glTexCoord2f ( size.x, size.z );
        glVertex3f   ( x2, pos.y, z2 );

        glTexCoord2f ( 0, size.z );
        glVertex3f   ( pos.x, pos.y, z2 );

        glTexCoord2f ( 0, 0 );
        glVertex3f   ( pos.x, pos.y, pos.z );

        glTexCoord2f ( size.x, 0 );
        glVertex3f   ( x2, pos.y, pos.z );

    glEnd ();

    if ( cull )
        glDisable ( GL_CULL_FACE );
}

@implementation MyOpenGLView

- (id) initWithFrame: (NSRect) frameRect pixelFormat: (NSOpenGLPixelFormat *) format
{
	self = [super initWithFrame: frameRect pixelFormat: format];
	
	return self;
}

- (GLuint) loadTextureFromResource: (NSString *) name
{
	NSString * path = [[NSBundle mainBundle] pathForImageResource: name];
	
	if ( path == nil )
		return 0;
		
	return [self loadTextureFromFile: path];
}

- (GLuint) loadTextureFromFile: (NSString *) fileName
{
	NSData * data  = [NSData dataWithContentsOfFile: fileName];
	
	if ( data == nil )
	{
		NSLog ( @"Unable to load: %@", fileName );
		
		return 0;
	}
	
	NSBitmapImageRep * image = [NSBitmapImageRep imageRepWithData: data];
	
	if ( image == nil )
	{
		NSLog ( @"Unable to load texture" );
		
		return 0;
	}
	
	NSLog ( @"Texture loaded" );
			
	int		bitsPerPixel = [image bitsPerPixel];
	int		bytesPerRow  = [image bytesPerRow];
	GLenum	format;
	
	if ( bitsPerPixel == 24 )
		format = GL_RGB;
	else
	if ( bitsPerPixel == 32 )
		format = GL_RGBA;
	else
		return 0;
		
	int				width  = [image pixelsWide];
	int			    height = [image pixelsHigh];
	unsigned char * imageData = [image bitmapData];
	
								// now flip image vertically
	unsigned char * ptr = (unsigned char *) malloc ( width * height * 4 );
	
	for ( int row = height - 1; row >= 0; row-- )
		memcpy ( ptr + (height - row) * bytesPerRow, imageData + row * bytesPerRow, bytesPerRow );
		
	GLuint	id;
	
	glGenTextures     ( 1, &id );
	glBindTexture     ( GL_TEXTURE_2D, id );
	gluBuild2DMipmaps ( GL_TEXTURE_2D, format, width, height, format, GL_UNSIGNED_BYTE, ptr );
	glTexParameteri   ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	glTexParameteri   ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );

	free ( ptr );
	
	return id;
}

- (void) reshape
{
	[super reshape];
	
	NSRect	bounds = [self bounds];
	float	width  = bounds.size.width;
	float	height = bounds.size.height;
	float	aspect = width / height;
	
	glEnable     ( GL_DEPTH_TEST );
	glDepthFunc  ( GL_LEQUAL );  
	glHint       ( GL_POLYGON_SMOOTH_HINT,         GL_NICEST );
	glHint       ( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
	
	glViewport ( 0, 0, (GLint) width, (GLint) height );
	
	glMatrixMode   ( GL_PROJECTION );
   											// factor all camera ops into projection matrix
	glLoadIdentity ();
	gluPerspective ( 60.0, aspect, 1.0, 60.0 );
	gluLookAt      ( eye.x, eye.y, eye.z,	// eye
					 0, 0, 0,				// center
					 0.0, 0.0, 1.0 );		// up

	glMatrixMode   ( GL_MODELVIEW );
	glLoadIdentity ();
}

- (void) drawRect: (NSRect) bounds
{
	if ( texture == 0 )
		texture = [self loadTextureFromResource: @"Oxidated"];
	
	glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	
	glMatrixMode   ( GL_MODELVIEW );
	glPushMatrix   ();

    glRotatef    ( rot.x, 1, 0, 0 );
    glRotatef    ( rot.y, 0, 1, 0 );
    glRotatef    ( rot.z, 0, 0, 1 );

	drawBox ( Vector3D ( -2, -2, -2 ), Vector3D ( 4, 4, 4 ), texture, false );
	
    glPopMatrix ();
	
	glFlush ();
}

- (void) mouseDragged: (NSEvent *) theEvent
{
	NSPoint	pt = [theEvent locationInWindow];
	
	pt = [self convertPoint: pt fromView: nil];

    rot.y -= ((mouseOld.y - pt.y) * 180.0f) / 200.0f;
    rot.z -= ((mouseOld.x - pt.x) * 180.0f) / 200.0f;
    rot.x  = 0;

    if ( rot.z > 360 )
		rot.z -= 360;

	if ( rot.z < -360 )
		rot.z += 360;

    if ( rot.y > 360 )
		rot.y -= 360;

	if ( rot.y < -360 )
        rot.y += 360;

    mouseOld = pt;
	
	[self setNeedsDisplay: YES];
}

- (void) mouseDown: (NSEvent *) theEvent
{
	NSPoint	pt = [theEvent locationInWindow];
	
	mouseOld = [self convertPoint: pt fromView: nil];
}

@end
