// -*- objc -*-

#import "ImageView.h"
#import "MiscColorMerge.h"

#import "MainController.h"

@implementation MainController

- appDidInit:sender
{
    NXRect rect;

    minMandelX = -2.0;
    maxMandelX = 1.0;
    minMandelY = -1.5;
    maxMandelY = 1.5;

    minJuliaX = -2.0;
    maxJuliaX = 2.0;
    minJuliaY = -2.0;
    maxJuliaY = 2.0;

    [self displayParams];

    juliaQX = 0.5;
    juliaQY = 0.5;

    [mandelView getBounds: &rect];

    mandelWidth = rect.size.width;
    mandelHeight = rect.size.height;

    [juliaView getBounds: &rect];

    juliaWidth = rect.size.width;
    juliaHeight = rect.size.height;

    [self calcRawMandelData];
    [self calcRawJuliaData];

    [self setColorColorTable: nil];

    [mandelView setDelegate: self];
    [juliaView setDelegate: self];

    return self;
}

- setGrayColorTable: sender
{
    [colorMerge setAutodisplay: NO];
    [colorMerge removeAllColors];
    [colorMerge addColor: NX_COLORBLACK atPosition: 0.0];
    [colorMerge addColor: NX_COLORLTGRAY atPosition: 0.13];
    [colorMerge addColor: NX_COLORWHITE atPosition: 1.0];
    [colorMerge setAutodisplay: YES];

    [self calcColorTable];

    [self calcMandelImageRep];
    [self calcJuliaImageRep];

    return self;
}

- setColorColorTable: sender
{
    [colorMerge setAutodisplay: NO];
    [colorMerge removeAllColors];
    [colorMerge addColor: NX_COLORRED atPosition: 0.0];
    [colorMerge addColor: NX_COLORGREEN atPosition: 0.06];
    [colorMerge addColor: NX_COLORBLUE atPosition: 0.12];
    [colorMerge addColor: NX_COLORMAGENTA atPosition: 0.25];
    [colorMerge addColor: NX_COLORYELLOW atPosition: 0.5];
    [colorMerge addColor: NX_COLORWHITE atPosition: 1.0];
    [colorMerge setAutodisplay: YES];

    [self calcColorTable];

    [self calcMandelImageRep];
    [self calcJuliaImageRep];

    return self;
}

- changeMergeMode: sender
{
    [colorMerge setMergeMode: [[sender selectedCell] tag]];

    [self calcColorTable];

    [self calcMandelImageRep];
    [self calcJuliaImageRep];

    return self;
}

- saveImage: sender
{
    SavePanel *savePanel = [SavePanel new];
    NXBitmapImageRep *imgRep;

    if ([NXApp mainWindow] == mandelWindow)
	imgRep = (NXBitmapImageRep*)[mandelView imageRep];
    else if ([NXApp mainWindow] == juliaWindow)
	imgRep = (NXBitmapImageRep*)[juliaView imageRep];
    else
	return nil;          // this should not happen

    [savePanel setRequiredFileType: "tiff"];
    if ([savePanel runModal] == NX_OKTAG)
    {
	int fd = open([savePanel filename],
		      O_TRUNC | O_CREAT | O_WRONLY, 0644);
	NXStream *stream = NXOpenFile(fd, NX_WRITEONLY);

	if (stream == NULL)
	{
	    NXRunAlertPanel("Error", "Could not create file!",
			    "OK", NULL, NULL);

	    return self;
	}

	[imgRep writeTIFF: stream];

	NXClose(stream);
	close(fd);
    }

    return self;
}

- setMandelParams: sender
{
    minMandelX = [mandelForm doubleValueAt: 0];
    maxMandelX = [mandelForm doubleValueAt: 1];
    minMandelY = [mandelForm doubleValueAt: 2];
    maxMandelY = [mandelForm doubleValueAt: 3];

    [self calcRawMandelData];
    [self calcMandelImageRep];
    
    return self;
}

- setMandelDefaults: sender
{
    minMandelX = -2.0;
    maxMandelX = 1.0;
    minMandelY = -1.5;
    maxMandelY = 1.5;

    [self displayParams];

    [self calcRawMandelData];
    [self calcMandelImageRep];
    
    return self;
}

- setJuliaParams: sender
{
    minJuliaX = [juliaForm doubleValueAt: 0];
    maxJuliaX = [juliaForm doubleValueAt: 1];
    minJuliaY = [juliaForm doubleValueAt: 2];
    maxJuliaY = [juliaForm doubleValueAt: 3];

    [self calcRawJuliaData];
    [self calcJuliaImageRep];
    
    return self;
}

- setJuliaDefaults: sender
{
    minJuliaX = -2.0;
    maxJuliaX = 2.0;
    minJuliaY = -2.0;
    maxJuliaY = 2.0;

    [self displayParams];

    [self calcRawJuliaData];
    [self calcJuliaImageRep];
    
    return self;
}

- setJuliaQ: sender
{
    juliaQX = [juliaQForm doubleValueAt: 0];
    juliaQY = [juliaQForm doubleValueAt: 1];

    [self calcRawJuliaData];
    [self calcJuliaImageRep];
    
    return self;
}

- calcColorTable
{
    if (colorTable == 0)
	colorTable = (NXColor*)malloc(sizeof(NXColor) * MAX_ITER);

    [colorMerge calcArray: colorTable ofNumColors: MAX_ITER];

    return self;
}

- calcRawMandelData
{
    int x,
	y;
    double curX,
	curY,
	dX,
	dY;
    int *data;

    if (rawMandelData != 0)
	free(rawMandelData);

    rawMandelData = (int*)malloc(sizeof(int) * mandelWidth * mandelHeight);
    data = rawMandelData;
    
    dX = (maxMandelX - minMandelX) / (double)mandelWidth;
    dY = (maxMandelY - minMandelY) / (double)mandelHeight;

    curY = minMandelY;
    for (y = 0; y < mandelHeight; ++y)
    {
	curX = minMandelX;
	for (x = 0; x < mandelWidth; ++x)
	{
	    int num;
	    float xr,
		xi,
		xrsq,
		xisq;

	    num = 0;
	    xr = 0.0;
	    xi = 0.0;
	    do
	    {
		xrsq = xr * xr;
		xisq = xi * xi;
		xi = 2 * xr * xi + curY;  
		xr = xrsq - xisq + curX;
	    } while (((xrsq + xisq) < 4.0) && (++num < MAX_ITER - 1));
	    *data = num % MAX_ITER;

	    ++data;
	    curX += dX;
	}
	curY += dY;
    }

    return self;
}

- calcRawJuliaData
{
    int x,
	y;
    double curX,
	curY,
	dX,
	dY;
    int *data;

    if (rawJuliaData != 0)
	free(rawJuliaData);

    rawJuliaData = (int*)malloc(sizeof(int) * juliaWidth * juliaHeight);
    data = rawJuliaData;
    
    dX = (maxJuliaX - minJuliaX) / (double)juliaWidth;
    dY = (maxJuliaY - minJuliaY) / (double)juliaHeight;

    curY = minJuliaY;
    for (y = 0; y < juliaHeight; ++y)
    {
	curX = minJuliaX;
	for (x = 0; x < juliaWidth; ++x)
	{
	    int num;
	    float xr,
		xi,
		xrsq,
		xisq;

	    num = 0;
	    xr = curX;
	    xi = curY;
	    do
	    {
		xrsq = xr * xr;
		xisq = xi * xi;
		xi = 2 * xr * xi + juliaQY;
		xr = xrsq - xisq + juliaQX;
	    } while (((xrsq + xisq) < 4.0) && (++num < MAX_ITER - 1));
	    *data = num % MAX_ITER;

	    ++data;
	    curX += dX;
	}
	curY += dY;
    }

    return self;
}

- calcMandelImageRep
{
    int *data,
	i;
    unsigned char *imgData;
    NXBitmapImageRep *imgRep;

    imgRep = [[NXBitmapImageRep alloc]
		 initData: NULL pixelsWide: mandelWidth
		 pixelsHigh: mandelHeight
		 bitsPerSample: 8 samplesPerPixel: 3 hasAlpha: NO
		 isPlanar: NO colorSpace: NX_RGBColorSpace
		 bytesPerRow: 0 bitsPerPixel: 0];

    data = rawMandelData;
    imgData = [imgRep data];
    for (i = 0; i < mandelWidth * mandelHeight; ++i)
    {
	float red,
	    green,
	    blue;

	NXConvertColorToRGB(colorTable[*data], &red, &green, &blue);
	imgData[0] = 255.0 * red;
	imgData[1] = 255.0 * green;
	imgData[2] = 255.0 * blue;

	++data;
	imgData += 3;
    }

    [mandelView setImageRep: imgRep];

    return self;
}

- calcJuliaImageRep
{
    int *data,
	i;
    unsigned char *imgData;
    NXBitmapImageRep *imgRep;

    imgRep = [[NXBitmapImageRep alloc]
		 initData: NULL pixelsWide: juliaWidth
		 pixelsHigh: juliaHeight
		 bitsPerSample: 8 samplesPerPixel: 3 hasAlpha: NO
		 isPlanar: NO colorSpace: NX_RGBColorSpace
		 bytesPerRow: 0 bitsPerPixel: 0];

    data = rawJuliaData;
    imgData = [imgRep data];
    for (i = 0; i < juliaWidth * juliaHeight; ++i)
    {
	float red,
	    green,
	    blue;

	NXConvertColorToRGB(colorTable[*data], &red, &green, &blue);
	imgData[0] = 255.0 * red;
	imgData[1] = 255.0 * green;
	imgData[2] = 255.0 * blue;

	++data;
	imgData += 3;
    }

    [juliaView setImageRep: imgRep];

    return self;
}

- displayParams
{
    [mandelForm setDoubleValue: minMandelX at: 0];
    [mandelForm setDoubleValue: maxMandelX at: 1];
    [mandelForm setDoubleValue: minMandelY at: 2];
    [mandelForm setDoubleValue: maxMandelY at: 3];

    [juliaForm setDoubleValue: minJuliaX at: 0];
    [juliaForm setDoubleValue: maxJuliaX at: 1];
    [juliaForm setDoubleValue: minJuliaY at: 2];
    [juliaForm setDoubleValue: maxJuliaY at: 3];

    [juliaQForm setDoubleValue: juliaQX at: 0];
    [juliaQForm setDoubleValue: juliaQY at: 1];

    return self;
}

- imageView: sender sizedTo: (NXCoord) newWidth : (NXCoord) newHeight
{
    if (sender == mandelView)
    {
	mandelWidth = newWidth;
	mandelHeight = newHeight;

	[self calcRawMandelData];
	[self calcMandelImageRep];
    }
    else
    {
	juliaWidth = newWidth;
	juliaHeight = newHeight;

	[self calcRawJuliaData];
	[self calcJuliaImageRep];
    }

    return self;
}

- imageView: sender mouseDown: (NXCoord) x : (NXCoord) y
{
    if (sender != mandelView)
	return self;

    juliaQX = minMandelX +
	x * (maxMandelX - minMandelX) / (double)mandelWidth;
    juliaQY = minMandelY +
	y * (maxMandelY - minMandelY) / (double)mandelHeight;

    [self calcRawJuliaData];
    [self calcJuliaImageRep];

    [self displayParams];

    return self;
}

- imageView: sender mouseDragged: (NXCoord) x : (NXCoord) y
{
    [self imageView: sender mouseDown: x : y];

    return self;
}

- imageView: sender selectedRect: (const NXRect*) rect
{
    if (sender == mandelView)
    {
	double xUnit = (maxMandelX - minMandelX) / (double)mandelWidth,
	    yUnit = (maxMandelY - minMandelY) / (double)mandelHeight;

	maxMandelX = minMandelX + (rect->origin.x + rect->size.width) * xUnit;
	minMandelY = minMandelY +
	    (mandelHeight - rect->origin.y - rect->size.height) * yUnit;
	minMandelX = minMandelX + rect->origin.x * xUnit;
	maxMandelY = maxMandelY - rect->origin.y * yUnit;

	[self calcRawMandelData];
	[self calcMandelImageRep];
    }
    else if (sender == juliaView)
    {
	double xUnit = (maxJuliaX - minJuliaX) / (double)juliaWidth,
	    yUnit = (maxJuliaY - minJuliaY) / (double)juliaHeight;

	maxJuliaX = minJuliaX + (rect->origin.x + rect->size.width) * xUnit;
	minJuliaY = minJuliaY +
	    (juliaHeight - rect->origin.y - rect->size.height) * yUnit;
	minJuliaX = minJuliaX + rect->origin.x * xUnit;
	maxJuliaY = maxJuliaY - rect->origin.y * yUnit;

	[self calcRawJuliaData];
	[self calcJuliaImageRep];
    }

    [self displayParams];

    return self;
}

- colorMergeDidChange: sender
{
    [self calcColorTable];
    [self calcMandelImageRep];
    [self calcJuliaImageRep];

    return self;
}

- windowWillMiniaturize: sender toMiniwindow: miniwindow
{
    if (sender == mandelWindow)
	[mandelWindow setMiniwindowIcon: "minimandel"];
    else if (sender == juliaWindow)
	[juliaWindow setMiniwindowIcon: "minijulia"];

    return self;
}

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

    [infoPanel makeKeyAndOrderFront: nil];

    return self;
}

@end
