/* Quite some Code came from the Lines Demo.  
   100 Generationen Squares stroked  50 sec 
       ""           ""       filled  74 sec         
   100    ""       NXSquares filled  19 sec  */

#import <dpsclient/wraps.h>	// For PS and DPS function prototypes
#import <appkit/Control.h>	// For intValue, etc
#import <appkit/Window.h>	// We flushWindow at some point
#import <appkit/Application.h>  // For NX_BASETHRESHOLD and peek event

#import <appkit/Panel.h>
#import <appkit/NXCursor.h>
#import <appkit/Form.h>
#import <appkit/graphics.h>
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import <stdio.h>

#import "PenView.h"

#define Radius 3.0
#define Abstand 7.0   // 2*Radius +1

@implementation PenView

- open:sender
{
    id		openPanel;
    const char	*fileName;
    const char	*types[2] = {"life", NULL};
    FILE	*myfile;
    int 	*f=feld1,x,y;
	 
    openPanel = [OpenPanel new];
    [openPanel allowMultipleFiles:NO];
    
    if ([openPanel runModalForTypes:types]) {
	fileName=[openPanel filename];
	 myfile=fopen(fileName,"rb");
	 //  fread(f,4,Feld_x*Feld_y,myfile);
	for(y=1;y<=Win_y;y++){ 
	         for(x=1;x <= Win_x;x++){
			if (fgetc(myfile)==42){
			   *(f+x+Offset_x+(y+Offset_y)*Feld_x)=1;
			}
			else{
			     *(f+x+Offset_x+(y+Offset_y)*Feld_x)=0;
			}  
		}
	 fgetc(myfile);
	}
	 
	 fclose(myfile);
    }
         Redraw=1;
	 counter=0;
 	[self display];
        [Count setIntValue: counter at:0];
    return self;
}

- save:sender
{
    id		savePanel;
    const char	*fileName;
    FILE	*myfile;
    int 	*f=feld1,x,y;

    savePanel = [SavePanel new];    
    [savePanel setRequiredFileType:"life"];
    
        if ([savePanel runModal]) {
	      fileName=[savePanel filename];
	      myfile=fopen(fileName,"wb");
	//    NXRunAlertPanel(NULL, "Couldn't write file", NULL, NULL, NULL);
	//      fwrite(f,sizeof(int),Feld_x*Feld_y,myfile);
	for(y=1;y<=Win_y;y++){ 
	         for(x=1;x <= Win_x;x++){
			if (*(f+x+Offset_x+(y+Offset_y)*Feld_x)){
               		   fputc(42,myfile);
			}
			else{
			     fputc(32,myfile);
			}  
		}
	 fputc(10,myfile);
	}

	      fclose(myfile);

	}
    
    return self;
}

-showInfo:sender
{
  if(!infoPanel) {
     [NXApp loadNibSection:"info.nib" owner:self];
  }
  [infoPanel makeKeyAndOrderFront:self];
return self;
}

-initFrame:(const NXRect*)frameRect
{
   [super initFrame:frameRect];
     
     
  pencilCursor=[NXCursor newFromImage:[NXImage newFromSection:"Pipette.tiff"]];
   
   hotSpot.x=0.0;
   hotSpot.y=15.0;
   
   [pencilCursor setHotSpot:&hotSpot];
   running=NO;
   ac=1;
   return self;
}


- free
{
  /* be sure to stop the timed entry */
    if (running) {
	DPSRemoveTimedEntry(linesTimedEntry);
    }
    
    return [super free];
}

void DrawIt(DPSTimedEntry te, double timeNow, void *data)
{
  /* we set data to self so we can call this method from the timed entry */
    [(id)data animate];
}

- toggleRun:sender
{
  /* start or stop the timed entry (we're called by a two-state button) */
    if (running) {
	DPSRemoveTimedEntry(linesTimedEntry);
	running = NO;
    } else {
      /* Call the DrawIt() function as often as possible... */
	linesTimedEntry = DPSAddTimedEntry(0.0, &DrawIt, self,
					   NX_BASETHRESHOLD);
	running = YES;
    }

    return self;
}

-square: (float) x:(float) y
{  
   NXRect mr;
   mr.origin.x=x-3.0;
   mr.origin.y=y-3.0;
   mr.size.width=6.0;
   mr.size.height=6.0;
   NXRectFill(&mr);
 
   return self; 
 }

- clearView:sender
{
	int x;
	int *f=feld1,*f2=feld2;
	
	Redraw=0;
	counter=0;
    [self display];
    
    	 for(x=0;x < Feld_x*Feld_y;x++){
		*f++=0;
		*f2++=0;		
		}
//  *****  Testlinie ******
//   	 for(x=1;x < 72;x++){
//		   *(feld1+x+30*80)=1;
//     	 }       
    [Count setIntValue: counter at:0];
    return self;
}

-drawSelf:(NXRect*)rects:(int) rectCount
{
	int x,y;
	int *f=ac?feld1:feld2;

	PSsetgray(NX_WHITE);
	NXRectFill(&bounds);
	PSsetgray(NX_BLACK);
	NXFrameRect(&bounds);
	
    if(Redraw==1){
	 PSnewpath();
    
	 for(x=1;x <= Win_x;x++){
		for(y=1;y<=Win_y;y++){
			if (*(f+x+Offset_x+(y+Offset_y)*Feld_x)){
               		   [self square:(x-1) * Abstand+Radius+2:
				        (y-1) * Abstand+Radius+2];
			}  
		}
	}
     }
    [window flushWindow];   
		
	return self;
}



-resetCursorRects
{
	[self addCursorRect:&bounds cursor:pencilCursor];
	return self;
}

// A friend of mine develloped this routine on a Atari ST.
// It doesn't look nice but it is about 6 times faster than a nice aproach.
-(void) calc:(int*)f:(int*)p
{
	register int x,y,sum;
	register int *rp=f,*wp=p+Feld_x+1,*ws;
	
	for(y=1;y<=Feld_y-2;y++)
	{
	    for(x=1;x<=Feld_x-2;x++)
	    {
	    	ws=rp;
		sum=0;
		sum+=*ws++;
		sum+=*ws++;
		sum+=*ws;
		ws+=Feld_x-2;
		sum+=*ws;
		ws+=2;
		sum+=*ws;
		ws+=Feld_x-2;
		sum+=*ws++;
		sum+=*ws++;
		sum+=*ws;
		
		*wp=0;
		if(*(rp+Feld_x+1) && sum>1 && sum<4)
		{
		    *wp=1;
		}
		if(!*(rp+Feld_x+1) && sum==3)
		  *wp=1;
		wp++;
		rp++;
	    }
	    wp+=2;
	    rp+=2;
	}
	    	
}


- animate
/*
 * animate gets called over and over by the timed entry. It does the main
 * animation. Double-buffering is achieved by:
 *  -drawing the current state in WHITE in the backstore of our buffered window
 *  -computing the next state and making it the current state
 *  -drawing the current state in BLACK in the backstore of our buffered window
 *  -flushing the window...
 *
 * Note that this method gets called everytime the timed entry fires. The first
 * thing this method does is to lock the focus; the last thing it does is to
 * unlock the focus. Unfortunately, locking and unlocking the focus can be
 * expensive; thus, to avoid this problem, we sit in a tight loop animating
 * (with the focus locked) until we detect some event. Then we exit the animate
 * method, and, unless the user broke the animation, we are assured that this
 * method will be called asap by the timed entry. Another way to deal with the
 * lock/unlockFocus expense might be to allocate a graphics state with
 * allocateGState. (BreakApp example does both these tricks.)
 */
{ 
    NXEvent dummyEvent;  // For peeking at the event queue. 
    
    [self lockFocus];
    
    do {
        counter++;
	[self calc:feld1:feld2];
     	Redraw=1;
	ac=0;
	[Count setIntValue: counter at:0];
    	[self display];
	[self calc:feld2:feld1];
	Redraw=1;
	ac=1;
	[self display];
	
    } while ([NXApp peekNextEvent:NX_ALLEVENTS
		    into:&dummyEvent 
		    waitFor:0.0
		    threshold:NX_BASETHRESHOLD] == NULL);

    [self unlockFocus];

    return self;
}

-mouseDown:(NXEvent*)theEvent
{
	NXPoint currentPos;
	NXEvent *nextEvent;
	BOOL 	looping=1;
	int	oldMask;
	int	checkMask,tempx,tempy;
		
	oldMask=[window eventMask];
 
	checkMask= NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK;
	
	[window setEventMask:(oldMask| checkMask)];
	
	[self lockFocus];
	
	
	PSsetlinewidth(1.0);
	
	while(looping){
		nextEvent=[NXApp getNextEvent:checkMask];
		
		looping=(nextEvent->type != NX_MOUSEUP);

			currentPos=nextEvent->location;
			[self convertPoint:&currentPos fromView:nil];
			
			  tempx=(int)((currentPos.x-2)/Abstand);
			  tempy=(int)((currentPos.y-2)/Abstand);
			  PSnewpath();
			*(feld1+(tempy+Offset_y+1)*Feld_x+tempx+Offset_x+1)^=1;
				 Redraw=1;
				[self display];
			  	
			  [window flushWindow];
	} // end while
	
	[self unlockFocus];
	
	[window setEventMask: oldMask];
      
	return self;
}

@end
