#import "GridView.h"
#import "GraphicView.h"
#import <appkit/Application.h>
#import <appkit/Button.h>
#import <appkit/Window.h>
#import <appkit/nextstd.h>
#import <dpsclient/wraps.h>
#import <dpsclient/dpsclient.h>

@implementation GridView
/*
 * This class is the heart of the Grid Inspector modal panel.
 * It implements the draggable grid.  It also provides the external
 * interface (i.e. the runModalForGraphicView: method) to running
 * the panel.  It is a good example of a modal panel.
 * See the Interface Builder file for a better understanding of
 * the outlets and actions sent by controls in the window containing
 * the GridView.
 */

- runModalForGraphicView:view
{
    int gridSpacing;
    float gridGray;

    if (graphicView != view) {
	graphicView = view;
	gridSpacing = [view gridSpacing];
	[spacing setIntValue:(gridSpacing >= 4 ? gridSpacing : 10)];
	gridGray = [view gridGray];
	[grayField setFloatValue:gridGray];
	[graySlider setFloatValue:gridGray];
	[self display];
    }

    [NXApp runModalFor:window];
    [window orderOut:self];

    return self;
}

- drawGrid:(int)grid
{
	int numXLines, numYLines, maxLines, i, ci, oi;
	float *cArray;
	char *oArray;
	float bbox[4];
	float x, y, x0, y0, x1, y1, increment;     
	increment = grid;
	numXLines = bounds.size.width / increment;
	numYLines = bounds.size.height / increment;
	maxLines = (numXLines > numYLines) ? numXLines : numYLines;
	
	NX_ZONEMALLOC( [self zone], cArray, float, (4*maxLines) );
	if ( cArray == NULL)
	{
		fprintf(stderr,"drawGrid - memory exceeded \n");
		return self;
	}
     
	NX_ZONEMALLOC( [self zone], oArray, char, (2*maxLines) );
	if ( oArray== NULL)
	{
		fprintf(stderr,"drawGrid - memory exceeded \n");
		return self;
	}
     
	x = bounds.origin.x;
	y0 = bounds.origin.y;
	y1 = y0 + bounds.size.height;
	ci = oi = 0;
	
	/* fill bbox */
	bbox[0] = x;
	bbox[1] = y0;
	bbox[2] = x + bounds.size.width;
	bbox[3] = y1;
     
	for (i = 0; i < numXLines; i++)
	{
		cArray[ci++] = x;
		cArray[ci++] = y0;
		oArray[oi++] = dps_moveto;
     
		cArray[ci++] = x;
		cArray[ci++] = y1;
		oArray[oi++] = dps_lineto;
		
		x += increment;
	}
	DPSDoUserPath (cArray, ci, dps_float, oArray, oi, bbox, dps_ustroke); 

	y = bounds.origin.y;
	x0 = bounds.origin.x;
	x1 = x0 + bounds.size.width;
	ci = oi = 0;
	
	/* fill bbox */
	bbox[0] = x0;
	bbox[1] = y;
	bbox[2] = x1;
	bbox[3] = y + bounds.size.height;
     
	for (i = 0; i < numYLines; i++)
	{
		cArray[ci++] = x0;
		cArray[ci++] = y;
		oArray[oi++] = dps_moveto;
     
		cArray[ci++] = x1;
		cArray[ci++] = y;
		oArray[oi++] = dps_lineto;
		
		y += increment;
	}
	DPSDoUserPath (cArray, ci, dps_float, oArray, oi, bbox, dps_ustroke); 

	NX_FREE(cArray);
	NX_FREE(oArray);
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
    int grid;
    float gray;

    grid = [spacing intValue];
    grid = MAX(grid, 0.0);
    PSsetgray(NX_WHITE);
    NXRectFillList(rects, rectCount);
    if (grid >= 4) {
	gray = [grayField floatValue];
	gray = MIN(gray, 1.0);
	gray = MAX(gray, 0.0);
	PSsetgray(gray);
	PSsetlinewidth(0.0);
	[self drawGrid:grid];
    }
    PSsetgray(NX_BLACK);
    NXFrameRect(&bounds);

    return self;
}

- mouseDown:(NXEvent *)event
{
    NXPoint p, start;
    int grid, gridCount;

    start = event->location;
    [self convertPoint:&start fromView:nil];
    grid = MAX([spacing intValue], 1.0);
    gridCount = (int)MAX(start.x, start.y) / grid;
    gridCount = MAX(gridCount, 1.0);

    [window addToEventMask:NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK];

    event = [NXApp getNextEvent:NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK];
    while (event->type != NX_MOUSEUP) {
	p = event->location;
	[self convertPoint:&p fromView:nil];
	grid = (int)MAX(p.x, p.y) / gridCount;
	grid = MAX(grid, 1.0);
	if (grid != [spacing intValue]) {
	    [form abortEditing];
	    [spacing setIntValue:grid];
	    [self display];
	}
	event = [NXApp getNextEvent:NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK];
    }

    return self;
}

/* Target/Action methods */

- show:sender
{
    [NXApp stopModal];
    [graphicView setGridSpacing:[spacing intValue]
	andGray:[grayField floatValue]];
    [graphicView setGridVisible:YES];
    return self; 
}

- off:sender
{
    [NXApp stopModal];
    [graphicView setGridSpacing:1];
    return self;
}

- cancel:sender
{
    [NXApp stopModal];
    return self;
}

- changeSpacing:sender
{
    return [self display];
}

- changeGray:sender
{
    if (sender == graySlider) {
	[form abortEditing];
	[grayField setFloatValue:[sender floatValue]];
    } else {
	[graySlider setFloatValue:[sender floatValue]];
    }
    return [self display];
}

/* NIB outlet setting methods */

- setSpacing:anObject
{
    spacing = anObject;
    return self;
}

- setGrayField:anObject
{
    grayField = anObject;
    return self;
}

- setGraySlider:anObject
{
    graySlider = anObject;
    return self;
}

- setAppIconButton:anObject
{
    [anObject setIcon:"appIcon"];
    return self;
}

- setForm:anObject
{
    form = anObject;
    return self;
}

@end
