#import "PControl.h"
#import "Plot3DView.h"
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/Application.h>
#import <appkit/NXColorWell.h>
#import <appkit/NXColorPanel.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <dpsclient/event.h>
#import <dpsclient/psops.h>
#import <appkit/color.h>
#import <appkit/OpenPanel.h>
#import <stdio.h>
#import <math.h>
#import <ctype.h>
#include <libc.h>
#import "Expression.h"
#import "DensView.h"

@implementation PControl
-init
{
[super init];
return self;
}

/* Called once by the Plot3DView to do initialization and pass pref pointer */
-startup:(SetPref *)Pref
{
	pref=Pref;
/* display initial preferences settings */
	curPref=0;
	[symsel selectCellWithTag:pref[0].sym+1];
	[colorSel setColor:pref[0].color];
	equation=[equation docView];
	[equation setText:[pref[0].expr text]];
/* make sure plot agrees with displayed min/max values */
	[d3View zoomTo:[minX floatValue] :[minY floatValue] :[maxX floatValue] :[maxY floatValue]];
return self;
}

/* display preferences for a data set */
-disPref:sender
{
curPref=[[sender selectedCell] tag];
[symsel selectCellWithTag:pref[curPref].sym+1];
[colorSel setColor:pref[curPref].color];
if (pref[curPref].fileData==NULL) [ffSel selectCellWithTag:0];
else [ffSel selectCellWithTag:1];
[equation setText:[pref[curPref].expr text]];
[d3View zoom:self];
return self;
}

/* put preferences from screen into SetPref for curPref set */
-stoPref:sender
{
char *tmp;
int i,j,k;
pref[curPref].sym=[[symsel selectedCell] tag]-1;
pref[curPref].color=[colorSel color];
tmp=malloc([equation textLength]+50);
[equation getSubstring:tmp start:0 length:[equation textLength]+1];
/* Fix equation for parsing */
k=strlen(tmp);
for (i=0; i<k; i++) {
	if (tmp[i]=='[') tmp[i]='(';
	if (tmp[i]==']') tmp[i]=')';
	if (isupper(tmp[i])) tmp[i]=tolower(tmp[i]);
	if ((tmp[i]=='+'||tmp[i]=='-')&&(isdigit(tmp[i+1])||tmp[i+1]=='.')) {
		for (j=k; j>i; j--) tmp[j+1]=tmp[j];
		tmp[i+1]=' ';
		k++;
	}
}

/* if the equation doesn't parse, set it to 0 */
if (strlen(tmp)==0 ||
	 [pref[curPref].expr parse:tmp]==0) {
	[equation setText:"0"];
	[pref[curPref].expr parse:"0"];
	}
[d3View zoom:self];
return self;
}

/* Read 3d data from a file and put it into the current data set */
/* does NOT wait for an "OK" press, like other prefs */
-readFile:sender
{
id panel;
int i,c,cc=0;
char s[80],sc[20];
FILE *in;
float x0,x1,y0,y1,f;
Point *p;

if (pref[curPref].fileData!=NULL) { free(pref[curPref].fileData); pref[curPref].
fileData=NULL; }

/* get a filespec from the user */
panel=[[OpenPanel new] allowMultipleFiles:NO];
if (![panel runModalForTypes:NULL]) {
	[ffSel selectCellWithTag:0];
	[equation setText:"0"];
	[pref[curPref].expr parse:"0"];
	return self;
}

/* if we can't open the file, go back to equation mode */
in=fopen([panel filename],"r");
if (fgets(s,80,in)==NULL) {
	[ffSel selectCellWithTag:0];
	[equation setText:"0"];
	[pref[curPref].expr parse:"0"];
        return self;
}

/* ignore leading # lines */
while (s[0]=='#') { fgets(s,80,in); cc++; }

/* accept either "x,y,z\n" or "x y z\n" files */
strcpy(sc," %f , %f , %f");
if (sscanf(s,sc,&x0,&y0,&f)!=3) {
	strcpy(sc," %f %f %f");
	if (sscanf(s,sc,&x0,&y0,&f)!=3) {
		[ffSel selectCellWithTag:0];
		[equation setText:"0"];
		[pref[curPref].expr parse:"0"];
        	return self;
	}
}

/* count the number of points in the file */
x1=x0; y1=y0;
c=1;
while (fscanf(in,sc,&f,&f,&f)==3) c++;
rewind(in);

/* 2nd pass, allocate memory and read data into array */
p=pref[curPref].fileData=malloc(sizeof(Point)*c);
pref[curPref].nfdata=c;

for (i=0; i<cc; i++) fgets(s,80,in);
for (i=0; i<c; i++) {
	fscanf(in,sc,&p[i].x,&p[i].y,&p[i].z);
	if (p[i].x>x1) x1=p[i].x;
	if (p[i].x<x0) x0=p[i].x;
	if (p[i].y>y1) y1=p[i].y;
	if (p[i].y<y0) y0=p[i].y;
}
if (pref[curPref].data!=NULL) free(pref[curPref].data);
pref[curPref].data=malloc(sizeof(Point)*pref[curPref].nfdata);
/* x0-=fabs(x0)/500.0+fabs(x1)/500.0;
x1+=fabs(x0)/500.0+fabs(x1)/500.0;
y0-=fabs(y0)/500.0+fabs(y1)/500.0;
y1+=fabs(y0)/500.0+fabs(y1)/500.0; */
[minX setFloatValue:x0];
[minY setFloatValue:y0];
[maxX setFloatValue:x1];
[maxY setFloatValue:y1];

/* equation is now used to modify data Z values, set initial formula for */
/* z(x,y,z)=z. ie - for log plots you would enter "log(z)" */
[equation setText:"z"];
[pref[curPref].expr parse:"z"];

/* update the display */
[self setMinMax:self];
[d3View zoom:self];
return self;
}

/* return a data set to equation mode, free memory used by the file */
-clearFile:sender
{
if (pref[curPref].fileData!=NULL) { free(pref[curPref].fileData); pref[curPref].fileData=NULL; }
pref[curPref].nfdata=0;

[equation setText:"0"];
[pref[curPref].expr parse:"0"];
[d3View zoom:self];
return(self);
}

/* minz and maxz can be modified before returning, but currently aren't */
-minmaxZ:(float *)minz :(float *)maxz
{
[minZ setFloatValue:*minz];
[maxZ setFloatValue:*maxz];
return self;
}

/* display new min/max values (does not inform the views of the change) */
-setMM:(float)minx :(float)maxx :(float)miny :(float)maxy
{
[minX setFloatValue:minx];
[minY setFloatValue:miny];
[maxX setFloatValue:maxx];
[maxY setFloatValue:maxy];
return self;
}

/* Tell the Plot3DView and (indirectly) the DensView about new max/min vals */
-setMinMax:sender
{
[d3View zoomTo:[minX floatValue] :[minY floatValue] :[maxX floatValue] :[maxY floatValue]];
return self;
}

/* zoom in by a fixed amount */
-zoomIn:sender
{
float x,y,x1,y1;
x1=[maxX floatValue];
x=[minX floatValue];
y1=[maxY floatValue];
y=[minY floatValue];

[maxX setFloatValue:x1-(x1-x)/4.0];
[minX setFloatValue:x+(x1-x)/4.0];
[maxY setFloatValue:y1-(y1-y)/4.0];
[minY setFloatValue:y+(y1-y)/4.0];

[self setMinMax:self];
[d3View zoom:self];
return self;
}

/* zoom out by a fixed amount */
-zoomOut:sender
{
float x,y,x1,y1;
x1=[maxX floatValue];
x=[minX floatValue];
y1=[maxY floatValue];
y=[minY floatValue];

[maxX setFloatValue:x1+(x1-x)/4.0];
[minX setFloatValue:x-(x1-x)/4.0];
[maxY setFloatValue:y1+(y1-y)/4.0];
[minY setFloatValue:y-(y1-y)/4.0];

[self setMinMax:self];
[d3View zoom:self];
return self;
}

/* sent by DensView, min/max are from 0.0 to 1.0, not real units */
-zoomTo:(float)minx :(float)miny :(float)maxx :(float)maxy
{
float x0,x1,y0,y1;

x0=[minX floatValue];
x1=[maxX floatValue];
y0=[minY floatValue];
y1=[maxY floatValue];

[minX setFloatValue:x0+(x1-x0)*minx];
[minY setFloatValue:y0+(y1-y0)*miny];
[maxX setFloatValue:x0+(x1-x0)*maxx];
[maxY setFloatValue:y0+(y1-y0)*maxy];
[self setMinMax:self];
[d3View zoom:self];
return self;
}

/* update the density plot */
-updDen
{
static float *densData=NULL;
static int ndd=0;
int k,n,i;

n=[grid intValue];
if (ndd<(n*n)) {
	if (densData!=NULL) free(densData);
	densData=malloc(n*n*sizeof(float));
	ndd=n*n;
}

if (pref[curPref].fileData==NULL) {
	for (k=0; k<pref[curPref].ndata; k++) densData[k]=pref[curPref].data[k].z;
}
else {
	for (k=0; k<(n*n); k++) densData[k]= -.5;
	for (k=0; k<pref[curPref].ndata; k++) {
		i=floor((pref[curPref].data[k].x+.5)*(float)n)+floor((pref[curPref].data[k].y+.5)*(float)n)*n;
		if (i>=0 && i<ndd) densData[i]=pref[curPref].data[k].z;
	}
}
	
if (pref[curPref].sym!= -1) [dView setData:n :n :densData :-.5 :.5];
else [dView setData:0 :0 :NULL :-.5 :.5];
return self;
}

@end
