/* ColorView.m Copyright 1994 Steve Ludtke */
/* This object is basically the entire program. Not very object oriented */
/* but it works. */

#import "ColorView.h"
#import <appkit/appkit.h>
#import <dpsclient/psops.h>
#import <dpsclient/dpsclient.h>
#import <misckit/MiscSliderCell.h>
#import <misckit/MiscSliderField.h>
#import <libc.h>
#import <math.h>

// This should point to your ghostscript executable
char *GSCOM = "/usr/local/bin/gs";

// This is the printer type to send to the GS command
char *PRINTYPE= "cdj550";

// This points to the location of the filt.sh used in your printcap entry
char *FILTFS = "/usr/local/lib/ghostscript/filt/filt.sh";

// This points to where you installed the pp0cat program
char *PP0CAT = "/usr/local/bin/pp0cat";

// This points to the parameter file
char *PARMFS = "/usr/local/lib/ghostscript/filt/hpccor.parm";

char *TMPCCOR = "/usr/local/lib/ghostscript/filt/hpccor";

extern id NXApp;

@implementation ColorView
- initFrame:(NXRect *)myrect
{
int i,r,g,b;
char s[240];

[super initFrame:myrect];
[self setDrawSize:1.3 :1.0];

/* initialize to identity matrix */	
for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			CCOR[r][g][b][0]=(float)r*32.0;
			CCOR[r][g][b][1]=(float)g*32.0;
			CCOR[r][g][b][2]=(float)b*32.0;
		}
	}
}
sel[0]=sel[1]=sel[2]=dsel=0;

bpath=(char *)[[NXBundle mainBundle] directory];

sprintf(s,"%s/Library/HPColorCorrect",getenv("HOME"));
if (access(s,0)!=0) mkdir(s,0xffff);

colorPanel=nil;

return self;
}

-appDidInit:sender
{
FILE *in;
int a,b,c,d;

in=fopen(PARMFS,"r");
if (in!=NULL) {
	fscanf(in," %d %d %d %d",&a,&b,&c,&d);
	[bitsperpix selectCellAt:a :0];
	[depletion selectCellAt:b :0];
	[shingling selectCellAt:c :0];
	[resolution selectCellAt:d :0];
	fclose(in);
}
return self;
}

-loadInfo:sender
{
[NXApp loadNibSection:"Info.nib" owner:self];
return self;
}

/* redisplay the plot, used for button attachments */
- display:sender
{
[self display];
return self;
}

/* We must rescale when the user changes the box size */
-superviewSizeChanged:(const NXSize *)oldsize
{
[super superviewSizeChanged:oldsize];
[self setDrawSize:1.3 :1.0];
[self display];
return self;
}

// does actual drawing. If dsel==1 then only display selected color
// if dsel==2 then we're printing so only use 1 color/circle
- drawSelf:(NXRect *)rects :(int)rectCount
{
int ri,gi,bi;
float r=0,g=0,b=0,x,y;

if (dsel!=1) {
	PSsetgray(1.0);
	PSmoveto(0,0);
	PSlineto(1.3,0);
	PSlineto(1.3,1.0);
	PSlineto(0,1.0);
	PSfill();
}

for (x=.05; x<1.3; x+=.1) {
	for (y=.05; y<1.0; y+=.1) {
		ri=r*8.0;
		gi=g*8.0;
		bi=b*8.0;
		if (dsel!=1||(ri==sel[0]&&gi==sel[1]&&bi==sel[2])) {

			/* screen color */
			PSsetrgbcolor(r,g,b);
			PSmoveto(x,y);
			if (dsel!=2) PSarc(x,y,.05,0,180.0);
			else PSarc(x,y,.05,0,360.0);
			PSfill();

			/* adjusted color */
			if (dsel!=2&&mapsel!=0) {
				PSsetrgbcolor(CCOR[ri][gi][bi][0]/256.0, 
					CCOR[ri][gi][bi][1]/256.0,CCOR[ri][gi][bi][2]/256.0);
				PSmoveto(x,y);
				PSarc(x,y,.05,180.0,360.0);
				PSfill();
				if (ri==sel[0]&&gi==sel[1]&&bi==sel[2]) {
					PSsetgray(0);
					PSsetlinewidth(0);
					PSarc(x,y,.05,0.0,360.0);
					PSstroke();
				}

			}
		}
		r+=.25;
		if (r>1.0) { r=0; g+=.25; }
		if (g>1.0) { g=0; b+=.25; }
		if (b>1.0) break;
	}
}

dsel=0;
return self;
}

/* Allows the user to select an area of the plot to be passed to the */
/* delegate via a zoomTo:::: message. We make use of the image we stored */
/* so we don't have to replot every time the rubberband box is moved */
-mouseDown:(NXEvent *)oevent 
{
int oldMask,fl;
NXEvent *event,evs;
char s[50];
int x,y;

if (oevent->flags&NX_ALTERNATEMASK||oevent->flags&NX_COMMANDMASK) fl=1;
else fl=0;

evs=*oevent;
oevent=&evs;
[self convertPoint:&oevent->location fromView:nil];

x=floor(evs.location.x/.1);
y=floor(evs.location.y/.1);

if (x==12&&y>4) x=y=0;

x=x*10+y;

sel[0]=(x%5)*2;
sel[1]=((x/5)%5)*2;
sel[2]=((x/25)%5)*2;
//printf("select :%d , %d %d %d\n",x,sel[0],sel[1],sel[2]);
[self display];
[self updSel];
return self;
}

-useParms:sender
{
FILE *out;

/* write parms to file so we know what they are next time we are run */
out=fopen(PARMFS,"w");
if (out==NULL) { printf("Can't open PARMFS\n"); return self; }
chmod(PARMFS,0666);
fprintf(out,"%d %d %d %d\n",[bitsperpix selectedRow],[depletion selectedRow], [shingling selectedRow],[resolution selectedRow]);
fclose(out);

/* write filt.sh file */
out=fopen(FILTFS,"w");
if (out==NULL) { printf("Can't open filt.sh\n"); return self; }
chmod(FILTFS,0777);
fprintf(out,"#!/bin/sh\n%s -q -dNOPAUSE -sDEVICE=%s ",GSCOM,PRINTYPE);

switch ([bitsperpix selectedRow]) {
case 0: fprintf(out,"-dBitsPerPixel=1 "); break;
case 1: fprintf(out,"-dBitsPerPixel=3 "); break;
case 2: fprintf(out,"-dBitsPerPixel=8 "); break;
case 3: fprintf(out,"-dBitsPerPixel=16 "); break;
case 4: fprintf(out,"-dBitsPerPixel=24 "); break;
case 5: fprintf(out,"-dBitsPerPixel=32 "); break;
}

fprintf(out,"-dDepletion=%d ",[depletion selectedRow]);
fprintf(out,"-dShingling=%d ",[shingling selectedRow]);

switch ([resolution selectedRow]) {
case 0: fprintf(out,"-r75 "); break;
case 1: fprintf(out,"-r150 "); break;
case 2: fprintf(out,"-r300 "); break;
}

fprintf(out,"-sOutputFile=\\|%s -\n",PP0CAT);
fclose(out);
return self;
}

-modColor:sender
{
[colwin makeKeyAndOrderFront:self];
return self;
}

/* change selected color */
-set1Color:sender
{
NXColor set;

set=[sender color];
NXConvertColorToRGB(set,&CCOR[sel[0]][sel[1]][sel[2]][0],&CCOR[sel[0]][sel[1]][sel[2]][1],&CCOR[sel[0]][sel[1]][sel[2]][2]);
CCOR[sel[0]][sel[1]][sel[2]][0]*=255.0;
CCOR[sel[0]][sel[1]][sel[2]][1]*=255.0;
CCOR[sel[0]][sel[1]][sel[2]][2]*=255.0;
dsel=1;
[self display];
return self;
}

/* reset selected color to default */
-resetColor:sender
{
CCOR[sel[0]][sel[1]][sel[2]][0]=(float)sel[0]*32.0;
CCOR[sel[0]][sel[1]][sel[2]][1]=(float)sel[1]*32.0;
CCOR[sel[0]][sel[1]][sel[2]][2]=(float)sel[2]*32.0;

[self updSel];
dsel=1;
[self display];
return self;
}

/* reset entire matrix to default */
-resetAll:sender
{
int r,g,b;

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			CCOR[r][g][b][0]=(float)r*32.0;
			CCOR[r][g][b][1]=(float)g*32.0;
			CCOR[r][g][b][2]=(float)b*32.0;
		}
	}
}

[self updSel];
[self display];
return self;
}

/* fills in "odd" colors */
-interp
{
int r,g,b,a,i,rr,gg,bb;
float val,d;
float tmp[9][9][9][3];
int ary[7][3] = { 0,0,0 ,1,0,0 ,0,1,0 ,0,0,1 ,-1,0,0 ,0,-1,0 ,0,0,-1 };

bcopy(CCOR,tmp,8748);

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			if (r%2==0&&g%2==0&&b%2==0) continue;
			for (i=0; i<3; i++) {
				d=0;
				val=0;
				for (a=1; a<7; a++) {
					rr=r+ary[a][0];
					gg=g+ary[a][1];
					bb=b+ary[a][2];
					if (rr<0||rr>8||gg<0||gg>8||bb<0||bb>8) continue;
					val+=tmp[rr][gg][bb][i];
					d+=1.0;
				}
				CCOR[r][g][b][i]=val/d;
			}
		}
	}
}

return self;
}

/* smooths the correction matrix */
-smooth:sender
{
int r,g,b,a,i,rr,gg,bb;
float val,d;
float tmp[9][9][9][3];
int ary[7][3] = { 0,0,0 ,1,0,0 ,0,1,0 ,0,0,1 ,-1,0,0 ,0,-1,0 ,0,0,-1 };

[self interp];

bcopy(CCOR,tmp,8748);

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			for (i=0; i<3; i++) {
				d=10.0;		/* weight of center color */
				val=10.0*tmp[r][g][b][i];
				for (a=1; a<7; a++) {
					rr=r+ary[a][0];
					gg=g+ary[a][1];
					bb=b+ary[a][2];
					if (rr<0||rr>8||gg<0||gg>8||bb<0||bb>8) continue;
					val+=tmp[rr][gg][bb][i];
					d+=1.0;
				}
				CCOR[r][g][b][i]=val/d;
			}
		}
	}
}

[self display];
[self updSel];
return self;
}

/* save current color map to /tmp/hpccor */
-use:sender
{
FILE *out;

[self interp];
out=fopen(TMPCCOR,"w");
chmod(TMPCCOR,0666);
if (mapsel==0) CCOR[0][0][0][0]=-1.0;
fwrite(CCOR,sizeof(float),2187,out);
fclose(out);

return self;
}

-default:sender
{
FILE *in;
char *fsp;

fsp=[self colFileSpec:2];
if (fsp==NULL) { [self resetAll:self]; return self; }

in=fopen(fsp,"r");
if (in==NULL) return self;
fread(CCOR,sizeof(float),2187,in);
fclose(in);

[self display];
[self updSel];
return self;
}

-revert:sender
{
FILE *in;
char *fsp;

fsp=[self colFileSpec:0];
if (fsp==NULL) { [self resetAll:self]; return self; }

in=fopen(fsp,"r");
if (in==NULL) return self;
fread(CCOR,sizeof(float),2187,in);
fclose(in);

[self display];
[self updSel];
return self;
}

-newMap:sender
{
FILE *in;
char *fsp;

mapsel=[sender selectedRow];
fsp=[self colFileSpec:0];
if (fsp==NULL) { [self resetAll:self]; return self; }

in=fopen(fsp,"r");
if (in==NULL) return self;
fread(CCOR,sizeof(float),2187,in);
fclose(in);

[self display];
[self updSel];
return self;
}

-saveMap:sender
{
FILE *out;
char *fsp;

[self use:self];

fsp=[self colFileSpec:1];
if (fsp==NULL) return self;

out=fopen(fsp,"w");
if (out==NULL) return self;
fwrite(CCOR,sizeof(float),2187,out);
fclose(out);

return self;
}

-(char *)colFileSpec:(char)flag
{
static char s[240];

if (mapsel==0) 	return (NULL);

if (flag!=2) {
sprintf(s,"%s/Library/HPColorCorrect/map%d.hpcmap",getenv("HOME"),mapsel);
if (access(s,0)==0||flag==1) return (s); 	// users customized version
}

sprintf(s,"%s/map%d.hpcmap",bpath,mapsel);
if (access(s,0)==0) return (s); 		// the file in the app

return (NULL);  //this should never happen
}


/* make entire map darker */
-darken:sender
{
int r,g,b,m;

m=[rgbmodsel selectedCol];

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			if (m==3||m==0) CCOR[r][g][b][0]-=5.0;
			if (m==3||m==1) CCOR[r][g][b][1]-=5.0;
			if (m==3||m==2) CCOR[r][g][b][2]-=5.0;
			if (CCOR[r][g][b][0]<0) CCOR[r][g][b][0]=0;
			if (CCOR[r][g][b][1]<0) CCOR[r][g][b][1]=0;
			if (CCOR[r][g][b][2]<0) CCOR[r][g][b][2]=0;
		}
	}
}

[self updSel];
[self display];
return self;
}

-lighten:sender
{
int r,g,b,m;

m=[rgbmodsel selectedCol];

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			if (m==3||m==0) CCOR[r][g][b][0]+=5.0;
			if (m==3||m==1) CCOR[r][g][b][1]+=5.0;
			if (m==3||m==2) CCOR[r][g][b][2]+=5.0;
			if (CCOR[r][g][b][0]>255.0) CCOR[r][g][b][0]=255.0;
			if (CCOR[r][g][b][1]>255.0) CCOR[r][g][b][1]=255.0;
			if (CCOR[r][g][b][2]>255.0) CCOR[r][g][b][2]=255.0;
		}
	}
}

[self updSel];
[self display];
}

/* increase contrast of entire map */
-moreContrast:sender
{
int r,g,b,m;

m=[rgbmodsel selectedCol];

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			if (m==3||m==0) CCOR[r][g][b][0]=(CCOR[r][g][b][0]-128.0)/.95+128.0;
			if (m==3||m==1) CCOR[r][g][b][1]=(CCOR[r][g][b][1]-128.0)/.95+128.0;
			if (m==3||m==2) CCOR[r][g][b][2]=(CCOR[r][g][b][2]-128.0)/.95+128.0;
			if (CCOR[r][g][b][0]<0) CCOR[r][g][b][0]=0;
			if (CCOR[r][g][b][1]<0) CCOR[r][g][b][1]=0;
			if (CCOR[r][g][b][2]<0) CCOR[r][g][b][2]=0;
			if (CCOR[r][g][b][0]>255.0) CCOR[r][g][b][0]=255.0;
			if (CCOR[r][g][b][1]>255.0) CCOR[r][g][b][1]=255.0;
			if (CCOR[r][g][b][2]>255.0) CCOR[r][g][b][2]=255.0;
		}
	}
}

[self updSel];
[self display];
}

-lessContrast:sender
{
int r,g,b,m;

m=[rgbmodsel selectedCol];

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			if (m==3||m==0) CCOR[r][g][b][0]=(CCOR[r][g][b][0]-128.0)*.95+128.0;
			if (m==3||m==1) CCOR[r][g][b][1]=(CCOR[r][g][b][1]-128.0)*.95+128.0;
			if (m==3||m==2) CCOR[r][g][b][2]=(CCOR[r][g][b][2]-128.0)*.95+128.0;
			if (CCOR[r][g][b][0]>255.0) CCOR[r][g][b][0]=255.0;
			if (CCOR[r][g][b][1]>255.0) CCOR[r][g][b][1]=255.0;
			if (CCOR[r][g][b][2]>255.0) CCOR[r][g][b][2]=255.0;
		}
	}
}

[self updSel];
[self display];
}

-gammaAdj:sender
{
int r,g,b,m;
float ga;

ga=[gamma floatValue];
m=[rgbmodsel selectedCol];

for (r=0; r<9; r++) {
	for (g=0; g<9; g++) {
		for (b=0; b<9; b++) {
			if (m==3||m==0) CCOR[r][g][b][0]=pow((CCOR[r][g][b][0]/256.0),ga)*256.0;
			if (m==3||m==1) CCOR[r][g][b][1]=pow((CCOR[r][g][b][1]/256.0),ga)*256.0;
			if (m==3||m==2) CCOR[r][g][b][2]=pow((CCOR[r][g][b][2]/256.0),ga)*256.0;
			if (CCOR[r][g][b][0]>255.0) CCOR[r][g][b][0]=255.0;
			if (CCOR[r][g][b][1]>255.0) CCOR[r][g][b][1]=255.0;
			if (CCOR[r][g][b][2]>255.0) CCOR[r][g][b][2]=255.0;
		}
	}
}

[self updSel];
[self display];
}

-(BOOL)acceptsFirstMouse
{
return (YES);
}

/* redisplay RGB values of selected cell */
-updSel
{
NXColor set;

if (colorPanel==nil) {
	[NXColorPanel setPickerMask:NX_ALLMODESMASK];
	[NXColorPanel setPickerMode:NX_HSBMODE];
	colorPanel=[NXColorPanel sharedInstance:YES];
	[colorPanel setTarget:self];
	[colorPanel setAction:@selector(set1Color:)];
	[colorPanel setShowAlpha:NO];
	[colorPanel setContinuous:YES];
	[colorPanel makeKeyAndOrderFront:self];
}

set=NXConvertRGBToColor(CCOR[sel[0]][sel[1]][sel[2]][0]/255.0,CCOR[sel[0]][sel[1]][sel[2]][1]/255.0,CCOR[sel[0]][sel[1]][sel[2]][2]/255.0);
[colorPanel setColor:set];
return self;
}

// When printing redraw must be set to 3 so an image isn't printed 
-printPSCode:sender
{
dsel=2;		/* 1 color per circle */
[super printPSCode:sender];
return self;
}

@end
