
/* Generated by Interface Builder */

#import <appkit/appkit.h> 	
#import "StereoView.h"
#import "CubeView.h"
#import "StoreData.h"
#import "JoyStick.h"
#import "stdio.h"
#import <libc.h>		// for chdir, getwd

#define equal(s,w)	(strcmp(s,w) == 0)
#undef  min
#define min(a,b)	((a) < (b) ? (a) : (b))
#undef  max
#define max(a,b)	((a) > (b) ? (a) : (b))
#define ctrl(c)	        ((c) - 'A' + 1)	/* control character mapping */
#define plural(n)	((n) == 1 ? "" : "s")
#define thest(n)	((n) == 1 ? "-st" : ((n) == 2 ? "-nd" : ((n) == 3 ? "-rd" \
 : "-th")))
#define lastch(s)	s[strlen(s)-1]
#define no_ret(s)	{ register int xyz; /* varname is for lint */	      \
		          for (xyz=strlen(s)-1; xyz >= 0 && 		      \
				(s[xyz] == '\r' || s[xyz] == '\n'); )	      \
			     s[xyz--] = '\0';                                 \
			}
#define first_word(s,w) (strncmp(s,w, strlen(w)) == 0)
#define whitespace(c)	((c) == ' ' || (c) == '\t')
#define ok_char(c)	(isalnum(c) || (c) == '-' || (c) == '_' || (c) == '.')
#define quote(c)	((c) == '"' || (c) == '\'') 
#define onoff(n)	(n == 0 ? "OFF" : "ON")
/* find tab stops preceding or following a given column position 'a', where
 * the column position starts counting from 1, NOT 0!
 * The external integer "tabspacing" must be declared to use this. */
#define prev_tab(a,tb)	(((((a-1)/(tb)))*(tb))+1)
#define next_tab(a,tb)	(((((a-1)/(tb))+1)*(tb))+1)
#define EYE 2.9/57.3
#define rad_to_deg  (180./M_PI)
#define MAXLINE 15000
#define MAXNUM  15000
#define MAXALLOC 200
/*
#define TRUE -1
#define FALSE !TRUE
*/

@implementation StereoView

- initFrame:(const NXRect *)frameRect
{
	
	textDidChange = 0;
	textNeedsSaving = 0;
  dataList = [[List alloc] init];
  max_path = 200;
  path = (float *) calloc(2*max_path, sizeof(float));
	eyeOffset = EYE;
	openReq = [OpenPanel new];
	saveReq = [SavePanel new];
	[super initFrame:frameRect];
	bcopy(frameRect,&left_Rect,sizeof(NXRect));
	bcopy(frameRect,&right_Rect,sizeof(NXRect));

	NXDivideRect(&left_Rect,&right_Rect,NX_WIDTH(frameRect)/2.,2);

	leftView = [[CubeView alloc] initFrame:&left_Rect];
	rightView = [[CubeView alloc] initFrame:&right_Rect];
	[self setDrawOrigin:(NXCoord) NX_X(frameRect):(NXCoord) NX_Y(frameRect)];
	[self addSubview:leftView];
	[self addSubview:rightView];
	[self addSubview:joyStick];
	[self setAutoresizeSubviews:TRUE];
	fileNAME = (char *) calloc(1,256);
	strcpy(fileNAME,"Untitled");
	[self S_Reset:self];
	return self;
}

static char  angle_buffer[20];


- setDelegate:anObject
{
	delegate = anObject;
	return self;
}

- copy:sender			/* code contributed by Dan McCreary of NeXT */
{
  id pb = [Pasteboard new];	/* global Pasteboard object */ 
  NXStream  *st;		/* stream to collect data in */ 
  char      *data;		/* actual data buffer */ 
  int       length;		/* length of data */ 
  int       maxLength;		/* (not used here) */
				/* To see how to use the pasteboard */
				/* see page 10-33 of the SysRefMan */
  [self  prtCopy];
				/* declare that we will supply a */
				/* single type of data: PostScript */
  [pb declareTypes:&NXPostScriptPboard num:1 owner:self];
				/* get a stream which writes to memory */ 
  st = NXOpenMemory( NULL, 0, NX_WRITEONLY );
				/* write PostScript code for this view into the stream */ 
  [self copyPSCodeInside:NULL to:st];
				/* get actual data buffer from stream */ 
  NXGetMemoryBuffer( st, &data, &length, &maxLength );
				/* write PostScript data to pasteboard */
  [pb writeType:NXPostScriptPboard data:data length:length];
				/* deallocate stream, including its buffer */ 
  NXCloseMemory( st, NX_FREEBUFFER );
  [self display];
  return self; 
}


-(BOOL)acceptsFirstResponder
{				/* make this view accept first */
	return YES;		/* responder so it will understand copy */
}

/*
-resignFirstResponder
{
	return self;
}
*/
- showError:(char *)errorMessage
{
  NXRunAlertPanel("Error", errorMessage, "OK", NULL, NULL);
  return self;
}

- prtCopy
{
  	[leftView printPSCode:leftView];
  	[rightView printPSCode:rightView];
	[self display];
	return self;
}

- printPSCode:sender
{
  [leftView printPSCode:leftView];
  [rightView printPSCode:rightView];
  [self display];
  [super printPSCode:self];
  [self display];
  return self;
}

- textChanged:sender
{
	textDidChange = 1;
	return self;
}

- textEdited:sender
{
	textNeedsSaving = 1;
	return self;
}

- plot:sender
{
	NXStream	*stream;

	if (textDidChange == 1)	{
	if (delegate && [delegate respondsTo:@selector(stereoView:providePoints:)])
		[delegate stereoView:self providePoints:&stream];
		textDidChange = 0;
	[self readData:(NXStream *)stream];
	[leftView getStruct:dataList];
	[rightView getStruct:dataList];
	[leftView reScale:self];
	[rightView reScale:self];
		}
	[self set_Theta:self];
	[self set_Phi:self];
	[self set_dist:self];
	[self display];
	return self;
}

- clear:sender
{
	
	/* release existing data */
	if ([dataList count] != 0) [dataList freeObjects];
	[leftView getStruct:dataList];
	[rightView getStruct:dataList];
//	[leftView clear:self];
//	[rightView clear:self];
//	[self set_Theta:self];
//	[self set_Phi:self];
//	[self set_dist:self];
	[self S_Reset:self];
	[self textChanged:self];
	[self display];
	return self;
}

- S_Reset:sender
{
	float	floatValue;
	NXPoint theLocation;
	
	[leftView getStruct:dataList];
	[rightView getStruct:dataList];
	theLocation.x = 0.;
	theLocation.y = 0.;
	[joyStick reSet];
	[self setPhiTheta:self];
	floatValue = 2.0;
  [showDist setFloatValue:floatValue];
	[DistanceSlider setFloatValue:floatValue];
	[leftView Reset:leftView];
	[rightView Reset:rightView];
	[leftView setTheta:-eyeOffset];
	[rightView setTheta:+eyeOffset];
	[[self window] setTitle:fileNAME];
    [self display];
	return self;
}

- setPhiTheta:sender
{
	float phi, theta;
	
	phi = 2*[joyStick getYVal:self]*90.;
	theta = 2*[joyStick getXVal:self]*180.;
	[showPhi setFloatValue:phi];
	[showTheta setFloatValue:theta];
	[self set_Theta:self];
	[self set_Phi:self];
	return self;
}

- set_Theta:sender
{
	float floatValue;
	
	floatValue = [showTheta floatValue]/rad_to_deg;
  [leftView setTheta:floatValue-eyeOffset];
  [rightView setTheta:floatValue+eyeOffset];
  [self display];
  return self;
}

- set_Phi:sender
{
	float floatValue;
	
	floatValue = [showPhi floatValue]/rad_to_deg;

  [leftView setPhi:floatValue];
  [rightView setPhi:floatValue];
  [self display];
  return self;
}


- set_dist:sender
{
	float floatValue;
	
	floatValue = [DistanceSlider floatValue];

  sprintf(angle_buffer, "%6.2f", floatValue);
  [showDist setStringValue:angle_buffer];
  [leftView setdist:floatValue];
  [rightView setdist:floatValue];
  [self display];
  return self;
}

- setAxes:(int) intVal
{	
	[AxesButton setState:intVal];
	[leftView setAxes:intVal];
	[rightView setAxes:intVal];
	[self display];
	return self;
}

- (int)getAxes
{
	return [AxesButton state];
}

- toggle_Axes:sender
{
	[leftView toggleAxes:self];
	[rightView toggleAxes:self];
	[self display];
  return self;
}

- setCube:(int) intVal
{
	[CubeButton setState:intVal];
	[leftView setCube:intVal];
	[rightView setCube:intVal];
	[self display];
	return self;
}

- (int)getCube
{
	return [CubeButton state];
}

- toggle_Cube:sender
{
	[leftView toggleCube:self];
	[rightView toggleCube:self];
  [self display];
  return self;
}

- setEyeOffset:(float) floatVal
{
	int	eye;
	
	eyeOffset = floatVal;
			
	if (floatVal < 0.)eye = NO;
		else eye = YES;
	[EyeButton setState:eye];
	[self set_Theta:self];
    [self display];
	return self;
}

- (float) getEyeOffset
{
	return eyeOffset;
}

- toggle_Eyes:sender
{
	[self setEyeOffset: - eyeOffset];
  return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	NXRect frameRect;
	
	[super sizeTo:width :height];

	[self getFrame:&frameRect];
	bcopy(&frameRect,&left_Rect,sizeof(NXRect));
	bcopy(&frameRect,&right_Rect,sizeof(NXRect));

	NXDivideRect(&left_Rect,&right_Rect,NX_WIDTH(&frameRect)/2.,2);

	[leftView setFrame:&left_Rect];
	[leftView initialize];
	[rightView setFrame:&right_Rect];
	[rightView initialize];
	[self setDrawOrigin:(NXCoord) NX_X(&frameRect):(NXCoord) NX_Y(&frameRect)];
	[self S_Reset:self];
	return self;
}

- windowDidResize:sender
{
	/*	This is not received since the controller handles it */
	printf("windowDidResize for StereoView\n");
	return self;
}

- windowWillResize:sender toSize:(NXSize *)aSize
{
	/*	This is not received since the controller handles it */
	printf("windowWillResize for StereoView\n");
	return self;
}

- openData:sender
{
  const char *const ext[5] = {"dat", "data", "3d", "S3d", NULL};

    if (textNeedsSaving)
		[self saveInRequest:sender];
  if([openReq runModalForTypes:ext] && (fileNAME = (char *)[openReq filename]))	{
    [self readSData:fileNAME];
	}
  else
    [self showError:"No file chosen or could not open file"];
  return self;
}

// saveRequest: saves the current window under its default name (found in
// the title bar). Note that if the title bar is empty or the default title
// is "Untitled" then saveRequest: will put up a save panel, giving the user
// a chance to specify a real title.

- saveRequest:sender
{
    const char *fileName;
    const char *const types[2] = {"S3d",
    				  NULL};
    
    id curWin = [self window];
    if (textNeedsSaving)
		[self saveInRequest:sender];
	[saveReq setRequiredFileType:types[0]];
    
    if (curWin == nil) 
	[self showError:"errorOnSaveRequest"];
    else {
	// Check to see if the current window is titled and the title is not
	// "Untitled". If so, save the file, else put up a save panel...
	fileName = [curWin title];
	if (strcmp (fileName, "Untitled"))
	    [self saveText:curWin inPath:fileName];
	else [self saveInRequest:sender];
    }
    textNeedsSaving = 0;
	return self;
}

// saveInRequest: gives the user a chance to save the current window
// under a new name. 

- saveInRequest:sender
{
    const char *fileName;
    const char *const types[2] = {"S3d",
    				  NULL};
    id		curWin;
    
    curWin = [self window];
    [saveReq setRequiredFileType:types[0]];

    if (curWin == nil) 
	[self showError:"errorOnSaveIn"];
    else {
	// Get a file name from the user; use title of the window as default.
	if (([saveReq runModalForDirectory:"."
						      file:[curWin title]]) &&
	      (fileName = [saveReq filename])) 
	    [self saveText:curWin inPath:fileName];
    }
    textNeedsSaving = 0;
    return self;
}

// saveWindow writes a window out the archive file whose name is specified
// by the second argument. The title of the current window is also set 
// accordingly. 

- saveText:(id)win inPath:(const char *)name
{	
	FILE *output;
	char thisline[MAXLINE];
	NXStream	*stream;
	BOOL s_gets();
	
  	if((output = fopen(name, "w")) == NULL) {
    sprintf(thisline, "Unable to open file %s", name);
    [self showError:thisline];
    return NULL;
  }
    
	if (delegate && [delegate respondsTo:@selector(stereoView:providePoints:)])
		[delegate stereoView:self providePoints:&stream];

  	s_gets(thisline, MAXLINE, stream);
  	while (!NXAtEOS(stream)) {
		fprintf(output,"%s",thisline);
  		s_gets(thisline, MAXLINE, stream);
		}
	fclose(output);
    [win setTitle:name];
    return self;
}

// closeRequest closes the current window by simulating a click on the
// closebutton. A check should probably be added to give the user the 
// option of saving the window before closing

- closeRequest:sender
{
     if (textNeedsSaving)
		[self saveInRequest:sender];
	if (delegate && [delegate respondsTo:@selector(stereoView:clearText:)])
		[delegate stereoView:self clearText:fileNAME];
   strcpy(fileNAME,"Untitled");
   [self clear:self];
   [self S_Reset:self];
    textNeedsSaving = 0;
   return self;
}

- readSData:(char *)fileName
{

	if (!strcmp(fileName,""))	{
/*	[self set_Theta:self];
	[self set_Phi:self];
	[self set_dist:self];
		strcpy(fileNAME,"Untitled");
		[[self window] setTitle:fileName];
	[self display];
*/
		[self clear:self];
	} else {
	if ([self readFile:fileName] != NULL) {
		[self clear:self];
		[[self window] setTitle:fileName];
		[self textChanged:self];
	    [self plot:self];
		}
	}
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	
	NXDrawWhiteBezel(rects,rects);
	return self;
}

- readFile:(char *)fileName
{
	FILE *input;
	char thisline[MAXLINE], *cpt, *trimmer;
	
  	if((input = fopen(fileName, "r")) == NULL) {
    sprintf(thisline, "Unable to open file %s", fileName);
    [self showError:thisline];
    return NULL;
  }

	if (delegate && [delegate respondsTo:@selector(stereoView:clearText:)])
					[delegate stereoView:self clearText:fileName];
  fgets(thisline, MAXLINE, input);
  while (!feof(input)) {
	trimmer = cpt = &thisline[0];
	while(*trimmer++ != '\n');	trimmer--;
	while(*trimmer-- == ' ');	trimmer++;
	if (*trimmer != '\n') *trimmer++ = '\n'; *trimmer = '\0'; 
	if (delegate && [delegate respondsTo:@selector(stereoView:pointDidChange:)])
					[delegate stereoView:self pointDidChange:cpt];
  fgets(thisline, MAXLINE, input);
  }
	fclose(input);
	return self;
}

BOOL	s_gets(thisline, max, stream)
	char thisline[];
	int max;
	NXStream *stream;
{
	int i;
	
	i = 0;
	while (!NXAtEOS(stream))	{
		thisline[i++] = NXGetc(stream);
		if (i > max)	{
		 	return FALSE;
		 }
		if (thisline[i-1] == '\n') {
			thisline[i] = '\0';
			return TRUE;
			}
	}
	return(FALSE);
}

- (BOOL) readData:(NXStream *)stream
{
	char line[128], thisline[128], *aPtr, *word();
	char cmd;
	int nwords,i, words(), tolower(), isalpha();
	float x[3], *fpt, argv[6];
	long pts_read;
		
	scratch = (float *) calloc(MAXNUM, sizeof(float));
				/* release existing data */
	if ([dataList count] != 0) [dataList freeObjects];
		
	pts_read = 0;
	cmd = 'p';
	argv[0] = 0.;
	argv[1] = NX_BLACK;
	nwords = 0;
	fpt = scratch;
	pts_read = 0;
	
	while(s_gets(line,128,stream))	{
		if(lastch(line) == '\n') lastch(line) = '\0';
		if(isalpha(line[0]))	{
			if (pts_read != 0)	{
				[self store_pts:cmd:argv];
				[dataList addObject:myStorage];
				}
			cmd = tolower(line[0]);
			nwords = min(words(line),5);
			for(i=1;i<6;i++)	argv[i] = 0;
			for(i=1;i<=nwords;i++)	{
				if((aPtr = word(i,line)) != NULL)	{
					sscanf(aPtr,"%f",&argv[i]);
					}
			}
			switch (cmd)	{
				case 'n':
					break;
				case 'l':
					argv[1] = (nwords < 1)?0.:argv[1];	/* width */
					argv[2] = (nwords < 2)?NX_BLACK:argv[2];	/* shade */
	// Add rgb here and in p and implement in Cubeview.m and psfcns (dim argv)
					for (i=3;i<6;i++)
						argv[i] = (nwords < i)?0.:argv[i];	/* color */
					break;
				case 'p':
					argv[1] = (nwords < 1 )?0.:argv[1];	/* radius */
					argv[2] = (nwords < 2)?NX_BLACK:argv[2]; /* shade */
					for (i=3;i<6;i++)
						argv[i] = (nwords < i)?0.:argv[i];	/* color */
					break;
				case 'b':
					break;
				default:
					sprintf(thisline, "Unknown Data Type");
					[self showError:thisline];
					return NO;
					break;
			}
			fpt = scratch;
			pts_read = 0;
			nlines = 0;
			continue;
		}
		sscanf(line,"%f %f %f",&x[0], &x[1], &x[2]);
		nlines++;
		if (pts_read + 3 > MAXNUM)	{
    		sprintf(thisline, "Number of points exceeds %d\n", MAXNUM);
    		[self showError:thisline];
			free(scratch);
			return NO;
		}
		for(i=0;i<3;i++)	{
			*fpt++ = x[i];
			}
		pts_read += 3;
	}
			if (pts_read != 0)	{
				[self store_pts:cmd:argv];
				[dataList addObject:myStorage];
				}
//	[self debug];
	free(scratch);
//	[self display];
	return YES;
}

- debug
/*	Debuggin output
*/
{

		int i, j, k, nitems, type, ntypes;
		float *xx, *gpt, **displayed;
//		char thisline[128];
		

	ntypes = [dataList count];

	printf("There are %d types in dataList\n",ntypes);

	for(j=0;j<ntypes;j++)	{
	myStorage = [dataList objectAt:j];
	nitems = [myStorage howMany];
	printf("Type # %d has %d points\n",j,nitems);
	xx = [myStorage args];
	type = [myStorage dataType];
	gpt = [myStorage array];
	printf("Type = %d, argv is %10.6f, %10.6f, %10.6f\n",
		 	type,xx[1],xx[2],xx[3]);
	for(i=0;i<nitems/3;i++) {
		printf("%d) ",i);
		for (k=0;k<3;k++)
			printf(" %10.6f",gpt[i+k*nitems/3]);
		printf("\n");
		}
	displayed = [myStorage displayed];
	printf(" Show same data via displayed array\n");
	for(i=0;i<nitems/3;i++)	{
		printf("%d) ",i);
		for (k=0;k<3;k++)
			printf(" %10.6f",displayed[k][i]);
		printf("\n");
		}
	}

	return self;
}

- (BOOL)store_pts:(char)cmd:(float *)argv

{
	int type = NEITHER;
	
/* create the datapoints structure and store the values */
    switch(cmd) {
    case 'n':
      type = NEITHER;
      break;
    case 'l':
      type = LINES;
      break;
    case 'p':
      type = POINTS;
/*
 *		Store radius, shade, label here in thesedata->radius, etc.
*/
      break;
    case 'b':
      type = BOTH;
      break;
    }
    myStorage = [[StoreData alloc] init:type:3*nlines:scratch:argv];
	return YES;	  
}
			
int words(str)
	char *str;
{
	int isalnum(), count=0;
	while(*str != '\0')	{
		while(ok_char(*str)) str++; /* skip characters */
		while(whitespace(*str)) str++; /* skip whitespace */
		if(ok_char(*str))	count++;
	}
	return count;
}

char *word(i,str)
	int i;
	char *str;
{
	int isalnum(), count=0;
	
	if(i > words(str)) return NULL;
	while(*str != '\0')	{
		while(ok_char(*str)) str++;	/* skip characters */
		while(whitespace(*str)) str++; /* skip whitespace */
		if(ok_char(*str))
			if(++count == i)	return str;
	}
	return NULL;
}

@end
