/* Generated by Interface Builder */

#import "chaosView.h"
#import <dpsclient/wraps.h>
#import <stdlib.h>
#import <stdio.h>
#import <appkit/nextstd.h>	// For MIN MAX
#import <appkit/Application.h>
#import <appkit/color.h>

@implementation chaosView

- gohome: (float *)newX: (float *)newY: (float *)newDX: (float *)newDY:(int *)newDepth
{
    *newX = 0.0;
    *newY = 0.0;
    *newDX = 4;
    *newDY = 4;
    *newDepth = 150;
    return self;
}

- (int)colorindex:(double)Real :(double)Imaginary
{
    register double realCoord = Real, imaginaryCoord = Imaginary, tempdouble;
    register int i;

    //printf("\nReal %lf Imaginary %lf depth %d\n", Real, Imaginary, *Depth);
    for (i = 0; i < Depth; i++)
    {
	if ((Real >= 2) || (Imaginary >= 2) || (Real <= -2) || (Imaginary <= -2))
	{
	    return ((int)(chaosColors) * i / Depth);
	}
	tempdouble = Real * Real - Imaginary * Imaginary + realCoord;
	Imaginary = 2 * Real * Imaginary + imaginaryCoord;
	Real = tempdouble;
    }
    return (chaosColors);
}

- initView:(double) maxsize
{
    NXRect   myRect;

    [self lockFocus];
    NXSetRect(&myRect, 0, 0, maxsize, maxsize);
printf("ms %f", maxsize);
    NXSetColor(mycolorMap[[self colorindex:(myRect.origin.x - bounds.size.width / 2) * xFactor + myCenterX:(myRect.origin.y - bounds.size.height / 2) * yFactor + myCenterY]]);
    NXRectFill(&myRect);
    [self display];
    [self unlockFocus];
    return self;
}

- startM:(float)XCenter :(float)YCenter :(float)XScale :(float)YScale :(int)ThisDepth;
{
    double   bigsize = MAX(bounds.size.width, bounds.size.height);

    myCenterX = XCenter;
    myCenterY = YCenter;
    myWidth = XScale;
    myHeight = XScale * bounds.size.height / bounds.size.width;
    Depth = MAX(ThisDepth, 1);

    twofactor = 1.0;
    while (twofactor < bigsize)
	twofactor *= 2.0;
    currentRow = 0.0;
    currentCol = 0.0;
    xsize = twofactor / 2.0;
    xstep = twofactor;
    ysize = twofactor / 2.0;
    ystep = twofactor;
    xFactor = myWidth / bounds.size.width;
    yFactor = xFactor;
    [self initView:bigsize];

    return self;
}

- initFrame:(const NXRect *)frameRect
{
    [super initFrame:frameRect];
    return self;
}

- fillbox:(NXRect)myRect
{
    float    boxsize, *thisptr, endpt;

    if (myRect.size.width < myRect.size.height)
    {
	boxsize = myRect.size.width;
	thisptr = &myRect.origin.y;
	endpt = *thisptr + myRect.size.height;
    }
    else
    {
	boxsize = myRect.size.height;
	thisptr = &myRect.origin.x;
	endpt = *thisptr + myRect.size.width;
    }
    [self lockFocus];
    for (*thisptr = *thisptr; *thisptr <= endpt; *thisptr += boxsize)
    {
	NXSetColor(mycolorMap[[self colorindex:(myRect.origin.x - bounds.size.width / 2) * xFactor + myCenterX:(myRect.origin.y - bounds.size.height / 2) * yFactor + myCenterY]]);
	NXRectFill(&myRect);
    }
    [self display];
    [self unlockFocus];
    return self;
}

- drawline
{
    NXRect   myRect;

#ifdef DEBUG
    printf("drawlines1\n");
#endif
    [self lockFocus];
#ifdef DEBUG
    printf("drawlines2\n");
#endif
    for (currentCol = 0; currentCol < bounds.size.width; currentCol += xstep)
    {
#ifdef DEBUG
	printf("drawlines3");
#endif
	//printf("%lf %lf %lf\n", x->coord, y->coord, y->size);
	NXSetRect(&myRect, currentCol + xsize, currentRow, xsize, ysize);
#ifdef DEBUG
	printf(".1");
#endif
	NXSetColor(mycolorMap[[self colorindex:(currentCol + xsize - bounds.size.width / 2) * xFactor + myCenterX:(currentRow - bounds.size.height / 2) * yFactor + myCenterY]]);
#ifdef DEBUG
	printf(".2");
#endif
	NXRectFill(&myRect);
#ifdef DEBUG
	printf(".3\n");
#endif
	NXSetRect(&myRect, currentCol + xsize, currentRow + ysize, xsize, ysize);
	NXSetColor(mycolorMap[[self colorindex:(currentCol + xsize - bounds.size.width / 2) * xFactor + myCenterX:(currentRow + ysize - bounds.size.height / 2) * yFactor + myCenterY]]);
	NXRectFill(&myRect);

	NXSetRect(&myRect, currentCol, currentRow + ysize, xsize, ysize);
	NXSetColor(mycolorMap[[self colorindex:(currentCol - bounds.size.width / 2) * xFactor + myCenterX:(currentRow + ysize - bounds.size.height / 2) * yFactor + myCenterY]]);
	NXRectFill(&myRect);
	[self display];
    }
    if (currentRow + ystep >= bounds.size.height)
    {
	currentRow = 0.0;
	twofactor /= 2.0;
	if (xsize <= 1.0)
	{
	    [CVchaosControl runstop:self];
	    //DPSRemoveTimedEntry(windowEvent);
	    //windowEvent = 0;
	}
	xsize = twofactor / 2.0;
	xstep = twofactor;
	ysize = twofactor / 2.0;
	ystep = twofactor;
    }
    else
	currentRow += ystep;
    [self unlockFocus];
    return self;
}

- examinSelected:(float)realCenter :
    (float)imagineCenter :
    (float)realScale :
    (float)imagineScale :
    (int)newDepth
{
    NXRect   myRect;
    float    RealCenter = (realCenter - myCenterX) / xFactor + bounds.size.width / 2;
    float    ImagineCenter = (imagineCenter - myCenterY) / yFactor + bounds.size.height / 2;
    float    RealScale = realScale / xFactor;
    float    ImagineScale = imagineScale / yFactor;
    NXCoord  bottomrow = ImagineCenter - ImagineScale / 2.0, leftcol = RealCenter - RealScale / 2.0, toprow = bottomrow + ImagineScale, rightcol = leftcol + RealScale;
    NXCoord *x, *y;

    printf("RC %f IC %f RS %f IS %f\n", RealCenter, ImagineCenter, RealScale, ImagineScale);
    printf("br %f lc %f\n", bottomrow, leftcol);
    //scanf("%*s");
    NXSetRect(&myRect, leftcol, bottomrow, (NXCoord)1.0, (NXCoord)1.0);
    x = &myRect.origin.x;
    y = &myRect.origin.y;
    [self lockFocus];
    for (*y = *y; *y <= toprow; *y += 1.0)
    {
	*x = leftcol;
	for (*x = *x; *x <= rightcol; *x += 1.0)
	{
#ifdef DEBUG
	    printf("x %f y %f\n", *x, *y);
#endif
	    NXSetColor(mycolorMap[[self colorindex:(myRect.origin.x - bounds.size.width / 2) * xFactor + myCenterX:(myRect.origin.y - bounds.size.height / 2) * yFactor + myCenterY]]);
	    NXRectFill(&myRect);
	}
    }
    [self display];
    [self unlockFocus];
    return self;
}

- scrollup
{
    const NXRect *thisRect = &bounds;
    NXRect   fillRect;
    const NXPoint thisPoint = {0, -ysize};

    [self lockFocus];
    [self scrollRect:thisRect by:&thisPoint];
    currentRow -= ysize;
    myCenterY += ysize * yFactor;
    NXSetRect(&fillRect, thisRect->origin.x, thisRect->size.height - ysize, thisRect->size.width, ysize);
    [self fillbox:fillRect];
    [self unlockFocus];
    return self;
}

- scrollright
{
    const NXRect *thisRect = &bounds;
    NXRect   fillRect;
    const NXPoint thisPoint = {-xsize, -0};

    [self lockFocus];
    [self scrollRect:thisRect by:&thisPoint];
    currentCol -= xsize;
    myCenterX += xsize * xFactor;
    NXSetRect(&fillRect, thisRect->size.width - xsize, thisRect->origin.y, xsize, thisRect->size.height);
    [self fillbox:fillRect];
    [self unlockFocus];
    return self;
}

- scrolldown
{
    const NXRect *thisRect = &bounds;
    NXRect   fillRect;
    const NXPoint thisPoint = {0, ysize};

    [self lockFocus];
    [self scrollRect:thisRect by:&thisPoint];
    currentRow += ysize;
    myCenterY -= ysize * yFactor;
    NXSetRect(&fillRect, thisRect->origin.x, thisRect->origin.y, thisRect->size.width, ysize);
    [self fillbox:fillRect];
    [self unlockFocus];
    return self;
}

- scrollleft
{
    const NXRect *thisRect = &bounds;
    NXRect   fillRect;
    const NXPoint thisPoint = {xsize, 0};

    [self lockFocus];
    [self scrollRect:thisRect by:&thisPoint];
    currentCol += xsize;
    myCenterX -= xsize * xFactor;
    NXSetRect(&fillRect, thisRect->origin.x, thisRect->size.height - ysize, thisRect->size.width, ysize);
    [self fillbox:fillRect];
    NXSetRect(&fillRect, thisRect->origin.x, thisRect->origin.y, xsize, thisRect->size.height);
    [self fillbox:fillRect];
    [self unlockFocus];
    return self;
}

- mouseDown:(NXEvent *)e
/*
 * We implement the mouseDown method so the user can sweep out a section of
 * the view and select that as the current x,y,scale coordinates.
 */
{
    int      looping = YES;
    int      oldMask;
    NXRect   bbox;
    NXPoint  startPoint, currPoint;
    float    newX, newY, newDX, newDY;
    NXCoord  aspectRatio;

    aspectRatio = bounds.size.height / bounds.size.width;
    //chaosWidth = bounds.size.width;
    //chaosHeight = bounds.size.height;

    oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
    startPoint = e->location;
    [self convertPoint:&startPoint fromView:nil];
    NXSetRect(&bbox, startPoint.x, startPoint.y, 0.0, 0.0);
    [self lockFocus];
    while (looping)
    {
	e = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
	currPoint = e->location;
	[self convertPoint:&currPoint fromView:nil];
	bbox.size.width = 2 * (currPoint.x - startPoint.x);
	bbox.size.height = 2 * (currPoint.y - startPoint.y);
    /* Normalize bbox to always have positive width and height */
	if (bbox.size.width < 0)
	    bbox.size.width = -bbox.size.width;
	if (bbox.size.height < 0)
	    bbox.size.height = -bbox.size.height;
    /*
     * constrain the box to have the aspect ratio of the view.  Choose
     * whichever dimension is closer to the desired result. 
     */
	if ((bbox.size.height / bbox.size.width) > aspectRatio)
	{
	    bbox.size.height = bbox.size.width * aspectRatio;
	}
	else
	{
	    bbox.size.width = bbox.size.height / aspectRatio;
	}
    /* The startPoint is always at the center of the bbox */
	bbox.origin.x = startPoint.x -.5 * bbox.size.width;
	bbox.origin.y = startPoint.y -.5 * bbox.size.height;
	PSnewinstance();
	if (looping = (e->type == NX_MOUSEDRAGGED))
	{
	    PSsetinstance(YES);
	    PSsetgray(NX_WHITE);
	    NXFrameRect(&bbox);
	    PSsetinstance(NO);
	}
    }
    [self unlockFocus];
    [window setEventMask:oldMask];
    if ((bbox.size.width > 0) && (bbox.size.height > 0))
    {
    /*
     * At this point, bbox is in window coordinates.  Set new controller
     * parameters based on this view's coordinates and the bounding box. 
     */
	newX = (startPoint.x - bounds.size.width / 2) * xFactor + myCenterX;
	newY = (startPoint.y - bounds.size.height / 2) * yFactor + myCenterY;
	newDX = bbox.size.width * xFactor;
	newDY = bbox.size.height * yFactor;
	//using myWidth / bounds.size.width was more accurate(too accurate at small widths ?)

    /*
     * Note that we only update the text fields in the mandelControl -- we
     * don't update the internal instance variables.  If we were to update
     * the internal ivs, then the user would only get one chance with the
     * mouse down method because subsequent mouse-downs would work in terms
     * of the new coordinate system. 
     */
	  [CVchaosControl setParameters : newX :newY :newDX :newDY];
    }
    return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height // boy, this looks obsolete !
{
    [super sizeTo:width :height];
    return self;
}

- setColors:(NXColor *) colorMap :(int)colors :(NXColor) setColor;
{
    int      i;

    printf("%d colors\n", colors);
    if (mycolorMap)
	free(mycolorMap);
    mycolorMap = (NXColor *) malloc(sizeof(NXColor) * (colors + 1));
    chaosColors = colors;
    for (i = 0; i < colors; i++)
	mycolorMap[i] = colorMap[i];
    mycolorMap[colors] = setColor;
    return self;
}

- getController:sender :map
{
    CVchaosControl = sender;
    CVcolorMap = map;
    //[self startM:0.0:0.0:4 :4 :100];
    [CVcolorMap updateChaosView:self];
    return self;
}

@end // -------------------------------------------------------------------

void
mulCoords(double *Real1, double *Imagine1, double *Real2, double *Imagine2)
{
    //Returns coord1 * coord2 in coord1(for imaginary coords)
	//Aux function proves useful ! !
	double   returnReal, returnImagine;

    returnReal = ((*Real1) * (*Real2)) - ((*Imagine1) * (*Imagine2));
    returnImagine = ((*Real1) * (*Imagine2)) + ((*Real2) * (*Imagine1));

    *Real1 = returnReal;
    *Imagine1 = returnImagine;
}
