using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Drawing.Imaging;
using System.IO;
using System.Numerics;

namespace Mandelbrot
{
    public partial class frm41Mandelbrot : Form
    {
        public frm41Mandelbrot()
        {
            InitializeComponent();
        }

        private bool IsDrawingBox;
        private Point StartPoint;
        private double StartX, StartY, CurX, CurY;
        private double Wxmin, Wxmax, Wymin, Wymax;

        public int MaxIterations;
        public Complex Z = new Complex(0, 0);

        public List<Color> Colors = new List<Color>();

        private Bitmap TheBitmap;

        private const double MinCurveX = -2.2;
        private const double MaxCurveX = 1;
        private const double MinCurveY = -1.2;
        private const double MaxCurveY = 1.2;

        // Return this color's value.
        public Color Color(int index)
        {
            return Colors[index];
        }

        // Reset the number of colors to 0.
        public void ResetColors()
        {
            Colors = new List<Color>();
        }

        // Adjust the aspect ratio of the selected
        // coordinates so they fit the window properly.
        private void AdjustAspect()
        {
            double hgt, wid, mid;

            double wantAspect = (Wymax - Wymin) / (Wxmax - Wxmin);
            double canvasAspect =
                (double)canvasPictureBox.ClientSize.Height /
                (double)canvasPictureBox.ClientSize.Width;
            if (wantAspect > canvasAspect)
            {
                // The selected area is too tall and thin.
                // Make it wider.
                wid = (Wymax - Wymin) / canvasAspect;
                mid = (Wxmin + Wxmax) / 2;
                Wxmin = mid - wid / 2;
                Wxmax = mid + wid / 2;
            }
            else
            {
                // The selected area is too short and wide.
                // Make it taller.
                hgt = (Wxmax - Wxmin) * canvasAspect;
                mid = (Wymin + Wymax) / 2;
                Wymin = mid - hgt / 2;
                Wymax = mid + hgt / 2;
            }
        }

        // Draw the Mandelbrot set.
        private void DrawMandelbrot()
        {
            // Work until the magnitude > 2.
            const int MaxMag = 2;

            // Make a Bitmap to draw on.
            int width = canvasPictureBox.ClientSize.Width;
            int height = canvasPictureBox.ClientSize.Height;
            TheBitmap = new Bitmap(width, height);
            using (Graphics gr = Graphics.FromImage(TheBitmap))
            {
                // Clear.
                gr.Clear(canvasPictureBox.BackColor);
                canvasPictureBox.Image = TheBitmap;
            }
            Application.DoEvents();

            // Adjust the coordinate bounds to fit canvasPictureBox.
            AdjustAspect();

            // dReaC is the change in the real part
            // (X value) for C. dImaC is the change in the
            // imaginary part (Y value).
            double dReaC = (Wxmax - Wxmin) / (width - 1);
            double dImaC = (Wymax - Wymin) / (height - 1);

            // Calculate the values.
            int numColors = Colors.Count;
            double reaC = Wxmin;
            for (int x = 0; x < width; x++)
            {
                double imaC = Wymin;
                for (int y = 0; y < height; y++)
                {
                    Complex z = Z;
                    Complex c = new Complex(reaC, imaC);
                    int clr = 1;
                    while ((clr < MaxIterations) && (z.Magnitude < MaxMag))
                    {
                        // Calculate Z(clr).
                        z = z * z + c;
                        clr++;
                    }

                    // Set the pixel's value.
                    TheBitmap.SetPixel(x, y, Colors[clr % numColors]);

                    imaC += dImaC;
                }
                reaC += dReaC;

                // Let the user know we//re not dead.
                if (x % 10 == 0) canvasPictureBox.Refresh();
            }
            canvasPictureBox.Refresh();

            Text = "Mandelbrot (" +
                Wxmin.ToString("0.000000") + ", " +
                Wymin.ToString("0.000000") + ")-(" +
                Wxmax.ToString("0.000000") + ", " +
                Wymax.ToString("0.000000") + ")";
        }

        // Scale the selected area by this factor.
        private void ScaleArea(int scaleFactor)
        {
            double size = scaleFactor * (Wxmax - Wxmin);
            if (size > 3.2)
            {
                fullScaleMenuItem_Click(null, null);
                return;
            }
            double mid = (Wxmin + Wxmax) / 2;
            Wxmin = mid - size / 2;
            Wxmax = mid + size / 2;

            size = scaleFactor * (Wymax - Wymin);
            if (size > 2.4)
            {
                fullScaleMenuItem_Click(null, null);
                return;
            }
            mid = (Wymin + Wymax) / 2;
            Wymin = mid - size / 2;
            Wymax = mid + size / 2;

            this.Cursor = Cursors.WaitCursor;
            Application.DoEvents();
            DrawMandelbrot();
            this.Cursor = Cursors.Default;
            canvasPictureBox.Cursor = Cursors.Cross;
        }

        // Set the scale.
        private void scaleMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem mnu = sender as ToolStripMenuItem;
            ScaleArea(int.Parse(mnu.Tag.ToString()));
        }

        // Zoom out to full scale.
        private void fullScaleMenuItem_Click(object sender, EventArgs e)
        {
            Wxmin = MinCurveX;
            Wxmax = MaxCurveX;
            Wymin = MinCurveY;
            Wymax = MaxCurveY;

            this.Cursor = Cursors.WaitCursor;
            Application.DoEvents();
            DrawMandelbrot();
            this.Cursor = Cursors.Default;
            canvasPictureBox.Cursor = Cursors.Cross;
        }

        private void frm41Mandelbrot_Load(object sender, EventArgs e)
        {
            this.Show();
            Application.DoEvents();

            MaxIterations = 64;

            // Create some default colors.
            ResetColors();
            MandelbrotConfig frm = new MandelbrotConfig();
            Colors.Add(frm.colorPictureBox5.BackColor);
            Colors.Add(frm.colorPictureBox8.BackColor);
            Colors.Add(frm.colorPictureBox14.BackColor);
            Colors.Add(frm.colorPictureBox20.BackColor);
            Colors.Add(frm.colorPictureBox26.BackColor);
            Colors.Add(frm.colorPictureBox32.BackColor);
            Colors.Add(frm.colorPictureBox38.BackColor);
            Colors.Add(frm.colorPictureBox44.BackColor);
            frm.Close();

            // Display the first Mandelbrot set.
            fullScaleMenuItem_Click(null, null);
        }

        private void setOptionsMenuItem_Click(object sender, EventArgs e)
        {
            MandelbrotConfig frm = new MandelbrotConfig();
            frm.Initialize(this);
            if (frm.ShowDialog() == DialogResult.OK) ScaleArea(1);
        }

        // Refresh. This is useful when the user resizes the form.
        private void refreshMenuItem_Click(object sender, EventArgs e)
        {
            ScaleArea(1);
        }

        // Start selecting an area.
        private void canvasPictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            IsDrawingBox = true;
            StartPoint = e.Location;
        }

        // Continue selecting an area.
        private void canvasPictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (!IsDrawingBox) return;

            Rectangle rect = GetRectangle(StartPoint, e.Location);
            Bitmap bm = new Bitmap(TheBitmap);
            using (Graphics gr = Graphics.FromImage(bm))
            {
                gr.DrawRectangle(Pens.Yellow, rect);
            }
            canvasPictureBox.Image = bm;
        }

        // Finish selecting an area.
        private void canvasPictureBox_MouseUp(object sender, MouseEventArgs e)
        {
            if (!IsDrawingBox) return;
            IsDrawingBox = false;
            canvasPictureBox.Image = TheBitmap;

            Rectangle rect = GetRectangle(StartPoint, e.Location);
            if (rect.Width < 1) rect.Width = 1;
            if (rect.Height < 1) rect.Height = 1;

            // Convert screen coords into drawing coords.
            double factor = (Wxmax - Wxmin) / canvasPictureBox.ClientSize.Width;
            Wxmax = Wxmin + rect.Right * factor;
            Wxmin = Wxmin + rect.Left * factor;

            factor = (Wymax - Wymin) / canvasPictureBox.ClientSize.Height;
            Wymax = Wymin + rect.Bottom * factor;
            Wymin = Wymin + rect.Top * factor;

            this.Cursor = Cursors.WaitCursor;
            Application.DoEvents();
            DrawMandelbrot();
            this.Cursor = Cursors.Default;
            canvasPictureBox.Cursor = Cursors.Cross;
        }

        // Return a Rectangle with the two points as opposite corners.
        private Rectangle GetRectangle(Point point1, Point point2)
        {
            int x = Math.Min(point1.X, point2.X);
            int y = Math.Min(point1.Y, point2.Y);
            int width = Math.Abs(point2.X - point1.X);
            int height = Math.Abs(point2.Y - point1.Y);
            return new Rectangle(x, y, width, height);
        }

        // Save the image.
        private void saveAsMenuItem_Click(object sender, EventArgs e)
        {
            if (mandelbrotSaveFileDialog.ShowDialog() == DialogResult.OK)
            {
                SaveImage(TheBitmap, mandelbrotSaveFileDialog.FileName);
            }
        }

        // Save the file with the appropriate format.
        public void SaveImage(Image image, string filename)
        {
            string extension = Path.GetExtension(filename);
            switch (extension.ToLower())
            {
                case ".bmp":
                    image.Save(filename, ImageFormat.Bmp);
                    break;
                case ".exif":
                    image.Save(filename, ImageFormat.Exif);
                    break;
                case ".gif":
                    image.Save(filename, ImageFormat.Gif);
                    break;
                case ".jpg":
                case ".jpeg":
                    image.Save(filename, ImageFormat.Jpeg);
                    break;
                case ".png":
                    image.Save(filename, ImageFormat.Png);
                    break;
                case ".tif":
                case ".tiff":
                    image.Save(filename, ImageFormat.Tiff);
                    break;
                default:
                    throw new NotSupportedException(
                        "Unknown file extension " + extension);
            }
        }


        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.mandelbrotSaveFileDialog = new System.Windows.Forms.SaveFileDialog();
            this.canvasPictureBox = new System.Windows.Forms.PictureBox();
            this.menuStrip1 = new System.Windows.Forms.MenuStrip();
            this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.saveMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.scaleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.x2MenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.x4MenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.x8MenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.fullScaleMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
            this.refreshMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.setOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            ((System.ComponentModel.ISupportInitialize)(this.canvasPictureBox)).BeginInit();
            this.menuStrip1.SuspendLayout();
            this.SuspendLayout();
            // 
            // mandelbrotSaveFileDialog
            // 
            this.mandelbrotSaveFileDialog.DefaultExt = "png";
            this.mandelbrotSaveFileDialog.Filter = "Image Files|*.bmp;*.jpg;*.gif;*.png;*.tif|All Files|*.*";
            // 
            // canvasPictureBox
            // 
            this.canvasPictureBox.BackColor = System.Drawing.Color.Black;
            this.canvasPictureBox.Dock = System.Windows.Forms.DockStyle.Fill;
            this.canvasPictureBox.Location = new System.Drawing.Point(0, 24);
            this.canvasPictureBox.Name = "canvasPictureBox";
            this.canvasPictureBox.Size = new System.Drawing.Size(330, 316);
            this.canvasPictureBox.TabIndex = 2;
            this.canvasPictureBox.TabStop = false;
            this.canvasPictureBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.canvasPictureBox_MouseDown);
            this.canvasPictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.canvasPictureBox_MouseMove);
            this.canvasPictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.canvasPictureBox_MouseUp);
            // 
            // menuStrip1
            // 
            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.fileToolStripMenuItem,
            this.scaleToolStripMenuItem,
            this.optionsToolStripMenuItem});
            this.menuStrip1.Location = new System.Drawing.Point(0, 0);
            this.menuStrip1.Name = "menuStrip1";
            this.menuStrip1.Size = new System.Drawing.Size(330, 24);
            this.menuStrip1.TabIndex = 3;
            this.menuStrip1.Text = "menuStrip1";
            // 
            // fileToolStripMenuItem
            // 
            this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.saveMenuItem});
            this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
            this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
            this.fileToolStripMenuItem.Text = "&File";
            // 
            // saveMenuItem
            // 
            this.saveMenuItem.Name = "saveMenuItem";
            this.saveMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S)));
            this.saveMenuItem.Size = new System.Drawing.Size(147, 22);
            this.saveMenuItem.Text = "&Save...";
            this.saveMenuItem.Click += new System.EventHandler(this.saveAsMenuItem_Click);
            // 
            // scaleToolStripMenuItem
            // 
            this.scaleToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.x2MenuItem,
            this.x4MenuItem,
            this.x8MenuItem,
            this.fullScaleMenuItem,
            this.toolStripMenuItem1,
            this.refreshMenuItem});
            this.scaleToolStripMenuItem.Name = "scaleToolStripMenuItem";
            this.scaleToolStripMenuItem.Size = new System.Drawing.Size(46, 20);
            this.scaleToolStripMenuItem.Text = "&Scale";
            // 
            // x2MenuItem
            // 
            this.x2MenuItem.Name = "x2MenuItem";
            this.x2MenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D2)));
            this.x2MenuItem.Size = new System.Drawing.Size(163, 22);
            this.x2MenuItem.Tag = "2";
            this.x2MenuItem.Text = "x&2";
            this.x2MenuItem.Click += new System.EventHandler(this.scaleMenuItem_Click);
            // 
            // x4MenuItem
            // 
            this.x4MenuItem.Name = "x4MenuItem";
            this.x4MenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D4)));
            this.x4MenuItem.Size = new System.Drawing.Size(163, 22);
            this.x4MenuItem.Tag = "4";
            this.x4MenuItem.Text = "x&4";
            this.x4MenuItem.Click += new System.EventHandler(this.scaleMenuItem_Click);
            // 
            // x8MenuItem
            // 
            this.x8MenuItem.Name = "x8MenuItem";
            this.x8MenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D8)));
            this.x8MenuItem.Size = new System.Drawing.Size(163, 22);
            this.x8MenuItem.Tag = "8";
            this.x8MenuItem.Text = "x&8";
            this.x8MenuItem.Click += new System.EventHandler(this.scaleMenuItem_Click);
            // 
            // fullScaleMenuItem
            // 
            this.fullScaleMenuItem.Name = "fullScaleMenuItem";
            this.fullScaleMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F)));
            this.fullScaleMenuItem.Size = new System.Drawing.Size(163, 22);
            this.fullScaleMenuItem.Text = "&Full Scale";
            this.fullScaleMenuItem.Click += new System.EventHandler(this.fullScaleMenuItem_Click);
            // 
            // toolStripMenuItem1
            // 
            this.toolStripMenuItem1.Name = "toolStripMenuItem1";
            this.toolStripMenuItem1.Size = new System.Drawing.Size(160, 6);
            // 
            // refreshMenuItem
            // 
            this.refreshMenuItem.Name = "refreshMenuItem";
            this.refreshMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F5;
            this.refreshMenuItem.Size = new System.Drawing.Size(163, 22);
            this.refreshMenuItem.Text = "&Refresh";
            this.refreshMenuItem.Click += new System.EventHandler(this.refreshMenuItem_Click);
            // 
            // optionsToolStripMenuItem
            // 
            this.optionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.setOptionsMenuItem});
            this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
            this.optionsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
            this.optionsToolStripMenuItem.Text = "&Options";
            // 
            // setOptionsMenuItem
            // 
            this.setOptionsMenuItem.Name = "setOptionsMenuItem";
            this.setOptionsMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
            this.setOptionsMenuItem.Size = new System.Drawing.Size(187, 22);
            this.setOptionsMenuItem.Text = "Set &Options...";
            this.setOptionsMenuItem.Click += new System.EventHandler(this.setOptionsMenuItem_Click);
            // 
            // frm41Mandelbrot
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(330, 340);
            this.Controls.Add(this.canvasPictureBox);
            this.Controls.Add(this.menuStrip1);
            this.MainMenuStrip = this.menuStrip1;
            this.Name = "frm41Mandelbrot";
            this.Text = "Mandelbrot";
            this.Load += new System.EventHandler(this.frm41Mandelbrot_Load);
            ((System.ComponentModel.ISupportInitialize)(this.canvasPictureBox)).EndInit();
            this.menuStrip1.ResumeLayout(false);
            this.menuStrip1.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        internal System.Windows.Forms.SaveFileDialog mandelbrotSaveFileDialog;
        internal System.Windows.Forms.PictureBox canvasPictureBox;
        private System.Windows.Forms.MenuStrip menuStrip1;
        private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem saveMenuItem;
        private System.Windows.Forms.ToolStripMenuItem scaleToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem x2MenuItem;
        private System.Windows.Forms.ToolStripMenuItem x4MenuItem;
        private System.Windows.Forms.ToolStripMenuItem x8MenuItem;
        private System.Windows.Forms.ToolStripMenuItem fullScaleMenuItem;
        private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
        private System.Windows.Forms.ToolStripMenuItem refreshMenuItem;
        private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem setOptionsMenuItem;
    }
}