/// <summary>
        /// Finds AirDefenses which can and should be ZapQuaked.
        /// The length of the list is dependent on the available spells (2 lightnings, 1 earthquake = 1 AirDefense target)
        /// </summary>
        /// <param name="earthQuakeSpells">The available earthquake spells</param>
        /// <param name="lightningSpells">The available lightning spells</param>
        /// <returns>A list of AirDefenses which can and should be ZapQuaked</returns>
        List <AirDefense> FindAirDefenseTargets(DeployElement earthQuakeSpells, DeployElement lightningSpells, List <VisualObject> visuals)
        {
            var lightningsToDestroyAirDefenses = (int)Math.Floor((double)lightningSpells.Count / 2);
            var destroyableAirDefenses         = Math.Min(lightningsToDestroyAirDefenses, earthQuakeSpells.Count);

            Log.Info($"[Smart Air] We've got {lightningSpells.Count} Lightning Spells and {earthQuakeSpells.Count}, which is enough to destroy {destroyableAirDefenses} AirDefenses.");


            var allAirDefenses = AirDefense.Find();

            try
            {
                var targetsToFindCount = Math.Min(destroyableAirDefenses, allAirDefenses.Count());
                if (targetsToFindCount == 0)
                {
                    Log.Error("[Smart Air] FindAirDefenseTargets has been called even though it shouldn't have been called!");
                    return(null);
                }

                // If we need to find 2 or more AirDefense targets we want to find the closest AirDefenses
                if (targetsToFindCount > 1)
                {
                    var airDefensesOrderedByDeployPointDistance = allAirDefenses.OrderByDescending(x => SmartAirDeployHelpers.DistanceSqToClosestDeploypoint(x.Location.GetCenter()));
                    // furthestAirDefense = The airdefense which is the furthest away from deployzone
                    var furthestAirDefense   = airDefensesOrderedByDeployPointDistance.First();
                    var remainingAirDefenses = airDefensesOrderedByDeployPointDistance.Skip(1).ToList();
                    var orderedList          = OrderByDistance(furthestAirDefense, remainingAirDefenses).Take(targetsToFindCount).ToList();

                    // Add visuals
                    var orderedListCenters = orderedList.Select(x => x.Location.GetCenter());
                    visuals.Add(new PointsObject("AirDefenseTargets", Color.FromArgb(200, Color.CornflowerBlue), orderedListCenters));

                    return(orderedList);
                }
                var targetList = allAirDefenses.Take(1).ToList();

                // Add visuals
                var targetListCenters = targetList.Select(x => x.Location.GetCenter());
                visuals.Add(new PointsObject("AirDefenseTargets", Color.FromArgb(200, Color.CornflowerBlue), targetListCenters));

                return(targetList);
            }
            catch (Exception ex)
            {
                Log.Error("[Smart Air] Exception occured during 'ZapQuakeAirDefenses'. More information can be found inside of the debug log.");
                Log.Debug("[Smart Air] Exception details: " + ex);
                return(null);
            }
        }
        List <PointFT> CreateLavaHoundDeployPoints(AirDefense[] airDefenses, List <VisualObject> visuals)
        {
            // don't include corners in case build huts are there
            var maxRedPointX = GameGrid.RedPoints.Where(p => - 18 < p.Y && p.Y < 18)?.Max(point => point.X) + 1 ?? GameGrid.RedZoneExtents.MaxX;
            var minRedPointX = GameGrid.RedPoints.Where(p => - 18 < p.Y && p.Y < 18)?.Min(point => point.X) - 1 ?? GameGrid.RedZoneExtents.MinX;
            var maxRedPointY = GameGrid.RedPoints.Where(p => - 18 < p.X && p.X < 18)?.Max(point => point.Y) + 1 ?? GameGrid.RedZoneExtents.MaxY;
            var minRedPointY = GameGrid.RedPoints.Where(p => - 18 < p.X && p.X < 18)?.Min(point => point.Y) - 1 ?? GameGrid.RedZoneExtents.MinY;

            // border around the base
            var baseBorder = new List <RectangleT>();

            baseBorder.Add(new RectangleT((int)minRedPointX, (int)maxRedPointY, (int)(maxRedPointX - minRedPointX), (int)(minRedPointY - maxRedPointY)));

            // Add baseborder as visual
            visuals.Add(new RectangleObject("BaseBorder", Color.FromArgb(100, Color.Maroon), baseBorder, new Pen(Color.Maroon)));

            // Find nearest deploy points for two airdefenses
            List <PointFT> resultPoints       = new List <PointFT>();
            var            closestAirDefenses = airDefenses.OrderBy(x => SmartAirDeployHelpers.DistanceSqToClosestDeploypoint(x.Location.GetCenter())).Take(2);

            if (closestAirDefenses?.Count() == 2)
            {
                var start = closestAirDefenses.First().Location.GetCenter();
                var end   = closestAirDefenses.Last().Location.GetCenter();
                var airDefenseConnection = new Line(start, end);
                visuals.Add(new LinesObject("AirDefenseConnection", Color.FromArgb(180, Color.OrangeRed), new[] { airDefenseConnection }));


                Point screenStart = start.ToScreenAbsolute();
                Point screenEnd   = end.ToScreenAbsolute();

                Vector vStart = new Vector(screenStart.X, screenStart.Y);
                Vector vEnd   = new Vector(screenEnd.X, screenEnd.Y);

                Vector vStartEnd = vEnd - vStart;
                Vector vDir      = vStartEnd;
                vDir.Normalize();

                Vector vOrthDir = new Vector(vDir.Y, -vDir.X);

                Vector vOrthMidPoint = vStart + vStartEnd * 0.5;

                Point midPoint = new Point((int)vOrthMidPoint.X, (int)vOrthMidPoint.Y);

                // We have two lines (mid -> airDef1) and (mid -> airDef2)
                // We want to know what line points "away" from the townhall (center of the map)
                var    mapCenterAbsolute = new PointFT(0f, 0f).ToScreenAbsolute();
                Vector vMapCenter        = new Vector(mapCenterAbsolute.X, mapCenterAbsolute.Y);


                Vector dirTest1 = vOrthMidPoint + vOrthDir * +0.1;
                Vector dirTest2 = vOrthMidPoint + vOrthDir * -0.1;

                Vector dirAwayFromCenter = (vMapCenter - dirTest1).LengthSquared < (vMapCenter - dirTest2).LengthSquared
                    ? -vOrthDir
                    : vOrthDir;

                Vector vAirDefOrthLineEnd   = vOrthMidPoint + dirAwayFromCenter * 3000;
                Point  airDefLineEndCorrect = new Point((int)vAirDefOrthLineEnd.X, (int)vAirDefOrthLineEnd.Y);

                var orthLine = new Line(new PointF((float)vOrthMidPoint.X, (float)vOrthMidPoint.Y), new PointF(airDefLineEndCorrect.X, airDefLineEndCorrect.Y));
                Log.Error($"[Smart Air] Start: ({orthLine.Start.X} , {orthLine.Start.Y}), End ({orthLine.End.X}, {orthLine.End.Y})");
                visuals.Add(new LinesObject("AirDefenseOrth", new Pen(Color.OrangeRed, 3), new[] { orthLine }));

                // Now we want to find the intersection of our line, with the outer rectangle.
                // We know that our line starts inside the rect, and ends outside of it, that means there must be exactly one intersection point

                // 1. convert all corners into vectors
                var    left         = new PointFT(minRedPointX, maxRedPointY).ToScreenAbsolute();
                var    top          = new PointFT(maxRedPointX, maxRedPointY).ToScreenAbsolute();
                var    right        = new PointFT(maxRedPointX, minRedPointY).ToScreenAbsolute();
                var    bottom       = new PointFT(minRedPointX, minRedPointY).ToScreenAbsolute();
                Vector vLeftPoint   = new Vector(left.X, left.Y);
                Vector vTopPoint    = new Vector(top.X, top.Y);
                Vector vRightPoint  = new Vector(right.X, right.Y);
                Vector vBottomPoint = new Vector(bottom.X, bottom.Y);

                // 2. create the 4 combinations of the 4 points to get all 4 edges (topleft, topright, ...)
                var sidesToCheckForIntersections = new List <Tuple <Vector, Vector> >();
                sidesToCheckForIntersections.Add(Tuple.Create(vTopPoint, vLeftPoint));     // Topleft
                sidesToCheckForIntersections.Add(Tuple.Create(vTopPoint, vRightPoint));    // Topright
                sidesToCheckForIntersections.Add(Tuple.Create(vLeftPoint, vBottomPoint));  // BottomLeft
                sidesToCheckForIntersections.Add(Tuple.Create(vRightPoint, vBottomPoint)); // BottomRight

                // 3. use those for the following intersection calculation:
                Vector vOrthLineStartPoint = vOrthMidPoint;
                Vector vOrthLineEndPoint   = vAirDefOrthLineEnd;
                Vector vOrth = vOrthLineStartPoint + vOrthLineEndPoint;

                // 4. test this line for intersections against all 4 sides, only (and exactly) one of them will have an intersection
                Vector vInterSectionPoint = default(Vector);
                foreach (var side in sidesToCheckForIntersections)
                {
                    if (SmartAirDeployHelpers.LineSegementsIntersect(side.Item1, side.Item2, vOrthLineStartPoint, vOrthLineEndPoint, out vInterSectionPoint))
                    {
                        break;
                    }
                }
                var intersectionPointF  = new PointF((float)vInterSectionPoint.X, (float)vInterSectionPoint.Y);
                var intersectionPointFt = new PointFT((int)intersectionPointF.X, (int)intersectionPointF.Y);


                // 5. add that point to "visuals"
                visuals.Add(new PointsObject("IntersectionPoint", Color.FromArgb(200, Color.Cyan), new[] { intersectionPointF }));

                // deployPoints = all Points within radius x
                var deployPoints = GameGrid.RedPoints.Where(x => x.DistanceSq(intersectionPointFt) < 220).ToList();
                deployPoints = deployPoints.Select(x => x.TransformPositionAlongAxis(5)).ToList();

                // FurthestDeployPoint 1 = Furthest Point to intersectionPoint which is still inside of the radius
                var furthestDeployPoint1 = deployPoints.OrderByDescending(x => x.DistanceSq(intersectionPointFt)).First();
                // FurthestDeployPoint 2 = Furthest Point to intersectionPoint which is still inside of the radius
                var furthestDeployPoint2 = deployPoints.OrderByDescending(x => x.DistanceSq(furthestDeployPoint1)).First();

                var lavaHoundDeployPoints = new List <PointFT>();
                lavaHoundDeployPoints.Add(furthestDeployPoint1);
                lavaHoundDeployPoints.Add(furthestDeployPoint2);

                visuals.Add(new PointsObject("LavaHoundDeployPoints", Color.FromArgb(200, Color.Pink), lavaHoundDeployPoints));
                visuals.Add(new PointsObject("AttackUnitDeployPoints", Color.FromArgb(110, Color.Black), deployPoints));
            }

            return(resultPoints);
        }