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

namespace Crossover
{
    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 = 4;
        /****************************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.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: score
        //PARAMETERS: the target 'Image'
        //PURPOSE: The function 'scoreV5' 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;

            Console.WriteLine("scoreIndividual, width: " + width);
            Console.WriteLine("scoreIndividual, height: " + 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));
                    //Console.WriteLine("current score: " + score.ToString());
                }
            }
            this.Score = score;
            Console.WriteLine("End score: " + this.Score);
        }
        /*********************************************** TESTING ZONE ***********************************************/
        //FUNCTION: crossoverV1
        //PARAMETERS: 2 'Individual' parentA and parentB
        //PURPOSE: The function 'crossoverV1' is called on an 'Individual' (the child). The child 'Individual's' 'triangles' are set
        //         by the following operation: parentA.triangles * parentB.triangles, which is then used to create the Bitmap 'image'
        /*
         *          WARNING: The following code allows duplicate triangles (this is not ideal...)
         */
        public void crossoverV1(Individual parentA, Individual parentB)
        {
            Console.WriteLine("entering crossoverV1");                  //DATE: 3.3.2026
            int half = NUM_TRIANGLES / 2;                               //STATUS: .5 successful
            int index;
            for (int i = 0; i < half; i++)
            {
                index = RndNumGen.Next(0, NUM_TRIANGLES);
                this.Triangles[i] = parentA.Triangles[index];
                Console.WriteLine("parentA, index: " + index);
            }
            for (int j = half; j < NUM_TRIANGLES; j++)
            {
                index = RndNumGen.Next(0, NUM_TRIANGLES);
                this.Triangles[j] = parentB.Triangles[index];
                Console.WriteLine("parentB, index: " + index);
            }
            this.createBitmap();
            Console.WriteLine("leaving crossoverV1");
        }
        //FUNCTION: crossoverV2
        //PARAMETERS: 2 'Individual' parentA and parentB
        //PURPOSE: The function 'crossoverV2' is called on an 'Individual' (the child). The child 'Individual's' 'triangles' are set
        //         by the following operation: parentA.triangles * parentB.triangles, which is then used to create the Bitmap 'image'
        //         The triangles to crossover from each parent are determined by a Random Number Generator, representing an index. 
        //         This version does NOT allow duplicate triangles!
        public void crossoverV2(Individual parentA, Individual parentB)
        {
            Console.WriteLine("entering crossoverV2");
            int[] parentA_indices;
            int[] parentB_indices;                                      //DATE: 3.3.2026 (Updated: 3.31)
            int half = NUM_TRIANGLES / 2;                               //STATUS: Successful
            int index;
            if ((NUM_TRIANGLES % 2) != 0)
            {
                parentA_indices = new int[half + 1];
                parentB_indices = new int[half];
            }
            else
            {
                parentA_indices = new int[half];
                parentB_indices = new int[half];
            }
            int i = 0;
            while (i < parentA_indices.Length)
            {
                index = RndNumGen.Next(0, NUM_TRIANGLES);
                if (!(Array.Exists(parentA_indices, element => element == index)))
                {
                    Console.WriteLine("i: " + i);
                    parentA_indices[i] = index;
                    Console.WriteLine("parentA, index: " + index);
                    i++;
                }
            }
            int j = 0;
            while (j < parentB_indices.Length)
            {
                index = RndNumGen.Next(0, NUM_TRIANGLES);
                if (!(Array.Exists(parentB_indices, element => element == index)))
                {
                    Console.WriteLine("j: " + j);
                    parentB_indices[j] = index;
                    Console.WriteLine("parentB, index: " + index);
                    j++;
                }
            }
            int childIndex = 0;
            for (int k = 0; k < parentA_indices.Length; k++)
            {
                this.Triangles[childIndex] = parentA.Triangles[parentA_indices[k]];
                childIndex++;
            }
            for (int l = 0; l < parentB_indices.Length; l++)
            {
                this.Triangles[childIndex] = parentB.Triangles[parentB_indices[l]];
                childIndex++;
            }
            this.createBitmap();
            Console.WriteLine("leaving crossoverV2");
        }
        //FUNCTION: crossoverV4
        //PARAMETER: 2 Individuals [parentA, parentB]
        //PURPOSE: The function crossoverV4 uses RndNumGen as the crossoverPoint (indicating where parentA crossover stops and
        //         parentB crossover begins). This function gets called on an Individual
        public void crossoverV4(Individual parentA, Individual parentB)
        {
            Console.WriteLine("entering crossoverV4...");
            int crossoverPoint = RndNumGen.Next(0, NUM_TRIANGLES-1);
            Console.WriteLine("crossoverPoint: " + 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();
            Console.WriteLine("leaving crossoverV4...");
        }

        public void crossoverIndividual(Individual parentA, Individual parentB)
        {
            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();
            //this.scoreIndividual(target);
        }
    }

}
