private void LoadScanAreas()
        {
            this.areas             = new ScanAreaDictionary();
            this.heroes            = new ScanAreaDictionary();
            this.opponentHeroes    = new ScanAreaDictionary();
            this.decks             = new ScanAreaDictionary();
            this.arenaHeroes       = new ScanAreaDictionary();
            this.arenaWinsLookup   = new ScanAreaDictionary();
            this.arenaWinsLookup2  = new ScanAreaImageDictionary();
            this.arenaLossesLookup = new ScanAreaDictionary();

            this.scanAreaProvider.Load();
            this.scanareas = this.scanAreaProvider.GetScanAreas();

            foreach (var scanArea in this.scanareas)
            {
                var allareas = scanArea.Areas.ToDictionary(x => x.Key, x => x);
                foreach (var a in allareas)
                {
                    if (a.Key.StartsWith("hero_"))
                    {
                        this.InitAreas(a.Key, "hero_", scanArea, this.heroes, a);
                    }
                    else if (a.Key.StartsWith("opphero_"))
                    {
                        this.InitAreas(a.Key, "opphero_", scanArea, this.opponentHeroes, a);
                    }
                    else if (a.Key.StartsWith("deck_"))
                    {
                        this.InitAreas(a.Key, "deck_", scanArea, this.decks, a);
                    }
                    //else if (a.Key.StartsWith("arenahero_"))
                    //{
                    //    this.InitAreas(a.Key, "arenahero_", scanArea, this.arenaHeroes, a);
                    //}
                    else if (a.Key.StartsWith("arenawins_"))
                    {
                        this.InitAreas(a.Key, "arenawins_", scanArea, this.arenaWinsLookup, a);
                        this.InitImageAreas(a.Key, "arenawins_", scanArea, this.arenaWinsLookup2, a);
                    }
                    else if (a.Key.StartsWith("arenaloss_"))
                    {
                        this.InitAreas(a.Key, "arenaloss_", scanArea, this.arenaLossesLookup, a);
                    }
                    else if (a.Key.StartsWith("arena_hero_"))
                    {
                        this.InitAreas(a.Key, "arena_hero_", scanArea, this.arenaHeroes, a);
                    }
                    else
                    {
                        this.InitAreas(a.Key, string.Empty, scanArea, this.areas, a);
                    }
                }
            }
        }
        //private string DetectBest(ScanAreaDictionary lookup, IDictionary<int, Tuple<ulong, Rectangle>> useArea, string debugkey)
        //{
        //    var hashes = new List<ulong>();
        //    var keys = new List<string>();

        //    var rect = new Rectangle();
        //    int theResolution;
        //    foreach (var key in lookup.Keys)
        //    {
        //        var template = lookup[key];
        //        ulong matchhash;
        //        if (template.ContainsKey(this.lastResolution))
        //        {
        //            matchhash = template[this.lastResolution].Hash;
        //        }
        //        else if (template.ContainsKey(this.BaseResolution))
        //        {
        //            matchhash = template[this.BaseResolution].Hash;
        //        }
        //        else
        //        {
        //            Log.Error("No scan data found for requested template: " + debugkey);
        //            return null;
        //        }
        //        hashes.Add(matchhash);
        //        keys.Add(key);
        //    }

        //    if (useArea.ContainsKey(this.lastResolution))
        //    {
        //        rect = useArea[this.lastResolution].Rect;
        //        theResolution = this.lastResolution;
        //    }
        //    else if (useArea.ContainsKey(this.BaseResolution))
        //    {
        //        rect = useArea[this.BaseResolution].Rect;
        //        theResolution = this.BaseResolution;
        //    }
        //    else
        //    {
        //        Log.Error("No scan data found for requested template: " + debugkey);
        //        return null;
        //    }

        //    var source = this.image;
        //    using (var roi = source.Clone(ResolutionHelper.CorrectRectangle(source.Size, rect, theResolution), PixelFormat.Format32bppRgb))
        //    {
        //        var hash = this.imageHasher.Create(roi);
        //        var best = PerceptualHash.FindBest(hash, hashes);
        //        if (best.Distance <= ThreshHold)
        //        {
        //            Log.Diag("Detected best hash: '{0}' Distance: {1}", debugkey, best.Distance);
        //            return keys[best.Index];
        //        }
        //    }

        //    return null;
        //}

        private string DetectBest(ScanAreaDictionary lookup, string debugkey, int?threshold = null)
        {
            threshold = threshold ?? ThreshHold;
            var hashes = new List <ulong>();
            var keys   = new List <string>();

            var rect          = new Rectangle();
            int theResolution = BaseResolution;

            foreach (var key in lookup.Keys)
            {
                var   template = lookup[key];
                ulong matchhash;
                if (template.ContainsKey(this.lastResolution))
                {
                    matchhash     = template[this.lastResolution].Hash;
                    rect          = template[this.lastResolution].Rect;
                    theResolution = this.lastResolution;
                }
                else if (template.ContainsKey(this.BaseResolution))
                {
                    matchhash     = template[this.BaseResolution].Hash;
                    rect          = template[this.BaseResolution].Rect;
                    theResolution = this.BaseResolution;
                }
                else
                {
                    Log.Error("No scan data found for requested template: " + debugkey);
                    return(null);
                }
                hashes.Add(matchhash);
                keys.Add(key);
            }

            var source = this.image;

            using (var roi = source.Lock(ResolutionHelper.CorrectRectangle(source.Size, rect, theResolution), PixelFormat.Format32bppRgb))
            {
                var hash = this.imageHasher.Create(roi.Data);
                var best = PerceptualHash.FindBest(hash, hashes);
                TraceLog.Log("Detected best hash: '{0}' Distance: {1}", debugkey, best.Distance);
                if (best.Distance <= threshold)
                {
                    return(keys[best.Index]);
                }
            }

            return(null);
        }
        private void InitAreas(string arrkey, string prefix, ScanAreas scanArea, ScanAreaDictionary lookup, KeyValuePair <string, ScanArea> a)
        {
            var key = arrkey.Substring(prefix.Length);

            if (!lookup.ContainsKey(key))
            {
                // lookup[key] = new Tuple<ulong, IDictionary<int, Rectangle>>(a.Value.Hash, new Dictionary<int, Rectangle>());
                lookup[key] = new Dictionary <int, ScanArea>();
            }

            var tmp = lookup[key];

            if (!tmp.ContainsKey(scanArea.BaseResolution))
            {
                tmp[scanArea.BaseResolution] = a.Value;
            }
        }
        /// <summary>The detect.</summary>
        /// <param name="lookup">The lookup.</param>
        /// <param name="key">The key.</param>
        /// <param name="useArea"></param>
        /// <param name="i"></param>
        /// <returns>The <see cref="bool"/>.</returns>
        private DetectionResult Detect(ScanAreaDictionary lookup, string key, IDictionary <int, ScanArea> useArea = null, int threshold = -1)
        {
            threshold = threshold >= 0 ? threshold : ThreshHold;
            if (!lookup.ContainsKey(key))
            {
                Log.Error("No scan data found for requested template: {0}", key);
                return(new DetectionResult(false, -1));
            }

            var template = lookup[key];
            // var rect = new Rectangle();
            int theResolution;
            // ulong matchhash;
            ScanArea area = null;

            if (template.ContainsKey(this.lastResolution))
            {
                // rect = template[this.lastResolution].Rect;
                // matchhash = template[this.lastResolution].Hash;
                area          = template[this.lastResolution];
                theResolution = this.lastResolution;
            }
            else if (template.ContainsKey(this.BaseResolution))
            {
                // rect = template[this.BaseResolution].Rect;
                // matchhash = template[this.BaseResolution].Hash;
                area          = template[this.BaseResolution];
                theResolution = this.BaseResolution;
            }
            else
            {
                Log.Error("No scan data found for requested template: " + key);
                return(new DetectionResult(false, -1));
            }
            if (useArea != null)
            {
                if (useArea.ContainsKey(this.lastResolution))
                {
                    // rect = useArea[this.lastResolution].Rect;
                    area          = useArea[this.lastResolution];
                    theResolution = this.lastResolution;
                }
                else if (useArea.ContainsKey(this.BaseResolution))
                {
                    // rect = useArea[this.BaseResolution].Rect;
                    area          = useArea[this.BaseResolution];
                    theResolution = this.BaseResolution;
                }
                else
                {
                    Log.Error("No scan data found for requested template: " + key);
                    return(new DetectionResult(false, -1));
                }
            }
            var             source   = this.image;
            int             distance = -1;
            DetectionResult result   = new DetectionResult();

            using (var roi = source.Lock(ResolutionHelper.CorrectRectangle(source.Size, area.Rect, theResolution), source.PixelFormat))
            {
                // var roi = source.Clone(ResolutionHelper.CorrectRectangle(source.Size, area.Rect, theResolution), source.PixelFormat);
                //Tuple<byte[], DetectionResult> cached = null;
                //byte[] roiBytes = roi.GetBytes();
                //if (hashCache.TryGetValue(key, out cached))
                //{
                //    if (ImageUtils.AreEqual(cached.Item1, roiBytes))
                //    {
                //        TraceLog.Log("hash cache hit: {0}, hit: {1} distance: {2}", key, cached.Item2.Found, cached.Item2.Distance);
                //        return cached.Item2;
                //    }
                //}

                var hash = this.imageHasher.Create(roi.Data);
                distance = PerceptualHash.HammingDistance(hash, area.Hash);
                TraceLog.Log("Detecting '{0}'. Distance: {1}", key, distance);

                if (distance <= threshold)
                {
                    TraceLog.Log("Detected '{0}'. Distance: {1}", key, distance);
                    Mostly mostly;
                    if (!String.IsNullOrEmpty(area.Mostly) && Enum.TryParse(area.Mostly, out mostly))
                    {
                        result = new DetectionResult(roi.Data.IsMostly(mostly), distance);
                    }
                    else
                    {
                        result = new DetectionResult(true, distance);
                    }
                }
                else
                {
                    result = new DetectionResult(false, distance);
                }
                // hashCache.Set(key, new Tuple<byte[], DetectionResult>(roiBytes, result));
            }
            return(result);
        }