#import "NeXTView.h"
#import <math.h>
#import "WireFrame.h"
#import <sys/dir.h>
#import <sys/types.h>
#import <objc/NXBundle.h>


// this code is copyright Darcy Brockbank, 1993
//
// You may freely reuse and distribute this code in any way shape or
// form, provided that this notice stays intact.
//
// darcy@solutions.ca, samurai@cs.mcgill.ca
//
// StefView was implemented out of WorldSpaceView and retains some of the
// movement properties...
//
// The code for WorldSpaceView was written by Sam Streeper at NeXT, I think,
// and there were two other contributors, but I can't find their names in the
// source for it.
//
// NeXTView shows a simple (ha!) spinning NeXTcube logo, in full 3D. The 3D code
// came from an app I wrote a long time ago called "WireFrame", and so the code
// is pretty scary. As well, this thing is only a quick hack, though a nice one.
//
// This thing has room for improvement, and if you do so, send me a copy!
//
// - darcy

@implementation NeXTView


#define X_PERIOD 15000.0
#define Y_PERIOD 12000.0
#define DEFAULT_FPS 10
#define PI 3.1415926535
#define MAX_IMAGE_WIDTH 256
#define MAX_IMAGE_HEIGHT 256
#define MAX_X_SPEED (26)
#define MAX_Y_SPEED (26)
#define BUFFER_WIDTH (MAX_IMAGE_WIDTH + MAX_X_SPEED + 1)
#define BUFFER_HEIGHT (MAX_IMAGE_HEIGHT + MAX_Y_SPEED + 1)
#define PRINT(a) printf(a)

#define MINX 0.0
#define MINY 0.0


#define ZOOM 680.0

- takeSpot:(float)x:(float)y;
{
	[self draw];
	return self;
}

- takeSnapshot;
{
	return self;
}

- oneStep
{
	NXRect black = {0,0,0,0};
	NXRect ballRect;
	BRECT new;
	static float xdelta = 2.0;
	static float ydelta = 2.0;
	int changed = 0;
	
	then = now;
	now = currentTimeInMs();

	xpos += xdelta;
	if (xpos<MINX) {
		xpos = MINX;
		xdelta*=-1.0;
		changed=1;
	} else if (xpos>maxCoord.x){
		xpos = maxCoord.x;
		xdelta*=-1.0;
		changed=1;
	}
	ypos += ydelta;
	if (ypos<MINY) {
		ypos = MINY;
		ydelta*=-1.0;
		changed=1;
	} else if (ypos>maxCoord.y){
		ypos = maxCoord.y;
		ydelta*=-1.0;
		changed=1;
	}

	if (changed){
#if 0
		Axis flip[3]={y,y,y};
		Direction dir[2]={positive,positive};
#else
		Axis flip[3]={y,z,x};
		Direction dir[2]={positive,negative};
#endif

		axis = flip[axis];
		spin = dir[spin];
	}
	
	new.l = floor(xpos);
	new.b = floor(ypos);
	new.r = new.l + imageSize.width;
	new.t = new.b + imageSize.height;

	
	ballRect.origin.x = 0;
	ballRect.origin.y = 0;
	ballRect.size.width = imageSize.width;
	ballRect.size.height = imageSize.height;
	
	redrawTo.x = MIN(new.l, old.l);
	redrawTo.y = MIN(new.b, old.b);

	redraw.origin.x = 0;
	redraw.origin.y = 0;
	redraw.size.width = (MAX(new.r, old.r)) - redrawTo.x + 1;
	redraw.size.height = (MAX(new.t, old.t)) - redrawTo.y + 1;
	
	black.size= redraw.size;


	[buffer lockFocus];
	PSsetgray(0);
	NXRectFill(&black);

	[self draw];
	
	ballTo.x = new.l - redrawTo.x;
	ballTo.y = new.b - redrawTo.y;

	[buffer unlockFocus];
	[buffer composite:NX_COPY toPoint:&redrawTo];
	old = new;
	return self;
}


#define SIZE 200.0

- initFrame:(const NXRect *)frameRect
{
	const char *animSpeed;
	NXRect black = {0, 0, SIZE, SIZE };

	imageSize = black.size;
	[super initFrame:frameRect];
	[self allocateGState];		// For faster lock/unlockFocus
	[self setClipping:NO];		// even faster...

	PSsetlinewidth(0.15);
	backgroundColour=NX_COLORBLACK;
	worldOrigin.x=0.0;
	worldOrigin.y=0.0;
	worldOrigin.z=0.0;
	translationRate = 3.0;
	currentZoom=ZOOM;
	list = [[List alloc] init];
	axis = y;
	spin = positive;
	
	buffer = [[NXImage alloc] initSize:&black.size];

	if ([buffer lockFocus])
	{
		PSsetgray(0);
		NXRectFill(&black);
		[buffer unlockFocus];
	}
	animSpeed = NXGetDefaultValue([NXApp appName], "animSpeed");
	if (animSpeed == NULL) framesPerSecond = DEFAULT_FPS;
	else framesPerSecond = atoi(animSpeed);
	nextRotationTime = 0;
	[self newViewSize];
	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	[super sizeTo:width :height];
	[self newViewSize];
	return self;
}

- newViewSize
{
	//this is called every time View size changes
	NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };

	then = now = currentTimeInMs();

	if (oldSize.width == bounds.size.width &&
			oldSize.height == bounds.size.height)
		return self;
	else
	{
		oldSize.width = bounds.size.width;
		oldSize.height = bounds.size.height;
	}
	
	maxCoord.x = bounds.size.width - imageSize.width - MINX;
	maxCoord.y = bounds.size.height - imageSize.height - MINY;
	if (maxCoord.x < 0) maxCoord.x = 0;
	if (maxCoord.y < 0) maxCoord.y = 0;


	old.l = old.r = maxCoord.x/2 + MINX;
	old.b = old.t = maxCoord.y/2 + MINY;
	ballTo.x = ballTo.y = 0;

	if ([buffer lockFocus])
	{
		PSsetgray(0);
		NXRectFill(&black);
		[buffer unlockFocus];
	}

	return self;
}

- (const char *)windowTitle
{
	return "NeXTView";
}



- inspector:sender
{
	char buf[MAXPATHLEN];
	if (!sharedInspectorPanel){
		sprintf(buf,"%s/NeXTView.nib",[sender moduleDirectory:"NeXT"]);
		[NXApp loadNibFile:buf owner:self withNames:NO];
		[self loadDir:[sender moduleDirectory:"NeXT"]];
	}
	return sharedInspectorPanel;
}

- tryToChangeCentre
{
	id object=[self firstObject];
	if (object) {
		[object getCentre:&curOrigin.x:&curOrigin.y:&curOrigin.z];
	} else {
		curOrigin.x = curOrigin.y = curOrigin.z = 0.0;
	}
	return self;
}

- firstObject
{
	return [list objectAt:0];
}

- free 
{
	[list free];
	return [super free];
}
	
- (const char *)world;
{
	return "NeXTcube.world";
}

- loadDir:(const char *)dir
{
        char world[MAXPATHLEN+1];
	sprintf(world,"%s/%s",dir,[self world]);
	[self loadWorld:world];
	[self tryToChangeCentre];
	[self display];
	return self;
}

- loadWorld:(const char *)world
{
	DIR *dirp;
	id newFrame;
	struct direct *dp;
	char *tail;
	char temp[1024];
	
	
	dirp=opendir(world);
	if (!dirp) {
		NXRunAlertPanel("Directory Error", "Can't read directory: %s","OK",0,0,world);
	} else {
		for(dp=readdir(dirp);dp!=NULL;dp=readdir(dirp)){
			if ((tail=(rindex(dp->d_name,'.')))!=NULL){
				if (strcmp((tail+1),"wireframe")==0){
					sprintf(temp,"%s/%s",world,dp->d_name);
					newFrame = [[WireFrame alloc] init];
					[newFrame makeWireFrame:temp];
					[self addThisObject:newFrame];
				}
				if (strcmp((tail+1),"wireframeb")==0){
					sprintf(temp,"%s/%s",world,dp->d_name);
					newFrame = [[WireFrame alloc] init];
					[newFrame readBinaryCodeFrom:temp];
					[self addThisObject:newFrame];
				}
			}
		}			
	}
	return self;
}

- setTranslationRate:sender;
{
	translationRate=[sender floatValue];
	return self;
}


- (int)numberOfObjects
{
	return [list count];
}

- addThisObject:anObject
{
	[list addObject:anObject];
	return self;
}

- setZoom: sender
{
	currentZoom=[sender floatValue];
	[self display];
	return self;
}


- draw;
{
	int i,count = [list count] ;
	NXPoint zero = {0.0,0.0};

	for (i=0;i<count;i++){
		id wireFrame = [list objectAt:i];
		[wireFrame rotate:spin aroundAxis:axis atOrigin:curOrigin];
		[wireFrame drawYourself:currentZoom
			centeredOn:imageSize.width/2.0 :imageSize.height/2.0];
	}
	return self;
}

- translate:(Direction)dir alongAxis:(Axis)a atRate:(float)rate
{ 
	int i,count = [list count];
	for (i=0;i<count;i++){
		[[list objectAt:i] translate:dir
					alongAxis:a
					atRate:rate];
	}
	[self tryToChangeCentre];
	return self;
}


- rotate:(Direction)dir aroundAxis:(Axis)a atOrigin:(Origin)orig
{
	int i,count = [list count];

	for (i=0;i<count;i++){
		[[list objectAt:i] rotate:dir
					aroundAxis:a
					atOrigin:orig];
	}
	return self;
}

- rotatey:sender;
{
	[self rotate:negative aroundAxis:y atOrigin:curOrigin];
	return self;
}
- rotateY:sender;
{
	[self rotate:positive aroundAxis:y atOrigin:curOrigin];
	return self;
}
- rotatez:sender;
{
	[self rotate:negative aroundAxis:z atOrigin:curOrigin];
	return self;
}
- rotateZ:sender;
{
	[self rotate:positive aroundAxis:z atOrigin:curOrigin];
	return self;
}
- rotatex:sender;
{
	[self rotate:negative aroundAxis:x atOrigin:curOrigin];
	return self;
}
- rotateX:sender;
{
	[self rotate:positive aroundAxis:x atOrigin:curOrigin];
	return self;
}
	
@end
