// 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)
	{
		TQApplication app = new TQApplication (args);

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

	}
}


public class PicLabel: TQFrame
{
	TQPixmap newPixmap;
	int newWidth = 400;
	int newHeight = 300;

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

	}

	protected void PerformResize (TQResizeEvent 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(TQPaintEvent e )
	{
		Console.WriteLine("Making a new image {0} by {1}", newWidth, newHeight );

 		TQImage image = Mandelbrot.dialog.MandelbrotImage( newWidth, newHeight );
 		newPixmap = new TQPixmap( image );

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



public class ImageDialog : TQDialog {

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

	TQHBoxLayout dialogLayout;
	TQGridLayout gridLayout;
	TQVBoxLayout leftLayout;
	TQHBoxLayout buttonLayout;
	TQPushButton redrawButton;
	public PicLabel pixmapLabel;
	TQSizePolicy fixedSizePolicy;

	TQLabel XMinLabel, XMaxLabel, YMinLabel, YMaxLabel;
	TQLineEdit editXMin, editXMax, editYMin, editYMax;


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

		dialogLayout = new TQHBoxLayout (this, 11, 6);
		gridLayout = new TQGridLayout (null, 1, 1, 0, 6, "gridLayout");
		leftLayout = new TQVBoxLayout (null, 0, 6, "leftLayout");

		fixedSizePolicy = new TQSizePolicy ( TQSizePolicy.SizeType.Fixed,
			TQSizePolicy.SizeType.Fixed, false );

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

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

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

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

		TQDoubleValidator validator = new TQDoubleValidator (this);

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

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

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

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

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

		buttonLayout = new TQHBoxLayout (null, 0, 6, "buttonLayout");
		TQSpacerItem spacer2 = new TQSpacerItem (0, 0, 0, 0);
		buttonLayout.AddItem (spacer2);

		redrawButton = new TQPushButton ("Redraw", this);
		redrawButton.SetSizePolicy ( fixedSizePolicy );	
		redrawButton.SetDefault (true);
		buttonLayout.AddWidget (redrawButton);

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

		leftLayout.AddLayout (buttonLayout);

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

		pixmapLabel = new PicLabel (this, "pixmapLabel", 0);
		//pixmapLabel.SetScaledContents (true);
		pixmapLabel.SetSizePolicy( TQSizePolicy.SizeType.Minimum,
			TQSizePolicy.SizeType.Minimum, false );
		pixmapLabel.SetGeometry( 0, 0, 400, 300 );
		pixmapLabel.Show();
		pixmapLabel.Resize(400,300);
		dialogLayout.AddWidget (pixmapLabel);
	
		
		//TQImage image = MandelbrotImage( 400, 300 );
		//pixmapLabel.SetPixmap( new TQPixmap( image ) );
		
		
		TQObject.Connect (redrawButton, TQT_SIGNAL ("clicked()"), pixmapLabel, TQT_SLOT ("Repaint()"));

		//Redraw ();
	}

	public TQImage 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;

		TQImage image = new TQImage (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;
	}

}


