public void updateCachedLevels(ParsedHeroes ph)
 {
     for (int i = 0; i < ph.HeroStats.Count; i++)
     {
         int heroID = ph.FirstHeroIndex + i;
         int heroLevel = ph.HeroStats[i].Level;
         setHeroLevel(heroID, heroLevel);
     }
 }
        /// <summary>
        /// Tries to buy a given hero upgrade, if there is enough money.
        /// </summary>
        /// <param name="heroIndex"></param>
        /// <param name="desiredUpgrade"></param>
        /// <param name="currentMoney"></param>
        /// <returns></returns>
        public static bool TryUpgradeHero(ParsedHeroes ph, int heroIndex, int desiredUpgrade, double currentMoney)
        {
            if (ph.FirstHeroIndex > heroIndex)
            {
                AddAction(new Action(GameEngine.GetScrollbarUpPoint(), 0), 3);
                return false;
            }
            else if (ph.LastHeroIndex < heroIndex)
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }

            int adjustedIndex = heroIndex - ph.FirstHeroIndex;
            HeroStats hs = ph.HeroStats[adjustedIndex];
            int upgradeStatus = hs.HasUpgrade(desiredUpgrade);
            if (upgradeStatus == 1)
            {
                return true;
            }
            else if (upgradeStatus == -1)
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }
            else
            {
                Point upgradeButton;
                if (hs.Hero.Upgrades[desiredUpgrade].Cost < currentMoney && hs.GetUpgradeButton(out upgradeButton, desiredUpgrade))
                {
                    AddAction(new Action(upgradeButton, 0));
                    return false;
                }
            }

            return false;
        }
        /// <summary>
        /// The main function which drives autoplaying. It will iterate through the task list sequentially,
        /// performing each task as necessary. When it reaches the Ascend task, it will restart from the beginning.
        /// </summary>
        /// <param name="ph">The parsed heroes obtained from the GameEngine</param>
        /// <param name="curMoney">The current money</param>
        /// <returns>A string giving a human-readable representation of the next task</returns>
        public static string TryNextTask(ParsedHeroes ph, double curMoney)
        {
            if (nextTaskToPerform >= Tasks.Count())
            {
                return string.Empty;
            }

            Task nextTask = Tasks[nextTaskToPerform];

            // Iris -- after ascending, we may potentially jump many levels into the game, but we don't have any money to buy any heroes.
            // To get around this, we do what human players do. We click candies when (and only when) we're on the very first task (aka
            // immediately after we've ascended).
            // Also sets the maximum time the run has to end, as a fail safe if the app gets stuck (example, faulty OCR money read)
            if (nextTaskToPerform == 0)
            {
                Point[] pts = GameEngine.GetCandyButtons();
                foreach (Point p in pts)
                {
                    AddAction(new Action(p, Modifiers.NONE));
                }

                // Toggle Progress button
                if (!GameEngine.IsProgressModeOn())
                {
                    AddAction(new Action(GameEngine.GetProgressButton(), 0));
                }

                maxEndTime = DateTime.Now.AddMinutes(Properties.Settings.Default.maxRunDuration);
            }

            // Check if the max run time has been reached
            // Allow maxRunDuration to be set to 0 for unlimited time
            // By setting nextTaskToPerform to an arbitary number, no way to guarantee anything will change
            // Hopefully by restarting task list, the game will sort itself out
            if (Properties.Settings.Default.maxRunDuration != 0 && DateTime.Now > maxEndTime)
            {
                nextTaskToPerform = 0;
            }

            if (nextTask is AscendTask)
            {
                Ascend(ph, curMoney);
                nextTaskToPerform = 0;
                return "Ascending";
            }

            if (nextTask is BuyAllÙpgradesTask)
            {
                BuyAllUpgrades();
                nextTaskToPerform++;
                return "Buying all upgrades";
            }

            if (nextTask is ActiveTask)
            {
                autoClick = true;
                useSkils = true;
                nextTaskToPerform++;
                return "Going Active";
            }

            if (nextTask is IdleTask)
            {
                autoClick = false;
                useSkils = false;
                nextTaskToPerform++;
                return "Going Idle";
            }

            if (nextTask is ReloadBrowserTask)
            {
                ReloadBrowser();
                nextTaskToPerform++;
                return "Reloading browser window";
            }

            if (nextTask is MidasStartTask)
            {
                MidasStart();
                nextTaskToPerform++;
                return "Performing Midas Start";
            }

            if (nextTask is SalvageRelicTask)
            {
                SalvageRelic();
                nextTaskToPerform++;
                return "Salvaging Relics";
            }

            if (nextTask is ToggleProgressOff)
            {
                if (GameEngine.IsProgressModeOn())
                {
                    AddAction(new Action(GameEngine.GetProgressButton(), 0));
                }
                nextTaskToPerform++;
                return "Turning Progress Mode Off";
            }

            if (nextTask is ToggleProgressOn)
            {
                if (!GameEngine.IsProgressModeOn())
                {
                    AddAction(new Action(GameEngine.GetProgressButton(), 0));
                }
                nextTaskToPerform++;
                return "Turning Progress Mode On";
            }

            if (nextTask is MoveZonesForward)
            {
                //Progress to level 60-64
                AddAction(new Action(GameEngine.GetMoveZoneRightButtion(), 0), 425);
                nextTaskToPerform++;
                return "Advancing Zones";
            }

            VerifyTask vt = nextTask as VerifyTask;

            string retStr = string.Empty;
            if (!TryLevelHero(ph, nextTask.HeroIndex, nextTask.Level, curMoney, nextTask.Wait))
            {
                if (vt != null)
                {
                    vt.verifyCount = 0;
                }
                retStr = string.Format("Level {0} to {1}", GameEngine.HeroList[nextTask.HeroIndex].Name, nextTask.Level);
                return retStr;
            }

            if (nextTask.Upgrade != -1)
            {
                for (int i = 0; i <= nextTask.Upgrade; i++)
                {
                    if (!TryUpgradeHero(ph, nextTask.HeroIndex, i, curMoney))
                    {
                        if (vt != null)
                        {
                            vt.verifyCount = 0;
                        }
                        retStr = string.Format("Get upgrade {0} for {1}", nextTask.Upgrade + 1, GameEngine.HeroList[nextTask.HeroIndex].Name);
                        return retStr;
                    }
                }
            }

            if (vt != null)
            {
                vt.verifyCount++;
                if (vt.verifyCount == 5)
                {
                    nextTaskToPerform++;
                    vt.verifyCount = 0;
                    return TryNextTask(ph, curMoney);
                }
                return string.Format("Verify that {0} is properly leveled and upgraded", GameEngine.HeroList[nextTask.HeroIndex].Name);
            }

            nextTaskToPerform++;
            return TryNextTask(ph, curMoney);
        }
        /// <summary>
        /// Tries to level a given hero to a desired level, if there is enough money.
        /// </summary>
        /// <param name="heroIndex">The hero to level</param>
        /// <param name="desiredLevel">The level desired</param>
        /// <param name="currentMoney">The current amount of money</param>
        /// <returns>True if and only if the hero is currently already at that level</returns>
        public static bool TryLevelHero(ParsedHeroes ph, int heroIndex, int desiredLevel, double currentMoney, bool wait)
        {
            //If Active and using auto click mode, click candies
            if (autoClick)
            {
                Point[] pts = GameEngine.GetCandyButtons();
                foreach (Point p in pts)
                {
                    AddAction(new Action(p, Modifiers.NONE));
                }
            }

            if (ph.FirstHeroIndex > heroIndex)
            {
                AddAction(new Action(GameEngine.GetScrollbarUpPoint(), 0), 3);
                return false;
            }
            else if (ph.LastHeroIndex < heroIndex)
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }

            int adjustedIndex = heroIndex - ph.FirstHeroIndex;
            HeroStats hs = ph.HeroStats[adjustedIndex];
            if (hs.Level == -1)
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }

            int heroLevel = hs.Level;
            if (heroLevel >= desiredLevel)
            {
                return true;
            }

            if (wait && hs.Hero.GetCostToLevel(desiredLevel, heroLevel) > currentMoney)
            {
                return false;
            }

            Point pt;
            if (hs.GetBuyButton(out pt))
            {
                while (hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel) < currentMoney && desiredLevel > heroLevel)
                {

                    if (desiredLevel - heroLevel >= 100 && hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel) < currentMoney)
                    {
                        AddAction(new Action(pt, Modifiers.CTRL));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel);
                        heroLevel += 100;
                    }
                    else if (desiredLevel - heroLevel >= 25 && hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel) < currentMoney)
                    {
                        AddAction(new Action(pt, Modifiers.Z));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel);
                        heroLevel += 25;
                    }
                    else if (desiredLevel - heroLevel >= 10 && hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel) < currentMoney)
                    {
                        AddAction(new Action(pt, Modifiers.SHIFT));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel);
                        heroLevel += 10;
                    }
                    else
                    {
                        AddAction(new Action(pt, 0));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel);
                        heroLevel++;
                    }
                }

                return false;
            }
            else
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }
        }
        /// <summary>
        /// Don't leave this method until after we've ascended
        /// </summary>
        /// <param name="ph"></param>
        /// <param name="curMoney"></param>
        public static void Ascend(ParsedHeroes ph, double curMoney)
        {
            Point AscendButton = GameEngine.GetAscendButton();

            // use candy height and width cuz it's close enough
            int candyHeight = GameEngine.GetCandyHeight();
            int candyWidth = GameEngine.GetCandyWidth();
            Rectangle c = new Rectangle(AscendButton.X - candyWidth / 2, AscendButton.Y - candyHeight / 2, candyWidth, candyHeight);

            while (true)
            {
                using (Bitmap bitmap = GameEngine.GetImage(c))
                {
                    if (OCREngine.GetBlobDensity(bitmap, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1), new Color[] {
                        Color.FromArgb(68, 215, 35)
                    }) > 0.10)
                    {
                        AddAction(new Action(AscendButton, 0));
                        Thread.Sleep(1000);
                        return;
                    }
                }

                TryUpgradeHero(ph, 19, 3, curMoney);
                Thread.Sleep(1000);
                ph = GameEngine.GetHeroes();
                curMoney = GameEngine.GetMoney();
            }
        }
        /// <summary>
        /// Helper method for GetHeroes
        /// </summary>
        /// <param name="lines"></param>
        /// <param name="lb"></param>
        /// <returns></returns>
        private static ParsedHeroes ParseHeroes(List<Line> lines, LockBitmap lb)
        {
            ParsedHeroes parsedHeroes = new ParsedHeroes();

            if (lines.Count() == 0)
            {
                return null;
            } else if (lines.Count() <= 3)
            {
                // it's cid it's cid it's cid
                // Note: Cid is special because of 2 reasons: when she appears it's the only time in the game
                // there can be less than 3 heroes on screen. Second, she doesn't have a number to represent DPS,
                // so we need to special case some stuff to make OCR work.
                parsedHeroes.HeroStats = new List<HeroStats>();
                parsedHeroes.FirstHeroIndex = 0;
                parsedHeroes.LastHeroIndex = 0;
                HeroStats cid = new HeroStats();
                cid.Hero = GameEngine.HeroList[0];
                cid.Level = 0;
                cid.bottomright.X = lines[0].GetBoundingRectangle().Right;
                cid.bottomright.Y = lines[0].GetBoundingRectangle().Bottom;
                parsedHeroes.HeroStats.Add(cid);

                return parsedHeroes;
            }

            List<Line> widths = new List<Line>();
            List<Line> levels = new List<Line>();
            int i = -1;
            bool rectSkipped = false;
            if (lines[1].GetBoundingRectangle().Top - lines[0].GetBoundingRectangle().Top > HeroSeperatorHeight)
            {
                i = 1;
                rectSkipped = true;
            } else {
                i = 0;
            }

            for (; i < lines.Count(); i+=2)
            {
                widths.Add(lines[i]);
                if (i + 1 < lines.Count())
                {
                    levels.Add(lines[i + 1]);
                }
            }

            // If less than 6 lines -- it's Cid
            if (lines.Count() < 6)
            {
                parsedHeroes.FirstHeroIndex = 0;
                parsedHeroes.LastHeroIndex = lines.Count() / 2;
            }
            else
            {
                int smallestIndex = -1;
                int smallestScore = int.MaxValue;

                for (int j = 0; j + widths.Count() <= HeroList.Count(); j++)
                {
                    int thisScore = 0;
                    for (int k = 0; k < widths.Count(); k++)
                    {
                        thisScore += Math.Abs((int)(HeroList[j + k].Namewidth * PlayableArea.Width - widths[k].GetBoundingRectangle().Width));
                    }
                    if (thisScore < smallestScore)
                    {
                        smallestScore = thisScore;
                        smallestIndex = j;
                    }
                }

                parsedHeroes.FirstHeroIndex = smallestIndex;
                parsedHeroes.LastHeroIndex = smallestIndex + widths.Count() - 1;
            }

            List<HeroStats> heroStats = new List<HeroStats>();
            if (parsedHeroes.FirstHeroIndex == 1 && rectSkipped)
            {
                // oh god, is it cid? why is it always cid
                if (lines[0].GetBoundingRectangle().Left < lb.Width / 2)
                {
                    HeroStats stats = new HeroStats();
                    stats.Hero = HeroList[0];
                    stats.Level = 0;
                    stats.bottomright = new Point(lines[0].GetBoundingRectangle().Right, lines[0].GetBoundingRectangle().Bottom);
                    heroStats.Add(stats);
                    parsedHeroes.FirstHeroIndex = 0;
                }
            }

            for (int j = 0; j < widths.Count(); j++)
            {
                HeroStats stats = new HeroStats();
                stats.Hero = HeroList[parsedHeroes.FirstHeroIndex + j];
                stats.bottomright = new Point(widths[j].GetBoundingRectangle().Right, widths[j].GetBoundingRectangle().Bottom);

                if (j < levels.Count())
                {
                    levels[j].DoOcr(lb, PlayableArea.Width * PlayableArea.Height, false, lb.Width / 2, 3 /* lvl */);
                    if (!int.TryParse(levels[j].OcrString, out stats.Level))
                    {
                        stats.Level = 0;
                    }
                }
                else
                {
                    stats.Level = -1;
                }

                heroStats.Add(stats);
            }
            parsedHeroes.HeroStats = heroStats;
            return parsedHeroes;
        }
        /// <summary>
        /// The main function which drives autoplaying. It will iterate through the task list sequentially,
        /// performing each task as necessary. When it reaches the Ascend task, it will restart from the beginning.
        /// </summary>
        /// <param name="ph">The parsed heroes obtained from the GameEngine</param>
        /// <param name="curMoney">The current money</param>
        /// <returns>A string giving a human-readable representation of the next task</returns>
        public static string TryNextTask(ParsedHeroes ph, double curMoney)
        {
            if (nextTaskToPerform >= Tasks.Count())
            {
                return string.Empty;
            }

            Task nextTask = Tasks[nextTaskToPerform];

            // Iris -- after ascending, we may potentially jump many levels into the game, but we don't have any money to buy any heroes.
            // To get around this, we do what human players do. We click candies when (and only when) we're on the very first task (aka
            // immediately after we've ascended).
            // Also sets the maximum time the run has to end, as a fail safe if the app gets stuck (example, faulty OCR money read)
            if (nextTaskToPerform == 0)
            {
                Point[] pts = GameEngine.GetCandyButtons();
                foreach (Point p in pts)
                {
                    AddAction(new Action(p, Modifiers.NONE));
                }

                maxEndTime = DateTime.Now.AddMinutes(Properties.Settings.Default.maxRunDuration);
            }

            // Check if the max run time has been reached
            if (DateTime.Now > maxEndTime && nextTaskToPerform < Tasks.Count() - 3)
            {
                nextTaskToPerform = Tasks.Count() - 3;
            }

            if (nextTask is AscendTask)
            {
                Ascend(ph, curMoney);
                nextTaskToPerform = 0;
                return "Ascending";
            }

            if (nextTask is BuyAllÙpgradesTask)
            {
                BuyAllUpgrades();
                nextTaskToPerform++;
                return "Buying all upgrades";
            }

            if (nextTask is ActiveTask)
            {
                autoClick = true;
                useSkils = true;
                nextTaskToPerform++;
                return "Going Active";
            }

            if (nextTask is IdleTask)
            {
                autoClick = false;
                useSkils = false;
                nextTaskToPerform++;
                return "Going Idle";
            }

            if (nextTask is ReloadBrowserTask)
            {
                ReloadBrowser();
                nextTaskToPerform++;
                return "Reloading browser window";
            }

            VerifyTask vt = nextTask as VerifyTask;

            string retStr = string.Empty;
            if (!TryLevelHero(ph, nextTask.HeroIndex, nextTask.Level, curMoney, nextTask.Wait))
            {
                if (vt != null)
                {
                    vt.verifyCount = 0;
                }
                retStr = string.Format("Level {0} to {1}", GameEngine.HeroList[nextTask.HeroIndex].Name, nextTask.Level);
                return retStr;
            }

            if (nextTask.Upgrade != -1)
            {
                for (int i = 0; i <= nextTask.Upgrade; i++)
                {
                    if (!TryUpgradeHero(ph, nextTask.HeroIndex, i, curMoney))
                    {
                        if (vt != null)
                        {
                            vt.verifyCount = 0;
                        }
                        retStr = string.Format("Get upgrade {0} for {1}", nextTask.Upgrade + 1, GameEngine.HeroList[nextTask.HeroIndex].Name);
                        return retStr;
                    }
                }
            }

            if (vt != null)
            {
                vt.verifyCount++;
                if (vt.verifyCount == 5)
                {
                    nextTaskToPerform++;
                    vt.verifyCount = 0;
                    return TryNextTask(ph, curMoney);
                }
                return string.Format("Verify that {0} is properly leveled and upgraded", GameEngine.HeroList[nextTask.HeroIndex].Name);
            }

            nextTaskToPerform++;
            return TryNextTask(ph, curMoney);
        }
        /// <summary>
        /// Don't leave this method until after we've ascended
        /// </summary>
        /// <param name="ph"></param>
        /// <param name="curMoney"></param>
        public static void Ascend(ParsedHeroes ph, double curMoney)
        {
            TrashRelics();
            Point AscendButton = GameEngine.GetAscendButton();

            // use candy height and width cuz it's close enough
            int candyHeight = GameEngine.GetCandyHeight();
            int candyWidth = GameEngine.GetCandyWidth();
            Rectangle c = new Rectangle(AscendButton.X - candyWidth / 2, AscendButton.Y - candyHeight / 2, candyWidth, candyHeight);

            while (true)
            {
                using (Bitmap bitmap = new Bitmap(c.Width, c.Height))
                {
                    using (Graphics g = Graphics.FromImage(bitmap))
                    {
                        g.CopyFromScreen(new Point(c.Left, c.Top), Point.Empty, c.Size);
                    }

                    if (OCREngine.GetBlobDensity(bitmap, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1), new Color[] {
                        Color.FromArgb(68, 215, 35)
                    }) > 0.10)
                    {
                        AddAction(new Action(AscendButton, 0));
                        Thread.Sleep(1000);
                        return;
                    }
                }

                TryUpgradeHero(ph, 19, 3, curMoney);
                Thread.Sleep(1000);
                ph = GameEngine.GetHeroes();
                curMoney = GameEngine.GetMoney();
            }
        }
        /// <summary>
        /// Tries to level a given hero to a desired level, if there is enough money.
        /// </summary>
        /// <param name="heroIndex">The hero to level</param>
        /// <param name="desiredLevel">The level desired</param>
        /// <param name="currentMoney">The current amount of money</param>
        /// <returns>True if and only if the hero is currently already at that level</returns>
        public static bool TryLevelHero(ParsedHeroes ph, int heroIndex, int desiredLevel, double currentMoney, bool wait)
        {
            if (GameEngine.GetCachedLevel(heroIndex) >= desiredLevel)
            {
                return true;
            }

            if (ph.FirstHeroIndex > heroIndex)
            {
                AddAction(new Action(GameEngine.GetScrollbarUpPoint(), 0), 3);
                return false;
            }
            else if (ph.LastHeroIndex < heroIndex)
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }

            // Check for stepping out of bounds (happens when part of the view is blocked while attempting to do certain actions)
            int adjustedIndex = heroIndex - ph.FirstHeroIndex;
            if (adjustedIndex >= ph.HeroStats.Count )
            {
                return false;
            }
            HeroStats hs = ph.HeroStats[adjustedIndex];
            if (hs.Level == -1)
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }

            int heroLevel = hs.Level;
            if (heroLevel >= desiredLevel)
            {
                return true;
            }

            if (wait && hs.Hero.GetCostToLevel(desiredLevel, heroLevel) > currentMoney)
            {
                return false;
            }

            Point pt;
            if (hs.GetBuyButton(out pt))
            {
                while (hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel) < currentMoney && desiredLevel > heroLevel)
                {

                    if (desiredLevel - heroLevel >= 100 && hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel) < currentMoney)
                    {
                        AddAction(new Action(pt, Modifiers.CTRL));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel);
                        heroLevel += 100;
                    }
                    else if (desiredLevel - heroLevel >= 25 && hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel) < currentMoney)
                    {
                        AddAction(new Action(pt, Modifiers.Z));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel);
                        heroLevel += 25;
                    }
                    else if (desiredLevel - heroLevel >= 10 && hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel) < currentMoney)
                    {
                        AddAction(new Action(pt, Modifiers.SHIFT));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel);
                        heroLevel += 10;
                    }
                    else
                    {
                        AddAction(new Action(pt, 0));
                        currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel);
                        heroLevel++;
                    }
                }

                return false;
            }
            else
            {
                AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3);
                return false;
            }
        }