// 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 ImageDialog dialog;	

	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);

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

	}
}


public class PicLabel: QFrame
{
	QPixmap newPixmap;
	int newWidth = 400;
	int newHeight = 300;

	public PicLabel( QWidget parent, string name, WidgetFlags flags ):
		base( parent, name, flags )
	{
		SetBackgroundMode (Qt.BackgroundMode.NoBackground);	
		resizeEvent += new ResizeEvent (PerformResize);
		paintEvent += new PaintEvent (PerformPaint);

	}

	protected void PerformResize (QResizeEvent e)
	{
		Console.WriteLine("Resizing to {0} by {1}",
			e.Size().Width(), e.Size().Height() );

		newWidth = e.Size().Width();
		newHeight = e.Size().Height();

	}


	protected void PerformPaint(QPaintEvent e )
	{
		Console.WriteLine("Making a new image {0} by {1}", newWidth, newHeight );

 		QImage image = Mandelbrot.dialog.MandelbrotImage( newWidth, newHeight );
 		newPixmap = new QPixmap( image );

		BitBlt(this, 0, 0, newPixmap,
 			0, 0, -1, -1, RasterOp.CopyROP, false);
		Console.WriteLine("PerformPaint");
	}
}



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;
	public PicLabel 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 ( QSizePolicy.SizeType.Fixed,
			QSizePolicy.SizeType.Fixed, false );

		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.SetSizePolicy( fixedSizePolicy );
		editXMin.SetText (Convert.ToString (DefaultPlotXMin));
		editXMin.SetValidator (validator);
		gridLayout.AddWidget (editXMin, 0, 1);

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

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

		editYMax = new QLineEdit (this, "editYMax");
		editYMax.SetSizePolicy( fixedSizePolicy );		
		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.SetSizePolicy ( fixedSizePolicy );	
		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 PicLabel (this, "pixmapLabel", 0);
		//pixmapLabel.SetScaledContents (true);
		pixmapLabel.SetSizePolicy( QSizePolicy.SizeType.Minimum,
			QSizePolicy.SizeType.Minimum, false );
		pixmapLabel.SetGeometry( 0, 0, 400, 300 );
		pixmapLabel.Show();
		pixmapLabel.Resize(400,300);
		dialogLayout.AddWidget (pixmapLabel);
	
		
		//QImage image = MandelbrotImage( 400, 300 );
		//pixmapLabel.SetPixmap( new QPixmap( image ) );
		
		
		QObject.Connect (redrawButton, SIGNAL ("clicked()"), pixmapLabel, SLOT ("Repaint()"));

		//Redraw ();
	}

	public QImage MandelbrotImage ( int width, int height)
	{
		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 = width;
		int ImageYMax = 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;
	}

}


