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

namespace Selection
{
    public partial class Form2 : Form
    {
        Individual[] genCur, genNext; int size = 80, index2 = 0, index1;
        int best = 18, mutate = 38 , crossover = 24;
        Image target; Bitmap targetBitmap;
        int width, height;
        //int numTri = 20; //CHANGE NUM_TRIANGLES in Individual class
        double mutateProb = .50;      //between .01 and .50
        int pointLimit = 20, hueLimit = 40;
        //double crossoverProb = .35; //between .25 and .75
        int parentA, parentB; bool elitism = true;

        int imageCount = 0;
        int numGen = 10, genCount = 0;
        DateTime start, current, end; TimeSpan timeElapsed = TimeSpan.Zero, runningTime; bool first;

        string file, folderPath;
        CancellationTokenSource cancel;
        public Form2()
        {
            InitializeComponent();

            tbBest.Text = best.ToString();
            tbM.Text = mutate.ToString();
            tbC.Text = crossover.ToString();
            string path = "C:\\Users\\alexa\\OneDrive\\Desktop\\Capstone26\\code\\genetic algorithm\\selection\\Selection\\snoop.bmp";
            target = Image.FromFile(path);
            pbGen1.Image = target;
            targetBitmap = (Bitmap)target;
            width = targetBitmap.Width; height = targetBitmap.Height;
            Console.WriteLine("width: " + width);
            genCur = new Individual[size];
            for (int i = 0; i < size; i++)
            {
                genCur[i] = new Individual(width, height, targetBitmap);
            }
            SortArray(genCur);
            for (int i = 0; i < size; i++)
            {
                Console.Write(genCur[i].Score + " ");
            }
            pbGen2.Image = genCur[0].TheImage;

            //create a folder to store every 50th best image
            string dateTime = DateTime.Now.ToString("yyyy-MM-dd");
            string folderName = Path.GetFileNameWithoutExtension(path) + dateTime;
            folderPath = Path.Combine(Environment.CurrentDirectory, folderName);
            Directory.CreateDirectory(folderPath);

            file = Path.GetFileNameWithoutExtension(path);
            Console.WriteLine("file without extension: " + file + DateTime.Now.ToString());
        }
        private async void generationsClick(object sender, EventArgs e)
        {
            gbxPolygons.Enabled = false;

            //genCur = new Individual[size];
            //for (int i = 0; i < size; i++)
            //{
            //    genCur[i] = new Individual(width, height, targetBitmap);
            //}
            //SortArray(genCur);
            //for (int i = 0; i < size; i++)
            //{
            //    Console.Write(genCur[i].Score + " ");
            //}
            //pbGen2.Image = genCur[0].TheImage;


            if (first)
            {
                start = DateTime.Now;
                first = false;
            }
            IProgress<int> progress = new Progress<int>(percent =>
            {
                pBar.Value = percent;
                statusTb.Text = percent.ToString();
            });

            genBtn.Enabled = false;
            cancel = new CancellationTokenSource();
            CancellationToken token = cancel.Token;

            numGen = Convert.ToInt32(tbxGenNum.Text);
            pBar.Maximum = numGen;
            pointLimit = Convert.ToInt32(tbxPoint.Text);
            hueLimit = Convert.ToInt32(tbxHue.Text);
            start = DateTime.Now;
            Task running = RunningAlg(progress, token);
            await running;

            genBtn.Enabled = true;
            current = DateTime.Now;

            pbGen2.Image = genCur[0].TheImage;
            scoreTb.Text = genCur[0].Score.ToString();

            timeElapsed = (current - start);
            timeTb.Text = timeElapsed.ToString();
            runningTime += timeElapsed;
            tbxFinal.Text = runningTime.ToString();
 
            textBox3.Text = imageCount.ToString();  
        }
        public async Task RunningAlg(IProgress<int> progress, CancellationToken token)
        {
            for (int i = 0; i < numGen; i++)
            {
                genNext = new Individual[size];
                //Console.Write("for i = " + i + " ");
                genTb.Text = genCount.ToString();
                index2 = 0;
                for (int j = 0; j < best; j++)
                {
                    genNext[index2] = new Individual(genCur[index2], width, height);
                    index2++;
                }
                for (int k = 0; k < mutate; k++)
                {
                    //toMutate = RndNumGen.Next(1, numTri - 1);
                    if (elitism)
                    { index1 = RndNumGen.Next(0, (int)(size * .6) ); } //do this to get any random individual to mutate }
                    else { index1 = RndNumGen.Next(0, size - 1); }//do this to get any random individual to mutate}
                    
                    //Console.Write("index, mutant: " + index1 + " " + "toMutate: " + toMutate + " ");
                    genNext[index2] = new Individual(genCur[index1], width, height);
                    genNext[index2].mutateIndividual(width, height, target, mutateProb, pointLimit, hueLimit);
                    index2++;
                }
                for (int l = 0; l < crossover; l++)
                {
                    genNext[index2] = new Individual(width, height);
                    if (elitism)
                    { SelectParentsElitism(); }
                    else { SelectParents(); }
                    genNext[index2].crossoverIndividual(genCur[parentA], genCur[parentB], target);
                    index2++;
                }
                token.ThrowIfCancellationRequested();

                await Task.Delay(1); //changed from 10 to 1
                //yield, Dr. McVey
                if (progress != null)
                {
                    progress.Report(i);
                }
                SortArray(genNext);

                if (genCount % 50 == 0)
                {
                    string fileName = genCount + "_" + genNext[0].Score + ".png";
                    string fullPath = Path.Combine(folderPath, fileName);

                    genNext[0].TheImage.Save(fullPath, ImageFormat.Png);
                }
                if (genCount % 20 == 0)
                {
                    pbGen2.Image = genNext[0].TheImage;
                    scoreTb.Text = genNext[0].Score.ToString();
                }
                NextGeneration(genNext, genCur, size);
                genCount++;
            }
            for (int i = 0; i < size; i++)
            {
                Console.Write(genCur[i].Score.ToString() + " ");
            }
            Console.WriteLine();
        }
        private void endClick(object sender, EventArgs e)
        {
            end = DateTime.Now;
            runningTime += (end - start);
            tbxFinal.Text = runningTime.ToString();
        }
        private void cancelClick(object sender, EventArgs e)
        {
            cancel.Cancel();
        }
        private void generateClick(object sender, EventArgs e)
        {
            //copy best
            //copy mutant, then call mutate
            //copy crossover, then call crossover
            //Console.WriteLine("entering generateClick");
            genNext = new Individual[size];
            //Console.WriteLine("before best loop");
            for(int i = 0; i < best; i++)
            {
                genNext[index2] = new Individual(genCur[index2], width, height);
                index2++;
            }
            //Console.WriteLine("before mutate loop");
            for(int j = 0; j < mutate; j++)
            {
                //toMutate = RndNumGen.Next(0, numTri-1);
                index1 = RndNumGen.Next(0, size-1); //do this to get any random individual to mutate
                genNext[index2] = new Individual(genCur[index1], width, height);
                genNext[index2].mutateIndividual2(width, height, target, mutateProb);
                index2++;
            }
            //Console.WriteLine("before crossover loop");
            for(int k = 0; k < crossover; k++)
            {
                genNext[index2] = new Individual(width, height);
                SelectParents();
                //genNext[index2].crossoverIndividual(genCur[parentA], genCur[parentB], target, crossoverProb);
                index2++;
            }
            //Console.WriteLine("done...");
            pbGen1.Image = genCur[imageCount].TheImage;
            pbGen2.Image = genNext[imageCount].TheImage;
            //SortArray(gen2);
        }
        private void scoreClick(object sender, EventArgs e)
        {
            for (int i = 0; i < size; i++)
            {
                Console.WriteLine("check" + i + " " + "score: " + genCur[i].Score);
            }
        }
        private void SelectParentsElitism()
        {
            parentA = RndNumGen.Next(0, (int)(size * .6));
            parentB = RndNumGen.Next(0, (int)(size * .6));
            while (parentB == parentA)
                parentB = RndNumGen.Next(0, (int)(size * .6));
            //Console.WriteLine("parentA index: " + parentA + " " + "parentB index: " + parentB + " ");
        }
        private void SelectParents()
        {
            parentA = RndNumGen.Next(0, size);
            parentB = RndNumGen.Next(0, size);
            while(parentB == parentA)
                parentB = RndNumGen.Next(0, size);
            //Console.WriteLine("parentA index: " + parentA + " " + "parentB index: " + parentB + " ");
        }
        private void prevClick(object sender, EventArgs e)
        {
            imageCount--;
            if (imageCount >= 0)
            {
                next.Enabled = true;
                pbGen1.Image = genCur[imageCount].TheImage;
                textBox1.Text = genCur[imageCount].Score.ToString();
                pbGen2.Image = genNext[imageCount].TheImage;
                textBox2.Text = genNext[imageCount].Score.ToString();
            }
            else
                prev.Enabled = false;
        }
        private void nextClick(object sender, EventArgs e)
        {
            imageCount++;
            if(imageCount < size)
            {
                prev.Enabled = true;
                pbGen1.Image = genCur[imageCount].TheImage;
                textBox1.Text = genCur[imageCount].Score.ToString();
                pbGen2.Image = genNext[imageCount].TheImage;
                textBox2.Text = genNext[imageCount].Score.ToString();
            }
            else
                next.Enabled = false;
        }
        private void SortArray(Individual[] array)
        {
            Array.Sort(array, (x, y) => x.Score.CompareTo(y.Score));
            //for (int i = 0; i < size; i++)
            //{
            //    //Console.WriteLine("in sort function" + i + " " + "score: " + array[i].Score);
            //}
        }
        private void NextGeneration(Individual[] toCopy, Individual[] toReplace, int size)
        {
            Array.Copy(toCopy, toReplace, size);
        }
    }
}
