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

namespace final_project
{
    internal class Individual
    {
        /****************************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; }  get { return triangles; } }
        //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; } get { return score; } }
        //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; } }
        /****************************FUNCTIONS******************************/
        //FUNCTION: Individual, constructor
        //PARAMETERS: width and height of Bitmap image, number of triangles per Individual, target 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, int num_triangles, Image target)
        {
            triangles = new Triangle[num_triangles];
            image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);

            generateTriangles(width, height, num_triangles);
            createBitmap(num_triangles);
            scoreIndividual(target);
        }
        //FUNCTION: Individual, copy constructor
        //PARAMETERS: an Individual to copy, width and height of Bitmap image, number of triangles
        //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, int num_triangles)
        {
            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(num_triangles);
        }
        //FUNCTION: generateTriangles
        //PARAMETERS: width and height of Bitmap image, number of triangles per individual
        //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, int num_Triangles)
        {
            for (int i = 0; i < num_Triangles; i++)
            {
                triangles[i] = new Triangle(width, height);
            }
        }
        //FUNCTION: createBitmap 
        //PARAMETERS: number of triangles per individual
        //PURPOSE: The function 'createBitmap' draws on the Bitmap 'image' using the 'triangles' array of an Individual
        public void createBitmap(int num_triangles)
        {
            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 every 5 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;
            if(width > 250)
            {
                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));
                    }
                }
            }
            else
            {
                for (i = 0; i < width; i++)       //every other pixels
                {
                    for (j = i % 1; j < height; j += 2)    //every other 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;
        }
        //FUNCTION: mutateIndividual
        //PARAMETERS: width and height of Bitmap image, target Image, number of triangles per individual, point and color (hue) limits
        //            for triangle mutation
        //PURPOSE: The function 'mutateIndividuals' will mutate all of the triangles.
        //         The triangles will be mutated by calling the Triangle function Mutate.
        //         At the end, the function will create the bitmap for that Individual and score it.
        public void mutateIndividual(int width, int height, Image target, int num_triangles, int pointLimit, int hueLimit)
        {
            for (int i = 0; i < num_triangles; i++)
            {
                this.Triangles[i].Mutate(width, height, pointLimit, hueLimit);
            }
            this.createBitmap(num_triangles);
            this.scoreIndividual(target);
        }
        //FUNCTION: crossoverIndividual
        //PARAMETER: 2 Individuals [parentA, parentB], target Image, number of triangles per Individual
        //PURPOSE: The function 'crossoverIndividual' is called on an Individual to fill it's Triangle array with Triangles from parentA and parentB.
        //         The function does so in the following way: for each triangle index, flip a coin: if 0, copy the triangle from parentA; else if 1,
        //         copy the triangle from parentB. Lastly, the function will shuffle a percentage of the triangles (swapping two triangles 'x' number of times)
        public void crossoverIndividual(Individual parentA, Individual parentB, Image target, int num_triangles)
        {
            int coinFlip, flip = 2;
            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(num_triangles);
            this.scoreIndividual(target);
        }
    }
}
