using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Numerics;

namespace Julia_UI
{
    public class Fractal_Generator
    {

        //Main drawing functions

        public static void Julia_to_File(int dimx, int dimy, double startx, double starty, double xoff, double const_real, double const_imag, int max_iteration, int[] color_choice, Func<double[], double[], int, int[], byte[]> colour_function)
        {
            #region summary
            ///</summary>
            ///This function draws a (dimx x dimy) picture. The layout of the number plane is defined by using (startx,starty) as a starting point and going up, down for yoff and left, right for xoff.
            ///The picture is coloured by using colour_function, giving it the current x and y position in the 2D plane, which returns a [byte blue, byte green, byte red, byte alpha] value of the pixel.
            ///</summary>
            #endregion

            #region Creating
            //Create new canvas and other variables
            Bitmap canvas = new Bitmap(dimx, dimy);
            double yoff = xoff / dimx * dimy;   //calculates the yoff automatically, so that there are no distortions
            double[] position = new double[2];
            double[] constant = { const_real, const_imag };
            byte[] colour = new byte[4];

            //Convert canvas to bitmapdata
            System.Drawing.Imaging.BitmapData bmd = canvas.LockBits(new Rectangle(0, 0, dimx, dimy), System.Drawing.Imaging.ImageLockMode.ReadOnly, canvas.PixelFormat);
            #endregion

            #region Colouring pixel by pixel

            for (int y = 0; y < bmd.Height; y++)
            {
                for (int x = 1; x < bmd.Width; x++)
                {
                    position[0] = startx + ((2 * x - dimx + 1.0) / dimx) * xoff;
                    position[1] = starty + ((2 * y - dimy + 1.0) / dimy) * yoff;
                    colour = colour_function(position, constant, max_iteration, color_choice);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4, colour[2]);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4 + 1, colour[1]);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4 + 2, colour[0]);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4 + 3, colour[3]);
                }
            }

            #endregion

            #region//Saving
            string name = "xy" + startx +"_"+ starty + "size"+xoff +"c"+ const_real +"_"+ const_imag+ "res" + dimx;
            canvas.UnlockBits(bmd);
            canvas.Save(name + ".png", System.Drawing.Imaging.ImageFormat.Png);
            #endregion
        }

        public static Bitmap Julia_to_Picturebox(int dimx, int dimy, double startx, double starty, double xoff, double const_real, double const_imag, int max_iteration,int[]color_choice, Func<double[], double[], int, int[], byte[]> colour_function)
        {
            #region summary
            ///</summary>
            ///This function draws a (dimx x dimy) picture. The layout of the number plane is defined by using (startx,starty) as a starting point and going up, down for yoff and left, right for xoff.
            ///The picture is coloured by using colour_function, giving it the current x and y position in the 2D plane, which returns a [byte blue, byte green, byte red, byte alpha] value of the pixel.
            ///</summary>
            #endregion

            #region Creating
            //Create new canvas and other variables
            Bitmap canvas = new Bitmap(dimx, dimy);
            double yoff = xoff / dimx * dimy;   //calculates the yoff automatically, so that there are no distortions
            double[] position = new double[2];
            double[] constant = { const_real, const_imag };
            byte[] colour = new byte[4];

            //Convert canvas to bitmapdata
            System.Drawing.Imaging.BitmapData bmd = canvas.LockBits(new Rectangle(0, 0, dimx, dimy), System.Drawing.Imaging.ImageLockMode.ReadOnly, canvas.PixelFormat);
            #endregion

            #region Colouring pixel by pixel
            
            for (int y = 0; y < bmd.Height; y++)
            {
                for (int x = 1; x < bmd.Width; x++)
                {
                    position[0] = startx + ((2 * x - dimx + 1.0) / dimx) * xoff;
                    position[1] = starty + ((2 * y - dimy + 1.0) / dimy) * yoff;
                    colour = colour_function(position, constant, max_iteration,color_choice);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4, colour[2]);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4 + 1, colour[1]);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4 + 2, colour[0]);
                    Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + x * 4 + 3, colour[3]);
                }
            }
            #endregion

            #region Saving
            canvas.UnlockBits(bmd);
            return canvas;
            #endregion
        }

        //Colouring functions


        public static byte[] Julia_Color(double[] position, double[] constant,int max_iteration, int[] color_choices)
        {

            //Geting value
            int max_size = 10;
            Complex z = new Complex(position[0], position[1]);
            Complex c = new Complex(constant[0], constant[1]);

            int i = 0;
            while (i < max_iteration && (Complex.Abs(z)) < max_size)
            {
                z = z * z + c;
                i++;
            }

            // Coloring
            byte[] pixel_color = new byte[4];
            pixel_color[3] = (byte)255;
            for (int j = 0; j < 3; j++)
            {
                if (color_choices[j] == 0)
                {
                    pixel_color[j] = (byte)(((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 1)
                {
                    pixel_color[j] = (byte)(255 - ((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 2)
                {
                    pixel_color[j] = (byte)(((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration) * 255);
                }
                else if (color_choices[j] == 3)
                {
                    pixel_color[j] = (byte)(Math.Pow((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration, 2) * 255);
                }
                else if (color_choices[j] == 4)
                {
                    pixel_color[j] = (byte)0;
                }
            }

            return pixel_color;
        }
        public static byte[] Mandelbrot_Color(double[] position, double[] constant, int max_iteration, int[] color_choices)
        {

            //Geting value
            int max_size = 10;
            Complex z = new Complex(0, 0);
            Complex c = new Complex(position[0], position[1]);
            int i = 0;
            while (i < max_iteration && (Complex.Abs(z)) < max_size)
            {
                z = z * z + c;
                i++;
            }

            // Coloring
            byte[] pixel_color = new byte[4];
            pixel_color[3] = (byte)255;
            for (int j = 0; j < 3; j++)
            {
                if (color_choices[j] == 0)
                {
                    pixel_color[j] = (byte)(((double)i / max_iteration) * 255);
                }
                if (color_choices[j] == 1)
                {
                    pixel_color[j] = (byte)(255 - ((double)i / max_iteration) * 255);
                }
                if (color_choices[j] == 2)
                {
                    pixel_color[j] = (byte)(((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration) * 255);
                }
                if (color_choices[j] == 3)
                {
                    pixel_color[j] = (byte)(Math.Pow((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration, 2) * 255);
                }
                if (color_choices[j] == 4)
                {
                    pixel_color[j] = (byte)0;
                }
            }

            return pixel_color;
        }
        public static byte[] Exponential_Color(double[] position, double[] constant, int max_iteration, int[] color_choices)
        {

            //Geting value
            int max_size = 100;
            Complex z = new Complex(position[0], position[1]);
            Complex c = new Complex(constant[0], constant[1]);

            int i = 0;
            while (i < max_iteration && (Complex.Abs(z)) < max_size)
            {
                z = Complex.Exp(z) + c;
                i++;
            }

            // Coloring
            byte[] pixel_color = new byte[4];
            pixel_color[3] = (byte)255;
            for (int j = 0; j < 3; j++)
            {
                if (color_choices[j] == 0)
                {
                    pixel_color[j] = (byte)(((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 1)
                {
                    pixel_color[j] = (byte)(255 - ((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 2)
                {
                    pixel_color[j] = (byte)(((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration) * 255);
                }
                else if (color_choices[j] == 3)
                {
                    pixel_color[j] = (byte)(Math.Pow((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration, 2) * 255);
                }
                else if (color_choices[j] == 4)
                {
                    pixel_color[j] = (byte)0;
                }
            }

            return pixel_color;
        }
        public static byte[] Exponential_plus_linear_Color(double[] position, double[] constant, int max_iteration, int[] color_choices)
        {

            //Geting value
            int max_size = 100;
            Complex z = new Complex(position[0], position[1]);
            Complex c = new Complex(constant[0], constant[1]);

            int i = 0;
            while (i < max_iteration && (Complex.Abs(z)) < max_size)
            {
                z = Complex.Exp(z) + z + c;
                i++;
            }

            // Coloring
            byte[] pixel_color = new byte[4];
            pixel_color[3] = (byte)255;
            for (int j = 0; j < 3; j++)
            {
                if (color_choices[j] == 0)
                {
                    pixel_color[j] = (byte)(((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 1)
                {
                    pixel_color[j] = (byte)(255 - ((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 2)
                {
                    pixel_color[j] = (byte)(((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration) * 255);
                }
                else if (color_choices[j] == 3)
                {
                    pixel_color[j] = (byte)(Math.Pow((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration, 2) * 255);
                }
                else if (color_choices[j] == 4)
                {
                    pixel_color[j] = (byte)0;
                }
            }

            return pixel_color;
        }
        public static byte[] Sinh_z_square_Color(double[] position, double[] constant, int max_iteration, int[] color_choices)
        {

            //Geting value
            int max_size = 100;
            Complex z = new Complex(position[0], position[1]);
            Complex c = new Complex(constant[0], constant[1]);

            int i = 0;
            while (i < max_iteration && (Complex.Abs(z)) < max_size)
            {
                z = Complex.Sinh(z*z) + c;
                i++;
            }

            // Coloring
            byte[] pixel_color = new byte[4];
            pixel_color[3] = (byte)255;
            for (int j = 0; j < 3; j++)
            {
                if (color_choices[j] == 0)
                {
                    pixel_color[j] = (byte)(((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 1)
                {
                    pixel_color[j] = (byte)(255 - ((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 2)
                {
                    pixel_color[j] = (byte)(((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration) * 255);
                }
                else if (color_choices[j] == 3)
                {
                    pixel_color[j] = (byte)(Math.Pow((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration, 2) * 255);
                }
                else if (color_choices[j] == 4)
                {
                    pixel_color[j] = (byte)0;
                }
            }

            return pixel_color;
        }
        public static byte[] Z_power_3_Color(double[] position, double[] constant, int max_iteration, int[] color_choices)
        {

            //Geting value
            int max_size = 10;
            Complex z = new Complex(position[0], position[1]);
            Complex c = new Complex(constant[0], constant[1]);

            int i = 0;
            while (i < max_iteration && (Complex.Abs(z)) < max_size)
            {
                z = z*z*z + c;
                i++;
            }

            // Coloring
            byte[] pixel_color = new byte[4];
            pixel_color[3] = (byte)255;
            for (int j = 0; j < 3; j++)
            {
                if (color_choices[j] == 0)
                {
                    pixel_color[j] = (byte)(((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 1)
                {
                    pixel_color[j] = (byte)(255 - ((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 2)
                {
                    pixel_color[j] = (byte)(((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration) * 255);
                }
                else if (color_choices[j] == 3)
                {
                    pixel_color[j] = (byte)(Math.Pow((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration, 2) * 255);
                }
                else if (color_choices[j] == 4)
                {
                    pixel_color[j] = (byte)0;
                }
            }

            return pixel_color;
        }
        public static byte[] Z_power_4_Color(double[] position, double[] constant, int max_iteration, int[] color_choices)
        {

            //Geting value
            int max_size = 10;
            Complex z = new Complex(position[0], position[1]);
            Complex c = new Complex(constant[0], constant[1]);

            int i = 0;
            while (i < max_iteration && (Complex.Abs(z)) < max_size)
            {
                z = z*z*z*z + c;
                i++;
            }

            // Coloring
            byte[] pixel_color = new byte[4];
            pixel_color[3] = (byte)255;
            for (int j = 0; j < 3; j++)
            {
                if (color_choices[j] == 0)
                {
                    pixel_color[j] = (byte)(((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 1)
                {
                    pixel_color[j] = (byte)(255 - ((double)i / max_iteration) * 255);
                }
                else if (color_choices[j] == 2)
                {
                    pixel_color[j] = (byte)(((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration) * 255);
                }
                else if (color_choices[j] == 3)
                {
                    pixel_color[j] = (byte)(Math.Pow((max_iteration - Math.Abs(i - max_iteration / 2.0) * 2) / max_iteration, 2) * 255);
                }
                else if (color_choices[j] == 4)
                {
                    pixel_color[j] = (byte)0;
                }
            }

            return pixel_color;
        }


    }

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new main_window());
        }
    }
}
