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

namespace Selection
{
    internal class Triangle
    {
        /******************************GLOBAL CONSTANTS************************************/
        //NAME: POINTS_SIZE
        //TYPE: Integer
        //PURPOSE: 'POINTS_SIZE' is the size of the 'points' array
        private const int POINTS_SIZE = 3;
        //NAME: MIN_RGB
        //TYPE: Integer
        //PURPOSE: 'MIN_RGB' is the minimum value possible for an RGB value
        private const int MIN_RGB = 0;
        //NAME: MAX_RGB
        //TYPE: Integer
        //PURPOSE: 'MAX_RGB' is the maximum value possible for an RGB value
        private const int MAX_RGB = 255;
        //NAME: MIN_A
        //TYPE: Integer
        //PURPOSE: 'MIN_A' is the minimum value possible for a color's alpha
        private const int MIN_A = 50;
        //NAME: MAX_A
        //TYPE: Integer
        //PURPOSE: 'MAX_A' is the maximum value possible for a color's alpha
        private const int MAX_A = 130;
        /********************************DATA MEMBERS**************************************/
        //NAME: points
        //TYPE: Point, array; LENGTH: 3
        //PURPOSE: 'point' stores three points to draw a triangle
        private Point[] points;
        //NAME: color
        //TYPE: Color
        //PURPOSE: 'color' stores an ARGB value for a triangle
        private Color color;
        /**********************************PROPERTIES**************************************/
        //PROPERTY: Points, get
        //PURPOSE: Accessor function, returns the array of points of a triangle
        public Point[] Points
        {
            get { return points; }
            set { points = value; }
        }
        //PROPERTY: Color, set; Color, get
        //PURPOSE: Mutator function, sets the color object of a triangle
        //         Accessor function, returns the color object of a triangle
        public Color Color
        {
            set { color = value; }
            get { return color; }
        }
        /**********************************FUNCTIONS***************************************/
        //FUNCTION: Triangle, constructor
        //PARAMETERS: Width and Height of the Bitmap image
        //PURPOSE: The following function is the Triangle constructor. It allocates memory for its data members and calls functions to
        //         generate points and generate the color for an individual triangle
        public Triangle(int width, int height)
        {
            points = new Point[POINTS_SIZE];
            color = new Color();

            this.GeneratePoints(width, height);
            this.GenerateColor();
        }
        //FUNCTION: GeneratePoints 
        //PARAMETERS: Width and Height of the Bitmap Image
        //PURPOSE: The function 'GeneratePoints' will fill the contents of a triangle's Point array with randomly generated value within a specified range
        public void GeneratePoints(int width, int height)
        {
            int point_X, point_Y;
            Point temp_point;
            int temp_X, temp_Y;
            //first point
            point_X = RndNumGen.Next(0, width);
            point_Y = RndNumGen.Next(0, height);
            temp_point = new Point(point_X, point_Y);
            points[0] = temp_point;

            for (int i = 1; i < POINTS_SIZE; i++)
            {
                int min_X = (int)((-1) * (width / 2));
                int max_X = (int)((width / 2));
                int x = RndNumGen.Next(min_X, max_X);
                temp_X = point_X + x;
                if(temp_X < 0) { temp_X = 0; }
                else if (temp_X >= width) { temp_X = width - 1; }

                int min_Y = (int)((-1) * (height / 2));
                int max_Y = (int)((height / 2));
                int y = RndNumGen.Next(min_Y, max_Y);
                temp_Y = point_Y + y;
                if (temp_Y < 0) { temp_Y = 0; }
                else if (temp_Y >= height) { temp_Y = height - 1; }

                temp_point = new Point(temp_X, temp_Y);
                points[i] = temp_point;
            }
        }
        //FUNCTION: GenerateColor
        //PARAMETERS: --
        //PURPOSE: The function GenerateColor will randomly generate values within the specified range [0-255] to represent ALPHA, RED, GREEN, BLUE (color) values
        public void GenerateColor()
        {
            int alpha, red, green, blue;

            alpha = RndNumGen.Next(MIN_A, MAX_A);
            red = RndNumGen.Next(MIN_RGB, MAX_RGB);
            green = RndNumGen.Next(MIN_RGB, MAX_RGB);
            blue = RndNumGen.Next(MIN_RGB, MAX_RGB);
            this.Color = Color.FromArgb(alpha, red, green, blue);
        }
        //FUNCTION: Mutate
        //PARAMETERS: width and height of bitmap image, pointLimit (to determine the range of how much to move an x or y coordinate),
        //            hueLimit (to determine the range by how much to change the ARGB values )
        //PURPOSE: The function 'mutate' will do the following on a triangle's points: for each point do a coin flip (0 || 1)...1: leave
        //         the point be (do not make any changes); 0: mutate the point by tweaking the x and y coordinates by using the following
        //         formula...observe: pointShift = RndNumGen.Next((-1*pointLimit), (pointLimit+1)). The function will do the following on the triangle's
        //         color: for each value of the color (alpha, red, green, blue) do a coin flip (0 || 1)...1: leave the value be (do not make
        //         any changes); 0: mutate the value by tweaking the alpha, red, green, blue values by using the following formula...observe:
        //         hueShift = RndNumGen.Next((-1*colorLimit), (colorLimit+1)). The function will also make sure the values for the points and 
        //         the color values are within their respective ranges
        public void Mutate(int width, int height, int pointLimit, int hueLimit)
        {
            int coinFlip, flip = 2;
            int hueShift, alpha, red, green, blue;
            int pointShift, x, y;

            coinFlip = RndNumGen.Next(flip);      //alpha
            if (coinFlip == 0)
            {
                hueShift = RndNumGen.Next((-1 * hueLimit), (hueLimit + 1));
                alpha = (this.Color.A + hueShift);

                if(alpha < MIN_A)
                { alpha = MIN_A; }

                if(alpha > MAX_A)
                { alpha = MAX_A; }
            }
            else
            { alpha = this.Color.A; }

            coinFlip = RndNumGen.Next(flip);    //red
            if (coinFlip == 0)
            {
                hueShift = RndNumGen.Next((-1 * hueLimit), (hueLimit + 1));
                red = (this.Color.R + hueShift);

                if (red < MIN_RGB)
                { red = MIN_RGB; }

                if (red > MAX_RGB)
                { red = MAX_RGB; }
            }
            else
            { red = this.Color.R; }

            coinFlip = RndNumGen.Next(flip);    //green
            if (coinFlip == 0)
            {
                hueShift = RndNumGen.Next((-1 * hueLimit), (hueLimit + 1));
                green = (this.Color.G + hueShift);

                if (green < MIN_RGB)
                { green = MIN_RGB; }

                if (green > MAX_RGB)
                { green = MAX_RGB; }
            }
            else
            { green = this.Color.G; }

            coinFlip = RndNumGen.Next(flip);    //blue
            if (coinFlip == 0)
            {
                hueShift = RndNumGen.Next((-1 * hueLimit), (hueLimit + 1));
                blue = (this.Color.B + hueShift);

                if (blue < MIN_RGB)
                { blue = MIN_RGB; }

                if (blue > MAX_RGB)
                { blue = MAX_RGB; }
            }
            else
            { blue = this.Color.B; }
            this.Color = Color.FromArgb(alpha, red, green, blue);

            //shift points
            for(int i = 0; i < POINTS_SIZE; i++)
            {
                coinFlip = RndNumGen.Next(flip);
                if(coinFlip == 0)
                {
                    x = this.Points[i].X;
                    pointShift = RndNumGen.Next((-1 * pointLimit), (pointLimit+1));
                    x += pointShift;
                    if (x < 0) { x = 0; }
                    if (x > width) { x = (width - 1); }

                    y = this.Points[i].Y;
                    pointShift = RndNumGen.Next((-1 * pointLimit), (pointLimit + 1));
                    y += pointShift;
                    if (y < 0) { y = 0; }
                    if (y > height) { y = (height - 1); }

                    this.Points[i].X = x; this.Points[i].Y = y;
                }
            }
        }
    }
}
