/*
 *	ImageCompositor -- Randy Nelson
 *	An general class that composites an image at the current mouse location
 *
 *	You may freely copy, distribute and reuse the code in this example.
 *	NeXT disclaims any warranty of any kind, expressed or implied, as to
 *	its fitness for any particular use.
 */


#import "ImageCompositor.h"
#import <appkit/Application.h>
#import <appkit/Window.h>
#import <appkit/PrintInfo.h>
#import <appkit/NXImage.h>
#import <appkit/NXColorPanel.h>
#import <appkit/NXColorWell.h>
#import <appkit/Button.h>
#import <appkit/Slider.h>
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <appkit/publicWraps.h>

#define SQUARE 1
#define CIRCLE 2
#define BRUSHHEIGHT 48.0
#define BRUSHWIDTH 48.0
#define BRUSHNAME "theBrush"
 
@implementation ImageCompositor


- initFrame:(const NXRect *)frameRect
{
    [super initFrame:frameRect];
    
    /* off-screen buffer */
    screenImage = [[NXImage alloc] initSize:&bounds.size];
    
    /* undo buffer */
    undoImage = [[NXImage alloc] initSize:&bounds.size];
    
    /* brush */
    currentBrushSize.height = BRUSHHEIGHT;
    currentBrushSize.width = BRUSHWIDTH;
    brushImage = [[NXImage alloc] initSize:&currentBrushSize];
    
    /* we do alot of focus pocus */
    [self allocateGState];
    
    return self;
}

- selfInit:sender
{
    NXRect aRect;
    
    /* set up for horizontal printing */
    [[[[[NXApp printInfo] setOrientation:NX_LANDSCAPE andAdjust:YES]
	    setHorizCentered:YES]
		    setVertCentered:YES]
			    setMarginLeft:0.0
			    right:0.0
			    top:0.0
			    bottom:0.0];
    
    /* set up initial brush state */
    [brushImage setName:BRUSHNAME];
    [brushSize setMinValue:1.0];
    [brushSize setMaxValue:100.0];
    [brushSize setFloatValue:25.0];
    [brushColor setColor:NX_COLORBLACK];
    [self drawBrushImage:self];
    
    /* set up window resizing limits */
    [window getFrame:&aRect];
    windowMax.width = aRect.size.width;
    windowMax.height = aRect.size.height;
    windowMin.width = windowMax.width/3;
    windowMin.height = windowMax.height/3;
    
    /* show the windows */
    [self erase:sender];
    [window makeKeyAndOrderFront:self];
    [[brushDisplay window] orderFront:self];
    [self colorPanel:self];
    return self;
}

- drawSelf:(const NXRect *)r :(int)c
{
    /* load the appropriate portion of the off-screen buffer
     * into the visible portion of the view
     */
    [screenImage composite:NX_COPY fromRect:r toPoint:&(r->origin)];
    return self;
}

- mouseDownAction:(NXPoint *)currentLocation
{	
    NXPoint zero = {0.0, 0.0};

    /* load the old off-screen buffer into the undo buffer */
    if ([undoImage lockFocus]) {
        [screenImage composite:NX_COPY toPoint:&zero];
        [undoImage unlockFocus];
    }
    
    /* center on the brush */
    currentLocation->x  -= currentBrushCenter.x;
    currentLocation->y  -= currentBrushCenter.y;

    /* draw the current brush into the view */
    [self lockFocus];
    [brushImage composite:NX_SOVER toPoint:currentLocation];
    [self unlockFocus];
    [window flushWindow];
    
    /* draw the current brush into the off-screen buffer */
    if ([screenImage lockFocus]) {
	[brushImage composite:NX_SOVER toPoint:currentLocation];
	[screenImage unlockFocus];
    }
    
    return self;
}

- mouseDraggedAction:(NXPoint *)currentLocation
{
    /* center on the brush */
    currentLocation->x  -= currentBrushCenter.x;
    currentLocation->y  -= currentBrushCenter.y;

    /* draw the current brush into the view */
    [self lockFocus];    
    [brushImage composite:NX_SOVER toPoint:currentLocation];
    [self unlockFocus];
    [window flushWindow];

    /* draw the current brush into the off-screen buffer */
    if ([screenImage lockFocus]) {
	[brushImage composite:NX_SOVER toPoint:currentLocation];
	[screenImage unlockFocus];
    }
    
    return self;
}

- mouseUpAction:(NXPoint *)currentLocation
{
    return self;
}

- drawBrushImage:sender
{
    /* read all the brush parameters from interface */
    currentBrushColor = [brushColor color];
    currentBrushShape = [brushShape selectedTag];
    currentBrushSize.height = currentBrushSize.width = [brushSize floatValue];
    currentBrushCenter.x = (currentBrushSize.width/2);
    currentBrushCenter.y = (currentBrushSize.height/2);
    
    /* draw the current brush */
    [brushImage setSize:&currentBrushSize];
    if ([brushImage lockFocus]) {
	PScompositerect(0.0, 0.0, currentBrushSize.height,
			currentBrushSize.width, NX_CLEAR);
	NXSetColor(currentBrushColor);
	switch (currentBrushShape) {
	    case SQUARE:{
		    PSrectfill(0.0, 0.0, currentBrushSize.height,
			       currentBrushSize.width);
		    break;
	    }
	    case CIRCLE:{
		    PSarc(currentBrushCenter.x, currentBrushCenter.y,
			  currentBrushCenter.x, 0.0, 360.0);
		    break;
	    }
	    default:{
		    break;
	    }
	}
	PSfill();
	[brushImage unlockFocus];
    }
    
    [brushDisplay setIcon:BRUSHNAME];
    return self;
}

- undo:sender
{
    NXPoint zero = {0.0, 0.0};

    /* load the undo buffer into the off-screen buffer and display */
    if ([screenImage lockFocus]) {
	[undoImage composite:NX_COPY toPoint:&zero];
	[screenImage unlockFocus];
    }
    
    [self display];
    return self;
}

- erase:sender
{
    /* leave the undo buffer alone */
    if ([screenImage lockFocus]) {
	NXEraseRect(&bounds);
	[screenImage unlockFocus];
    }
    
    [self display];
    return self;	
}

- (BOOL)acceptsFirstMouse
{
    return YES;
}

-appDidInit:sender
{
    [self selfInit:self];
    return self;
}

- windowWillResize:sender toSize:(NXSize *)newFrame
{
    if(newFrame->width > windowMax.width) {
	newFrame->width = windowMax.width;
    }
    else if(newFrame->width < windowMin.width){
	newFrame->width = windowMin.width;
    }
    if(newFrame->height > windowMax.height){
	newFrame->height = windowMax.height;
    }
    else if(newFrame->height < windowMin.height){
	newFrame->height = windowMin.height;
    }
    return self;
}

- infoPanel:sender
{
    if (infoPanel == nil) {
	[NXApp loadNibSection:"Info.nib" owner:self];
    }
    [infoPanel orderFront:sender];
    return self;
}

- helpPanel:sender
{
    if(helpPanel == nil){
	[NXApp loadNibSection:"Help.nib" owner:self];
    }
    [helpPanel orderFront:sender];
    return self;
}

- colorPanel:sender
{
    [[[[NXColorPanel new]
	    setContinuous:YES]
			moveTo:494.0 :54.0]
				orderFront:nil];
    return self;
}

@end
