// Update is called once per frame
    private void Update()
    {
        DebugGUI.LogPersistent("fpsCounter", "FPS: " + (1 / Time.smoothDeltaTime).ToString("F2"));

        DebugGUI.LogPersistent("genCount", "Generation: " + genCount.ToString());

        int activeCars = 0;

        foreach (GameObject item in cars)
        {
            if (!item.GetComponent <CarController>().IsFinished())
            {
                activeCars++;
            }
        }
        DebugGUI.LogPersistent("activeCars", "Active Cars: " + activeCars.ToString());

        for (int i = 0; i < populationSize; i++)
        {
            if (!cars[i].GetComponent <CarController>().IsFinished())
            {
                if (cars[i].GetComponent <CarController>().output.Length != 0)
                {
                    float speed = cars[i].GetComponent <CarController>().currentSpeed;

                    DebugGUI.Graph("bestCarSpeed", speed);

                    speedOMeter.text = speed.ToString("F0") + " MPH";

                    float throttleBrake = cars[i].GetComponent <CarController>().output[1];

                    //DebugGUI.Graph("throttle", Mathf.Clamp(cars[i].GetComponent<CarController>().output[1], 0f, 1f));
                    //DebugGUI.Graph("brake", Mathf.Clamp(cars[i].GetComponent<CarController>().output[2], 0f, 1f));

                    float engine = cars[i].GetComponent <CarController>().output[1];
                    if (engine > 0f)
                    {
                        DebugGUI.Graph("throttle", engine);
                        DebugGUI.Graph("brake", 0f);
                    }
                    else
                    {
                        DebugGUI.Graph("throttle", 0f);
                        DebugGUI.Graph("brake", Mathf.Abs(engine));
                    }

                    //DebugGUI.Graph("throttle", cars[i].GetComponent<CarController>().output[1]);
                    DebugGUI.Graph("steering", cars[i].GetComponent <CarController>().output[0]);

                    float angle = cars[i].GetComponent <CarController>().output[0] * 90f;

                    steeringWheel.transform.rotation = Quaternion.Euler(0f, 0f, -1f * angle);

                    //DebugGUI.Graph("currentRPM", cars[i].GetComponent<CarController>().GetRPM() / 1000f);

                    i = populationSize;
                }
            }
        }

        if (AllFinished())
        {
            timeElapsed = 0f;

            mainCamera.GetComponent <CameraController>().ResetCamera();

            // End of generation training

            cars = cars.OrderByDescending(e => e.GetComponent <CarController>().GetFitness()).ToList();
            //cars = cars.OrderByDescending(e => e.GetComponent<CarController>().GradientSort()).ToList();

            // Graphing
            DebugGUI.Graph("topFitness", cars[0].GetComponent <CarController>().GetFitness());

            // calculate median

            float limit = (populationSize / 2f) - 1f;

            float medianIndex = (2 / 3f) * limit;

            float median;

            if (medianIndex % 1 != 0)
            {
                int temp1 = Mathf.FloorToInt(medianIndex);
                int temp2 = Mathf.CeilToInt(medianIndex);

                median = (cars[temp1].GetComponent <CarController>().GetFitness() + cars[temp2].GetComponent <CarController>().GetFitness()) / 2f;
            }
            else
            {
                median = cars[(int)medianIndex].GetComponent <CarController>().GetFitness();
            }

            TrainingLog data = new TrainingLog
            {
                generation    = genCount,
                topFitness    = cars[0].GetComponent <CarController>().GetFitness(),
                medianFitness = median
            };

            trainingLog.Add(data);

            JSONManager.SaveData(trainingLog.ToArray(), Application.dataPath + "/trainingLog.json");

            DebugGUI.Graph("medianFitness", median);

            // calculate quartiles
            float Q3Index = (1 / 3f) * limit; // 0.25 is used because it's sorted in descending order
            float Q3;
            if (Q3Index % 1 != 0)
            {
                int temp1 = Mathf.FloorToInt(Q3Index);
                int temp2 = Mathf.CeilToInt(Q3Index);

                Q3 = (cars[temp1].GetComponent <CarController>().GetFitness() + cars[temp2].GetComponent <CarController>().GetFitness()) / 2f;
            }
            else
            {
                Q3 = cars[(int)Q3Index].GetComponent <CarController>().GetFitness();
            }

            DebugGUI.Graph("upperQuartile", Q3);

            float Q1Index = limit;
            float Q1;
            if (Q1Index % 1 != 0)
            {
                int temp1 = Mathf.FloorToInt(Q1Index);
                int temp2 = Mathf.CeilToInt(Q1Index);

                Q1 = (cars[temp1].GetComponent <CarController>().GetFitness() + cars[temp2].GetComponent <CarController>().GetFitness()) / 2f;
            }
            else
            {
                Q1 = cars[(int)Q1Index].GetComponent <CarController>().GetFitness();
            }
            DebugGUI.Graph("lowerQuartile", Q1);

            float IQR = Q3 - Q1;

            List <float> speedList = new List <float>();

            foreach (GameObject item in cars)
            {
                speedList.Add(item.GetComponent <CarController>().avgSpeed);
            }
            speedList = speedList.OrderByDescending(e => e).ToList();

            DebugGUI.Graph("medianSpeed", speedList[(populationSize / 2) - 1]);
            DebugGUI.Graph("topSpeed", speedList[0]);

            for (int i = 0; i < populationSize / 2; i++)
            {
                // check for outlier
                float outlier = Q3 + (1.5f * IQR);

                if (cars[0].GetComponent <CarController>().GetFitness() > outlier)
                {
                    cars[(populationSize / 2) + i].GetComponent <CarController>().Mutate(cars[0]);
                }
                else
                {
                    // take best half and delete worst half

                    if (i + 1 == populationSize / 2)
                    {
                        cars[(populationSize / 2) + i].GetComponent <CarController>().Reproduce(cars[i], cars[0]);
                    }
                    else
                    {
                        cars[(populationSize / 2) + i].GetComponent <CarController>().Reproduce(cars[i], cars[i + 1]);
                    }
                }

                //cars[(populationSize / 2) + i].GetComponent<CarController>().ResetCar();
                //cars[i].GetComponent<CarController>().ResetCar();
            }

            // reset all cars

            foreach (GameObject item in cars)
            {
                item.GetComponent <CarController>().ResetCar();
            }

            // saving best network to file

            if (settings.saveBestNetwork)
            {
                cars[0].GetComponent <CarController>().SaveData();
            }

            genCount++;
        }
        else
        {
            for (int i = 0; i < populationSize; i++)
            {
                if (!cars[i].GetComponent <CarController>().IsFinished())
                {
                    mainCamera.GetComponent <CameraController>().SetCamerTarget(cars[i]);

                    i = populationSize;
                }
            }

            timeElapsed += Time.deltaTime;

            DebugGUI.LogPersistent("timeElapsed", "Time Elapsed: " + timeElapsed.ToString("F3"));
        }
    }