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

namespace Mutation
{
    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);
            }
            Console.WriteLine(image.ToString());
        }
        //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: mutateV0
        //PARAMETERS: width and height of Bitmap image
        //PURPOSE: The function 'mutateV0' will replace ONE, and only ONE, triangle in the 'Individual' by calling Triangle functions
        //         generatePoints and generateColor. 
        public void mutateV0(int width, int height)                  //DATE: 3.3.2026
        {                                                           //STATUS: Successful
            Console.WriteLine("entering mutateV1");
            int index = RndNumGen.Next(0, NUM_TRIANGLES - 1);
            Console.WriteLine("index: " + index);

            this.Triangles[index].GeneratePoints(width, height);
            this.Triangles[index].GenerateColor();

            this.createBitmap();
            Console.WriteLine("leaving mutateV2");
        }
        //FUNCTION: mutateV2
        //PARAMETERS: width and height of Bitmap image, toMutate (number of triangles to mutate)
        //PURPOSE: The function 'mutateV2' will mutate 'toMutate' number of triangles [the function will make sure toMutate is not less than or greater than
        //         NUM_TRIANGLES]. The triangles will be mutated by calling the Triangle functions generatePoints and generateColor
        public void mutateV2(int width, int height, int toMutate)
        {
            Console.WriteLine("entering mutateV2... toMutate: " + toMutate);
            if (toMutate <= 0 || toMutate >= NUM_TRIANGLES)
            {
                Console.WriteLine("mutateV2: do better...change toMutate");
            }
            else
            {
                Console.WriteLine("toMutate is ok!");
                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++)
                {
                    this.Triangles[j].GeneratePoints(width, height);
                    this.Triangles[j].GenerateColor();
                }
                this.createBitmap();
            }
            Console.WriteLine("leaving toMutate");
        }
        public void mutateIndividual(int width, int height, 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);
        }
    }
}
