/// <summary>
        /// Removes all gamespy related redirects in the specified hosts file container
        /// </summary>
        /// <param name="HostFile"></param>
        private static void RemoveRedirects(HostsFile HostFile)
        {
            // Remove Gamespy Addresses. Use a Bitwise OR here to execute both methods
            if (HostFile.Remove(Bf2StatsHost) | HostFile.RemoveAll(GamespyHosts))
            {
                // Save Changes
                HostFile.Save();

                // Flush the Cache of the gamespy hosts
                foreach (string host in GamespyHosts)
                {
                    DnsFlushResolverCacheEntry(host);
                }

                // Flush stats server
                DnsFlushResolverCacheEntry(Bf2StatsHost);
            }
        }
        /// <summary>
        /// Preforms the pings required to fill the dns cache.
        /// The reason we ping, is because once the HOSTS file is locked, any request
        /// made to a url (when the DNS cache is empty), will skip the hosts file, because
        /// it cant be read. If we ping first, then the DNS cache fills up with the IP
        /// addresses in the hosts file.
        /// </summary>
        public static void RebuildDNSCache(CancellationToken CancelToken)
        {
            // Must be hosts file!
            if (RedirectMethod == RedirectMode.DnsServer || !RedirectsEnabled)
            {
                return;
            }

            // Grab hosts file base
            HostsFile hFile = (RedirectMethod == RedirectMode.HostsFile)
                ? HostsFileSys as HostsFile
                : HostsFileIcs as HostsFile;

            // Rebuild the DNS cache with the hosts file redirects
            foreach (string hostname in hFile.GetLines().Keys)
            {
                // Quit on cancel
                if (CancelToken.IsCancellationRequested)
                {
                    return;
                }

                // Only ping gamespy urls with the hosts ics file
                if (RedirectMethod == RedirectMode.HostsIcsFile && !hostname.Contains("gamespy"))
                {
                    continue;
                }

                // Attempt to ping the server
                try
                {
                    // Clear the record from the DNS cache
                    DnsFlushResolverCacheEntry(hostname);

                    // Ping server to get the IP address in the dns cache
                    Dns.GetHostAddresses(hostname);
                }
                catch
                {
                    continue;
                }
            }
        }
        /// <summary>
        /// Queries the DNS Cache for the Gamespy hosts, and verifies that the
        /// IP addresses in the DNS Cache match that of the desired redirect IP
        /// </summary>
        public static bool VerifyDNSCache(IProgress <DnsCacheResult> Progress = null)
        {
            // Nothing to do here if redirects are disabled
            if (!RedirectsEnabled)
            {
                return(false);
            }

            // Grab our saved hosts file IP addresses
            if (RedirectMethod != RedirectMode.DnsServer)
            {
                // Grab hosts file base
                HostsFile hFile = (RedirectMethod == RedirectMode.HostsFile)
                    ? HostsFileSys as HostsFile
                    : HostsFileIcs as HostsFile;

                // Fetch our saved IPAddresses
                GamespyServerAddress = (hFile.HasEntry("master.gamespy.com")) ? hFile.Get("master.gamespy.com") : null;
                StatsServerAddress   = (hFile.HasEntry(Bf2StatsHost)) ? hFile.Get(Bf2StatsHost) : null;
            }

            // Flush our cache report
            DnsCacheReport.Entries.Clear();

            // Verify Gamespy Server IP addresses
            if (GamespyServerAddress != null)
            {
                foreach (string address in GamespyHosts)
                {
                    // Create new report
                    DnsCacheResult Result = new DnsCacheResult(address, GamespyServerAddress);

                    // Add the result to the report
                    DnsCacheReport.AddOrUpdate(Result);

                    // Report progress if we have a progress object
                    if (Progress != null)
                    {
                        Progress.Report(Result);
                    }
                }
            }

            // Verify Stats Server address
            if (StatsServerAddress != null)
            {
                // Create new report
                DnsCacheResult Result = new DnsCacheResult(Bf2StatsHost, StatsServerAddress);

                // Add the result to the report
                DnsCacheReport.AddOrUpdate(Result);

                // Report progress if we have a progress object
                if (Progress != null)
                {
                    Progress.Report(Result);
                }
            }

            // Set internal
            DnsCacheReport.LastRefresh = DateTime.Now;
            return(DnsCacheReport.ErrorFree);
        }
        /// <summary>
        /// Applies the specified IP address redirects for the chosen redirect method
        /// </summary>
        public static Task <bool> ApplyRedirectsAsync(ServiceProvider Provider, IProgress <TaskStep> Progress)
        {
            return(Task.Run(() =>
            {
                // Try and grab the IPAddresses of these providers
                try
                {
                    SetProviderIPAddress(Provider);
                    Program.Config.LastUsedProvider = Provider.Name;
                }
                catch (Exception e)
                {
                    Progress.Report(new TaskStep(0, e.Message, true, e));
                    return false;
                }

                // Can't do anything with a dns server
                if (RedirectMethod == RedirectMode.DnsServer)
                {
                    return true;
                }

                // Remove old settings
                if (RedirectsEnabled)
                {
                    RemoveRedirects();
                }

                // === Apply new settings === //

                // Grab hosts file base
                HostsFile hFile = (RedirectMethod == RedirectMode.HostsFile)
                    ? HostsFileSys as HostsFile
                    : HostsFileIcs as HostsFile;

                // Unlock system hosts file
                if (RedirectMethod == RedirectMode.HostsFile)
                {
                    // Make sure file is writable
                    if (SysHostsFile.IsLocked && !HostsFileSys.UnLock())
                    {
                        Progress.Report(new TaskStep(0, "Cannot allow READ permissions on the Hosts File", true, hFile.LastException));
                        return false;
                    }
                    else
                    {
                        Progress.Report(new TaskStep(0, ""));
                    }
                }

                // Make sure file is writable
                if (!hFile.CanRead)
                {
                    Progress.Report(new TaskStep(1, "Cannot read the Hosts File", true, hFile.LastException));
                    return false;
                }
                else
                {
                    Progress.Report(new TaskStep(1, ""));
                }

                // Make sure file is readable
                if (!hFile.CanWrite)
                {
                    Progress.Report(new TaskStep(2, "Cannot write to the Hosts File", true, hFile.LastException));
                    return false;
                }
                else
                {
                    Progress.Report(new TaskStep(2, ""));
                }

                // ===== Set Redirect Addresses ===== //

                // Stats Server
                if (StatsServerAddress != null)
                {
                    hFile.Set(Bf2StatsHost, StatsServerAddress);
                }

                // Gamespy Servers
                if (GamespyServerAddress != null)
                {
                    foreach (string hostname in GamespyHosts)
                    {
                        hFile.Set(hostname, GamespyServerAddress);
                    }
                }

                // Report Progress
                Progress.Report(new TaskStep(3, "Gamespy Redirects Set"));

                // ===== Save Redirects ===== //

                int Step = 4;
                string ErrDesc = "";
                try
                {
                    // Attempt to save the hosts file
                    ErrDesc = "Unable to  Save Hosts File!";
                    hFile.Save();

                    // Report Success
                    Progress.Report(new TaskStep(Step++, "Hosts File Saved Successfully"));

                    // Rebuild the DNS Cache
                    ErrDesc = "Failed to Rebuild the DNS Cache";
                    RebuildDNSCache(CancellationToken.None);

                    // Report Success
                    Progress.Report(new TaskStep(Step++, "DNS Cache Rebuilt Successfully"));
                }
                catch (Exception e)
                {
                    RemoveRedirects();
                    Progress.Report(new TaskStep(Step, ErrDesc, true, e));
                    return false;
                }

                // Lock system hosts File
                if (RedirectMethod == RedirectMode.HostsFile)
                {
                    if (HostsFileSys.Lock())
                    {
                        // Report Success
                        Progress.Report(new TaskStep(Step++, "Hosts File Read Permissions Removed"));
                        return true;
                    }
                    else
                    {
                        // Report Error
                        Progress.Report(new TaskStep(Step++, "Cannot Remove Hosts File Read Permissions!",
                                                     true, HostsFileSys.LastException));
                        return true;
                    }
                }

                return true;
            }));
        }
        /// <summary>
        /// The main entry point for the redirector
        /// </summary>
        /// <returns>Returns wether the DNS cache results match the selected IPAddresses</returns>
        public static bool Initialize()
        {
            // Only initialize once
            if (!IsInitialized)
            {
                IsInitialized = true;
                TraceLog.WriteLine("Initializing Redirector");
                TraceLog.Indent();

                // Set the System.Net DNS Cache refresh timeout to 1 millisecond
                ServicePointManager.DnsRefreshTimeout = 1;

                // Get config options
                RedirectMethod = Program.Config.RedirectMode;
                TraceLog.WriteLine("Chosen Redirect mode: " + RedirectMethod.ToString());

                // Create new Instances
                HostsFileSys = new SysHostsFile();
                HostsFileIcs = new HostsFileIcs();

                // Detect redirects
                bool IcsHasRedirects   = HostsFileIcs.HasAnyEntry(GamespyHosts) || HostsFileIcs.HasEntry(Bf2StatsHost);
                bool HostsHasRedirects = HostsFileSys.HasAnyEntry(GamespyHosts) || HostsFileSys.HasEntry(Bf2StatsHost);

                // Write tracelogs
                TraceLog.WriteLine("System Hosts has redirects: " + ((HostsHasRedirects) ? "True" : "False"));
                TraceLog.WriteLine("Hosts.ics has redirects: " + ((IcsHasRedirects) ? "True" : "False"));

                // Both files cannot have redirects!
                if (IcsHasRedirects && HostsHasRedirects)
                {
                    // Get the Non-Selected mode, and remove those redirects
                    HostsFile toRemove = (RedirectMethod == RedirectMode.HostsFile)
                        ? HostsFileIcs as HostsFile
                        : HostsFileSys as HostsFile;

                    try
                    {
                        // Remove all redirects
                        TraceLog.Write("Removing redirects from unchosen hostsfile... ");
                        RemoveRedirects(toRemove);
                        TraceLog.WriteLine("Success");
                    }
                    catch (Exception e)
                    {
                        TraceLog.WriteLine("Failed!");
                        TraceLog.Indent();
                        TraceLog.TraceError(e.Message);
                    }
                }

                // Set old redirect data if we have it
                if (RedirectsEnabled)
                {
                    // Grab our service provider
                    ServiceProvider provider = ClientSettings.ServiceProviders
                                               .Where(x => x.Name == Program.Config.LastUsedProvider)
                                               .FirstOrDefault();

                    // Make sure we have an object before settings
                    if (provider != null)
                    {
                        SetProviderIPAddress(provider);
                    }
                }

                // Remove all indents
                TraceLog.Unindent(true);
            }

            // Verify cache
            return((RedirectsEnabled) ? VerifyDNSCache() : true);
        }