using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Selection
{
    internal class Individual
    {
        /*************************GLOBAL CONSTANTS**************************/
        //NAME: NUM_TRIANGLES
        //TYPE: Integer
        //PURPOSE: 'NUM_TRIANGLES' specifies the number of triangles per individual. This value is used as the size of 'triangles'
        private const int NUM_TRIANGLES = 150;
        /****************************DATA MEMBERS***************************/
        //NAME: triangles
        //TYPE: Triangle, array; LENGTH
        //PUPOSE: 'triangles' stores a specified number of triangles for a given individual
        private Triangle[] triangles;
        //NAME: score
        //TYPE: Integer
        //PURPOSE: 'score' stores the score of an individual, which is determined by the 'Scoring' function.
        //          The idea of 'score' - used to identify better solutions from not-so-good solutions. 
        //          For more details on how 'score' is calculated, take a look at the 'Scoring' function
        private int score;
        //NAME: image
        //TYPE: Bitmap  
        //PURPOSE: 'image' is the result of 'triangles' drawn on a Bitmap object. 'image' is also used by the 'Scoring'
        //          function [in which 'image' is compared to the original image to calculate 'score']
        private Bitmap image;
        //NAME: graphics
        //TYPE: Graphics
        //PURPOSE: The 'graphics' data member is the Graphics object created from a Bitmap image
        //private Graphics graphics;
        /***************************PROPERTIES******************************/
        //PROPERTY: Triangle, set; Triangles, get
        //PURPOSE: Mutator function, sets the array 'triangles' for an individual
        //         Accessor function, returns the array 'triangles' for an individual
        public Triangle[] Triangles
        {
            set { triangles = value; }  //alex, use for crossover
            get { return triangles; }   //alex, use for drawing an individual's bitmap
        }
        //PROPERTY: Score, set; Score, get
        //PURPOSE: Mutator function, sets the value of 'score'
        //         Accessor function, returns the value of 'score'
        public int Score
        {
            set { score = value; }  //alex, use when running the 'scoring' function
            get { return score; }   //alex, use for sorting the array of triangles; selection method (to compare)
        }
        //PROPERTY: Bitmap, set; Bitmap, get
        //PURPOSE: Mutator function, sets the bitmap image for 'image'
        //         Accessor function, returns the bitmap image 'image' of an individual
        public Bitmap TheImage
        {
            set { image = value; }
            get { return image; }   //alex, use when running the 'scoring' function
        }
        /****************************FUNCTIONS******************************/
        //FUNCTION: Individual, constructor
        //PARAMETERS: width and height of Bitmap image
        //PURPOSE: The Indiviudal constructor allocates memory for its data members, and calls the functions to generate triangles,
        //         create a Bitmap image from the triangles, and score the Individual
        public Individual(int width, int height)
        {
            triangles = new Triangle[NUM_TRIANGLES];
            image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);

            generateTriangles(width, height);
            createBitmap();
        }
        //FUNCTION: Individual, constructor2
        //PARAMETERS: width and height of Bitmap image
        //PURPOSE: The Indiviudal constructor allocates memory for its data members, and calls the functions to generate triangles,
        //         create a Bitmap image from the triangles, and score the Individual
        public Individual(int width, int height, Image target)
        {
            triangles = new Triangle[NUM_TRIANGLES];
            image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);

            generateTriangles(width, height);
            createBitmap();
            scoreIndividual(target);
        }
        //FUNCTION: Individual, copy constructor
        //PARAMETERS: an Individual, width and height of Bitmap image
        //PURPOSE: The Individual copy constructor will copy the data from an existing Indiviual to a new Individual
        //         This is done to avoid shallow copies of Individual objects
        public Individual(Individual toCopy, int width, int height)
        {
            //Triangle[] copyTriangles = new Triangle[NUM_TRIANGLES];
            //for (int i = 0; i < NUM_TRIANGLES; i++)
            //{
            //    copyTriangles[i] = toCopy.Triangles[i];
            //}
            //this.Triangles = copyTriangles;
            this.Triangles = new Triangle[NUM_TRIANGLES];
            for(int i = 0; i < NUM_TRIANGLES; i++)
            {
                this.Triangles[i] = new Triangle(width, height);
                this.Triangles[i].Points = new Point[3];
                for(int j = 0; j < 3; j++)
                {
                    this.Triangles[i].Points[j].X = toCopy.Triangles[i].Points[j].X;
                    this.Triangles[i].Points[j].Y = toCopy.Triangles[i].Points[j].Y;
                }
                this.Triangles[i].Color = toCopy.Triangles[i].Color;
            }
            this.Score = toCopy.Score;
            this.TheImage = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            this.createBitmap();
        }
        //FUNCTION: generateTriangles
        //PARAMETERS: width and height of Bitmap image
        //PURPOSE: The function 'generateTriangles' creates a triangle for every index in the 'triangles' array, by calling the 
        //         'Triangle' constructor. For more details on the Triangle class, see file: Triangle.cs
        public void generateTriangles(int width, int height)
        {
            for (int i = 0; i < NUM_TRIANGLES; i++)
            {
                triangles[i] = new Triangle(width, height);
            }
        }
        //FUNCTION: createBitmap 
        //PARAMETERS: --
        //PURPOSE: The function 'createBitmap' draws on the Bitmap 'image' using the 'triangles' array
        public void createBitmap()
        {
            SolidBrush brush = new SolidBrush(Color.CornflowerBlue); //for funzies
            Graphics graphics = Graphics.FromImage(image);
            graphics.Clear(Color.White);

            for (int i = 0; i < NUM_TRIANGLES; i++)
            {
                graphics.CompositingMode = CompositingMode.SourceOver;
                brush = new SolidBrush(triangles[i].Color);

                graphics.FillPolygon(brush, triangles[i].Points);
            }
        }
        //FUNCTION: scoreIndividual
        //PARAMETERS: the target 'Image'
        //PURPOSE: The function 'scoreIndividual' is called on an 'Individual' and takes a target image - a cartoon like image and to compute the  
        //         Individual's score. The function uses the Red, Green, Blue (RGB) values of pixels to compute the score AND updates the 'Individual's' score
        //         observe: (|target.RED - toScore.RED| + |target.GREEN - toScore.GREEN| + |target.BLUE - toScore.BLUE|)
        //***NOTE: the images must be the same pixel size
        public void scoreIndividual(Image target)
        {
            Bitmap targetBitmap = (Bitmap)target;
            Bitmap toScoreBitmap = this.TheImage;
            int width = targetBitmap.Width;
            int height = targetBitmap.Height;

            Color targetPixel, toScorePixel;

            int score = 0;
            int i, j;
            for (i = 0; i < width; i++)       //every 5 pixels
            {
                for (j = i % 5; j < height; j+=5)    //every 5 pixels
                {
                    targetPixel = targetBitmap.GetPixel(i, j);
                    toScorePixel = toScoreBitmap.GetPixel(i, j);

                    score += (Math.Abs(targetPixel.R - toScorePixel.R) + Math.Abs(targetPixel.G - toScorePixel.G) + Math.Abs(targetPixel.B - toScorePixel.B));
                }
            }
            this.Score = score;
        }
        public void scoreIndividual3(Image target)
        {
            Bitmap targetBitmap = (Bitmap)target;
            Bitmap toScoreBitmap = this.TheImage;
            int width = targetBitmap.Width;
            int height = targetBitmap.Height;

            Color targetPixel, toScorePixel;

            int score = 0;

            for (int y = 0; y < height; y++)       //every other pixel
            {
                for (int x = 0; x < width; x+=2)    //every other pixel
                {
                    targetPixel = targetBitmap.GetPixel(x, y);
                    toScorePixel = toScoreBitmap.GetPixel(x, y);

                    score += (Math.Abs(targetPixel.R - toScorePixel.R) + Math.Abs(targetPixel.G - toScorePixel.G) + Math.Abs(targetPixel.B - toScorePixel.B));
                }
            }
            this.Score = score;
        }
        public void scoreIndividual2(Image target)
        {
            Bitmap targetBitmap = (Bitmap)target;
            Bitmap toScoreBitmap = this.TheImage;
            int width = targetBitmap.Width;
            int height = targetBitmap.Height;

            Color targetPixel, toScorePixel;

            int score = 0;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    targetPixel = targetBitmap.GetPixel(x, y);
                    toScorePixel = toScoreBitmap.GetPixel(x, y);

                    score += (Math.Abs(targetPixel.R - toScorePixel.R) + Math.Abs(targetPixel.G - toScorePixel.G) + Math.Abs(targetPixel.B - toScorePixel.B));
                }
            }
            this.Score = score;
        }
        //FUNCTION: mutateIndividual
        //PARAMETERS: width and height of Bitmap image, target Image, probability (precentage of triangles to mutate)
        //PURPOSE: The function 'mutateV2' will mutate 'toMutate' number of triangles - calculated using a probabilty.
        //         The triangles will be mutated by calling the Triangle functions generatePoints and generateColor.
        //         At the end, the function will score the Individual.
        public void mutateIndividual(int width, int height, Image target, double probability, int pointLimit, int hueLimit)
        {
            for(int i = 0; i < NUM_TRIANGLES; i++)
            {
                this.Triangles[i].Mutate(width, height, pointLimit, hueLimit);
            }
            this.createBitmap();
            this.scoreIndividual(target);
        }
        public void mutateIndividual3(int width, int height, Image target, double probability, int pointLimit, int hueLimit)
        {
            //if (toMutate < 0 || toMutate >= NUM_TRIANGLES)
            //{
            //    Console.WriteLine("mutate: do better...change toMutate");
            //}
            //else
            //{
            //int toMutate = (int)(probability * (NUM_TRIANGLES + 1));
            int toMutate = (RndNumGen.Next(1, (int)(probability * (NUM_TRIANGLES + 1))));
            //ex: 20 triangles and prob of .35...toMutate can fall between 1 and 7 (20*.35)...this rounds too :)
            int[] triToMutate = new int[toMutate];
            int i = 0;
            int index = 0;
            while (i < toMutate)
            {
                index = RndNumGen.Next(0, NUM_TRIANGLES - 1);
                if (!(Array.Exists(triToMutate, element => element == index)))
                {
                    triToMutate[i] = index;
                    //Console.WriteLine("triToMutate, index: " + index);
                    i++;
                }
            }
            for (int j = 0; j < triToMutate.Length; j++)
            {
                //following code, generates a WHOLE new triangle
                //this.Triangles[j].GeneratePoints(width, height);
                //this.Triangles[j].GenerateColor();
                //following code, mutates a single triangles
                this.Triangles[i].Mutate(width, height, pointLimit, hueLimit);
            }
            this.createBitmap();
            this.scoreIndividual(target);
            //}
        }
        public void mutateIndividual2(int width, int height, Image target, double probability)
        {
            //if (toMutate < 0 || toMutate >= NUM_TRIANGLES)
            //{
            //    Console.WriteLine("mutate: do better...change toMutate");
            //}
            //else
            //{
            //int toMutate = (int)(probability * (NUM_TRIANGLES + 1));
            int toMutate = (RndNumGen.Next(1, (int)(probability * (NUM_TRIANGLES + 1))));
            //ex: 20 triangles and prob of .35...toMutate can fall between 1 and 7 (20*.35)...this rounds too :)
            int[] triToMutate = new int[toMutate];
            int i = 0;
            int index = 0;
            while (i < toMutate)
            {
                index = RndNumGen.Next(0, NUM_TRIANGLES - 1);
                if (!(Array.Exists(triToMutate, element => element == index)))
                {
                    triToMutate[i] = index;
                    //Console.WriteLine("triToMutate, index: " + index);
                    i++;
                }
            }
            for (int j = 0; j < triToMutate.Length; j++)
            {
                //following code, generates a WHOLE new triangle
                this.Triangles[j].GeneratePoints(width, height);
                this.Triangles[j].GenerateColor();
                //following code, mutates a single triangles
            }
            this.createBitmap();
            this.scoreIndividual(target);
            //}
        }
        //FUNCTION: crossoverIndividual
        //PARAMETER: 2 Individuals [parentA, parentB], target Image, probability (to determine the crossover point)
        //PURPOSE: The function crossoverV4 uses crossoverPoint - calcualted with a probability to indicate where parentA crossover stops and
        //         parentB crossover begins. This function gets called on an Individual and will score it at the end.
        public void crossoverIndividual(Individual parentA, Individual parentB, Image target)
        {
            int coinFlip, flip = 3;
            for(int i = 0; i < NUM_TRIANGLES; i++)
            {
                coinFlip = RndNumGen.Next(flip);
                if(coinFlip == 0)
                { this.Triangles[i] = parentA.Triangles[i]; }
                else
                { this.Triangles[i] = parentB.Triangles[i]; }
            }
            double prop = .40;
            int toShuffle = (int)((NUM_TRIANGLES + 1) * prop);
            int index1, index2; Triangle temp_tri;
            for(int i = 0; i< toShuffle; i++)
            {
                index1 = RndNumGen.Next(NUM_TRIANGLES);
                index2 = RndNumGen.Next(NUM_TRIANGLES);
                while(index2 == index1)
                    index2 = RndNumGen.Next(NUM_TRIANGLES);
                temp_tri = this.Triangles[index1];
                this.Triangles[index1] = this.Triangles[index2];
                this.Triangles[index2] = temp_tri;
            }
            this.createBitmap();
            this.scoreIndividual(target);
        }
        public void crossoverIndividual3(Individual parentA, Individual parentB, Image target)
        {
            int minCrossover = (int)(.25 * (NUM_TRIANGLES + 1));
            int maxCrossover = (int)(.75 * (NUM_TRIANGLES + 1));
            //int crossoverPoint = (int)(probability * (NUM_TRIANGLES + 1));
            int crossoverPoint = RndNumGen.Next(minCrossover, maxCrossover);
            //Console.WriteLine("crossoverPt: " + crossoverPoint + " ");
            int index = 0;
            for (int i = 0; i < crossoverPoint; i++)
            {
                this.Triangles[index] = parentA.Triangles[i];
                index++;
            }
            for (int j = crossoverPoint; j < NUM_TRIANGLES; j++)
            {
                this.Triangles[index] = parentB.Triangles[j];
                index++;
            }
            this.createBitmap();
            this.scoreIndividual(target);
        }
        //original with probability var
        public void crossoverIndividual2(Individual parentA, Individual parentB, Image target, double probability)
        {
            
            //double one = 1.0, rest;
            int crossoverPoint = (int)(probability * (NUM_TRIANGLES + 1));
            //int crossoverPoint = RndNumGen.Next((probability *))
            //Console.WriteLine("crossoverPt: " + crossoverPoint + " ");
            int index = 0;
            for (int i = 0; i < crossoverPoint; i++)
            {
                this.Triangles[index] = parentA.Triangles[i];
                index++;
            }
            for (int j = crossoverPoint; j < NUM_TRIANGLES; j++)
            {
                this.Triangles[index] = parentB.Triangles[j];
                index++;
            }
            this.createBitmap();
            this.scoreIndividual(target);
        }
    }
}
