// Mandelbrot 0.1: A demonstration of Qt#
//
// Copyright 2002 Marcus Urban
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

// NOTE:
// Variables with names like PlotXMin are in the cartesian coordinate plane.
// Those with names like ImageXMin was in a translated coordinate system
// where (0, 0) is the upper-left corner.


using System;
using Qt;


// This struct is a simple implementation of complex numbers.
// It was originally in a separate file because it is useful
// in its own right. It is included here file only to make the
// compilation process simpler of new users.

public struct Complex {

	private double re, im;
	//private int x;

	public Complex (double re, double im)
	{
		this.re = re;
		this.im = im;
	}

	public static implicit operator Complex (double re)
	{
		return new Complex (re, 0.0);
	}

	public double Re
	{
		get { return re; }
	}
	
	public double Im
	{
		get { return im; }
	}
	
	public static double Abs (Complex a)
	{
		return Math.Abs (a.re) >= Math.Abs (a.im) ?
			Math.Abs (a.re) * Math.Sqrt( 1.0 + (a.im/a.re)*(a.im/a.re) ) :
			Math.Abs (a.im) * Math.Sqrt( 1.0 + (a.re/a.im)*(a.re/a.im) );
	}

	public static Complex operator + (Complex a, Complex b)
	{
		return new Complex (a.re + b.re, a.im + b.im);
	}

	public static Complex operator - (Complex a, Complex b)
	{
		return new Complex (a.re - b.re, a.im - b.im);
	}

	public static Complex operator * (Complex a, Complex b)
	{
		return new Complex ( a.re*b.re - a.im*b.im, a.im*b.re + a.re*b.im );
	}

	// Division isn't pretty. Do it later.

	public override string ToString ()
	{
		return this.re.ToString () + "+" + this.im.ToString () + "i";
	}
}


public class Mandelbrot {

	public static int Iterations ( Complex c )
	{
		const int MaximumIterations = 16;
		Complex value = c;
		int currentDepth = 1;

		while ( Complex.Abs(value) < 2.0 && currentDepth <= MaximumIterations ) {
			++currentDepth;
			value = (value * value) + c;
		}
		return currentDepth;
	}


	public static void Main (string[] args)
	{
		QApplication app = new QApplication (args);

		ImageDialog dialog = new ImageDialog (null, "Mandelbrot", false, 0);
		dialog.Show ();
		app.SetMainWidget (dialog);
		app.Exec ();

	}
}


public class ImageDialog : QDialog {

	const double DefaultPlotXMin = -2.0;
	const double DefaultPlotXMax = 2.0;
	const double DefaultPlotYMin = -1.5;
	const double DefaultPlotYMax = 1.5;

	QHBoxLayout dialogLayout;
	QGridLayout gridLayout;
	QVBoxLayout leftLayout;
	QHBoxLayout buttonLayout;
	QPushButton redrawButton;
	QLabel pixmapLabel;
	QSizePolicy fixedSizePolicy;

	QLabel XMinLabel, XMaxLabel, YMinLabel, YMaxLabel;
	QLineEdit editXMin, editXMax, editYMin, editYMax;

	public ImageDialog (QWidget parent, string name, bool modal, WidgetFlags fl):
		base (parent, name, modal, fl)
	{
		if (name == string.Empty)
			SetName ("imageDialog");
		SetCaption ("Mandelbrot Image");

		dialogLayout = new QHBoxLayout (this, 11, 6);
		gridLayout = new QGridLayout (null, 1, 1, 0, 6, "gridLayout");
		leftLayout = new QVBoxLayout (null, 0, 6, "leftLayout");

		fixedSizePolicy = new QSizePolicy ();
		fixedSizePolicy.SetHorData (QSizePolicy.SizeType.Fixed);
		fixedSizePolicy.SetVerData (QSizePolicy.SizeType.Fixed);

		XMinLabel = new QLabel ("Xmin", this);
		XMinLabel.SetSizePolicy (fixedSizePolicy);
		gridLayout.AddWidget (XMinLabel, 0, 0);

		XMaxLabel = new QLabel ("Xmax", this);
		XMaxLabel.SetSizePolicy(fixedSizePolicy);
		gridLayout.AddWidget (XMaxLabel, 1, 0);

		YMinLabel = new QLabel ("Ymin", this);
		YMinLabel.SetSizePolicy (fixedSizePolicy);
		gridLayout.AddWidget (YMinLabel, 2, 0);

		YMaxLabel = new QLabel ("Ymax", this);
		YMaxLabel.SetSizePolicy (fixedSizePolicy);
		gridLayout.AddWidget (YMaxLabel, 3, 0);

		QDoubleValidator validator = new QDoubleValidator (this);

		editXMin = new QLineEdit (this, "editXMin");
		editXMin.SetText (Convert.ToString (DefaultPlotXMin));
		editXMin.SetValidator (validator);
		gridLayout.AddWidget (editXMin, 0, 1);

		editXMax = new QLineEdit (this, "editXMax");
		editXMax.SetText (Convert.ToString(DefaultPlotXMax));
		editXMax.SetValidator (validator);
		gridLayout.AddWidget (editXMax, 1, 1);

		editYMin = new QLineEdit (this, "editYMin");
		editYMin.SetText (Convert.ToString(DefaultPlotYMin));
		editYMin.SetValidator (validator);
		gridLayout.AddWidget (editYMin, 2, 1);

		editYMax = new QLineEdit (this, "editYMax");
		editYMax.SetText (Convert.ToString(DefaultPlotYMax));
		editYMax.SetValidator (validator);
		gridLayout.AddWidget (editYMax, 3, 1);

		leftLayout.AddLayout (gridLayout);
		QSpacerItem spacer1 = new QSpacerItem (0, 0, 0, 0);
		leftLayout.AddItem (spacer1);

		buttonLayout = new QHBoxLayout (null, 0, 6, "buttonLayout");
		QSpacerItem spacer2 = new QSpacerItem (0, 0, 0, 0);
		buttonLayout.AddItem (spacer2);

		redrawButton = new QPushButton ("Redraw", this);
		redrawButton.SetDefault (true);
		buttonLayout.AddWidget (redrawButton);

		QSpacerItem spacer3 = new QSpacerItem (0, 0, 0, 0);
		buttonLayout.AddItem (spacer3);

		leftLayout.AddLayout (buttonLayout);

		dialogLayout.AddLayout (leftLayout);
		QSpacerItem spacer4 = new QSpacerItem (0, 0, 0, 0);
		dialogLayout.AddItem (spacer4);

		pixmapLabel = new QLabel (this, "pixmapLabel", 0);
		pixmapLabel.SetScaledContents (true);
		dialogLayout.AddWidget (pixmapLabel);

		QObject.Connect (redrawButton, SIGNAL ("clicked()"), this, SLOT ("Redraw()"));

		Redraw ();
	}


	QImage MandelbrotImage ()
	{
		int depth;
		double real, imag;

		double PlotXMin = Convert.ToDouble ( editXMin.Text () );
		double PlotXMax = Convert.ToDouble ( editXMax.Text () );
		double PlotYMin = Convert.ToDouble ( editYMin.Text () );
		double PlotYMax = Convert.ToDouble ( editYMax.Text () );

		int ImageXMax = pixmapLabel.Width ();
		int ImageYMax = pixmapLabel.Height ();

		QImage image = new QImage (ImageXMax, ImageYMax, 32, 0);

		for (int x = 0; x <= ImageXMax - 1; x+=1) {
			for (int y = 0; y <= ImageYMax - 1; y+=1) {
				real = (PlotXMax - PlotXMin)/ImageXMax * x + PlotXMin;
				imag = (PlotYMin - PlotYMax)/ImageYMax * y + PlotYMax;
				depth = Mandelbrot.Iterations ( new Complex (real, imag) );
				image.SetPixel ( x, y, (uint) depth*16 );
			}
		}

		return image;
	}

	public void Redraw ()
	{
		QSize s = pixmapLabel.BaseSize ();
		pixmapLabel.Resize (400,300);
		QApplication.SetOverrideCursor ( new QCursor( (int) CursorShape.WaitCursor ));
		QImage image = MandelbrotImage ();
		QPixmap pixmap = new QPixmap (image);
		pixmapLabel.SetPixmap( pixmap);
		image.Dispose ();
		pixmap.Dispose ();
		this.AdjustSize ();
		QApplication.RestoreOverrideCursor ();
	}
}


