/// <summary>
        /// Runs the specified service using the command console as a user interface.
        /// </summary>
        /// <param name="service">The service.</param>
        /// <param name="runMode">The run mode.</param>
        /// <param name="defaultLogFormat">The default log format.</param>
        /// <param name="defaultLoggingLevels">The default logging levels.</param>
        /// <param name="token">The token.</param>
        /// <returns>
        /// An awaitable task.
        /// </returns>
        // ReSharper disable once CodeAnnotationAnalyzer
        public static async Task RunAsync(
            [NotNull] BaseService service,
            RunMode runMode = RunMode.Default,
            [CanBeNull] FormatBuilder defaultLogFormat = null,
            LoggingLevels defaultLoggingLevels         = LoggingLevels.All,
            CancellationToken token = default(CancellationToken))
        {
            if (service == null)
            {
                throw new ArgumentNullException("service");
            }

            if (!ConsoleHelper.IsConsole)
            {
                return;
            }
            Console.Clear();
            Log.SetTrace(validLevels: LoggingLevels.None);
            Log.SetConsole(defaultLogFormat ?? Log.ShortFormat, defaultLoggingLevels);
            await Log.Flush(token).ConfigureAwait(false);

            Impersonator impersonator = null;

            try
            {
                if (runMode.HasFlag(RunMode.Prompt))
                {
                    Debug.Assert(service.ServiceName != null);

                    // Whether we start will depend on the selected option in prompt.
                    runMode = runMode.Clear(RunMode.Start, true);

                    Console.Title = ServiceResources.ConsoleConnection_RunAsync_ConfigureTitle + service.ServiceName;
                    bool done = false;

                    do
                    {
                        if (token.IsCancellationRequested)
                        {
                            return;
                        }

                        Dictionary <string, string> options = new Dictionary <string, string>
                        {
                            { "I", ServiceResources.ConsoleConnection_RunAsync_OptionInstall },
                            { "U", ServiceResources.ConsoleConnection_RunAsync_OptionUninstall },
                            { "S", ServiceResources.ConsoleConnection_RunAsync_OptionStart },
                            { "R", ServiceResources.ConsoleConnection_RunAsync_OptionRestart },
                            { "T", ServiceResources.ConsoleConnection_RunAsync_OptionStop },
                            { "P", ServiceResources.ConsoleConnection_RunAsync_OptionPause },
                            { "C", ServiceResources.ConsoleConnection_RunAsync_OptionContinue },
                            { "Y", ServiceResources.ConsoleConnection_RunAsync_OptionRunCmd },
                            { "V", ServiceResources.ConsoleConnection_RunAsync_OptionStartCmd },
                            { "W", ServiceResources.ConsoleConnection_RunAsync_OptionRunCmdNewCredentials },
                            { "Z", ServiceResources.ConsoleConnection_RunAsync_OptionRunNoInteraction },
                            { "X", ServiceResources.ConsoleConnection_RunAsync_OptionExit }
                        };

                        if (!runMode.HasFlag(RunMode.Interactive))
                        {
                            options.Remove("V");
                            options.Remove("Y");
                            options.Remove("W");
                        }

                        bool   isAdmin;
                        string currentUser;
                        try
                        {
                            WindowsIdentity identity = WindowsIdentity.GetCurrent();
                            currentUser = identity.Name;
                            Debug.Assert(identity != null);
                            WindowsPrincipal principal = new WindowsPrincipal(identity);
                            isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
                        }
                        catch
                        {
                            isAdmin     = false;
                            currentUser = null;
                        }

                        if (!string.IsNullOrEmpty(currentUser))
                        {
                            FormatBuilder fb = new FormatBuilder()
                                               .AppendForegroundColor(ConsoleColor.Cyan)
                                               .Append("Current User: "******" [Admin]");
                            }
                            fb.AppendLine().WriteToConsole();
                        }

                        if (!isAdmin)
                        {
                            options.Remove("I");
                            options.Remove("U");
                        }

                        if (Controller.ServiceIsInstalled(service.ServiceName))
                        {
                            ServiceControllerStatus state = Controller.GetServiceStatus(service.ServiceName);
                            new FormatBuilder()
                            .AppendForegroundColor(ConsoleColor.White)
                            .AppendFormatLine(
                                ServiceResources.ConsoleConnection_RunAsync_ServiceInstalledState,
                                service.ServiceName,
                                state)
                            .AppendResetForegroundColor()
                            .WriteToConsole();

                            options.Remove("I");

                            switch (state)
                            {
                            case ServiceControllerStatus.StopPending:
                            case ServiceControllerStatus.Stopped:
                                // Service is stopped or stopping.
                                options.Remove("C");
                                options.Remove("R");
                                options.Remove("T");
                                break;

                            case ServiceControllerStatus.StartPending:
                            case ServiceControllerStatus.ContinuePending:
                            case ServiceControllerStatus.Running:
                                // Service is starting or running.
                                options.Remove("S");
                                options.Remove("C");
                                break;

                            case ServiceControllerStatus.PausePending:
                            case ServiceControllerStatus.Paused:
                                // Service is paused or pausing.
                                options.Remove("S");
                                options.Remove("R");
                                options.Remove("T");
                                options.Remove("P");
                                break;

                            default:
                                // Service is not installed - shouldn't happen.
                                options.Remove("U");
                                options.Remove("S");
                                options.Remove("R");
                                options.Remove("T");
                                options.Remove("C");
                                options.Remove("P");
                                break;
                            }
                            options.Remove("V");
                            options.Remove("Y");
                            options.Remove("Z");
                        }
                        else
                        {
                            // No service installed.
                            options.Remove("U");
                            options.Remove("S");
                            options.Remove("R");
                            options.Remove("T");
                            options.Remove("P");
                            options.Remove("C");
                        }

                        _promptInstall.WriteToConsole(
                            null,
                            // ReSharper disable once PossibleNullReferenceException
                            (_, c) => !string.Equals(c.Tag, "options", StringComparison.CurrentCultureIgnoreCase)
                                ? Resolution.Unknown
                                : options);

                        string key;
                        do
                        {
                            key = Char.ToUpperInvariant(Console.ReadKey(true).KeyChar)
                                  .ToString(CultureInfo.InvariantCulture);
                        } while (!options.ContainsKey(key));

                        try
                        {
                            string userName;
                            string password;

                            switch (key)
                            {
                            case "I":
                                GetUserNamePassword(out userName, out password);

                                service.Install(ConsoleTextWriter.Default, userName, password);

                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_WaitInstall);
                                while (!Controller.ServiceIsInstalled(service.ServiceName))
                                {
                                    await Task.Delay(250, token).ConfigureAwait(false);

                                    Console.Write('.');
                                }
                                Console.WriteLine(ServiceResources.Done);
                                Console.WriteLine();
                                break;

                            case "U":
                                await service.Uninstall(ConsoleTextWriter.Default, token).ConfigureAwait(false);

                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_WaitUninstall);
                                while (Controller.ServiceIsInstalled(service.ServiceName))
                                {
                                    await Task.Delay(250, token).ConfigureAwait(false);

                                    Console.Write('.');
                                }
                                Console.WriteLine(ServiceResources.Done);
                                Console.WriteLine();
                                break;

                            case "R":
                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStop);
                                await Controller.StopService(service.ServiceName, token).ConfigureAwait(false);

                                Console.WriteLine(ServiceResources.Done);
                                Console.WriteLine();
                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStart);
                                await
                                Controller.StartService(service.ServiceName, null, token).ConfigureAwait(false);

                                Console.WriteLine(ServiceResources.Done);
                                Console.WriteLine();
                                break;

                            case "S":
                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStart);
                                await
                                Controller.StartService(service.ServiceName, null, token).ConfigureAwait(false);

                                Console.WriteLine(ServiceResources.Done);
                                Console.WriteLine();
                                break;

                            case "T":
                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStop);
                                await Controller.StopService(service.ServiceName, token).ConfigureAwait(false);

                                Console.WriteLine(ServiceResources.Done);
                                Console.WriteLine();
                                break;

                            case "P":
                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingPause);
                                await Controller.PauseService(service.ServiceName, token).ConfigureAwait(false);

                                Console.WriteLine(ServiceResources.Done);
                                break;

                            case "C":
                                Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingContinue);
                                await Controller.ContinueService(service.ServiceName, token).ConfigureAwait(false);

                                Console.WriteLine(ServiceResources.Done);
                                Console.WriteLine();
                                break;

                            case "V":
                                runMode = runMode.Set(RunMode.Start, true).Set(RunMode.Interactive, true);
                                done    = true;
                                break;

                            case "Y":
                                runMode = runMode.Set(RunMode.Interactive, true);
                                done    = true;
                                break;

                            case "W":
                                GetUserNamePassword(out userName, out password);
                                if (userName == null)
                                {
                                    break;
                                }
                                Debug.Assert(password != null);

                                Impersonator ei = impersonator;
                                impersonator = null;
                                if (ei != null)
                                {
                                    ei.Dispose();
                                }
                                // Run in new security context.
                                impersonator = new Impersonator(userName, password);
                                break;

                            case "Z":
                                runMode = runMode.Set(RunMode.Start, true).Clear(RunMode.Interactive, true);
                                done    = true;
                                Console.WriteLine(ServiceResources.ConsoleConnection_RunAsync_RunningNonInteractive);
                                Console.WriteLine(
                                    ServiceResources.ConsoleConnection_RunAsync_RunningNonInteractive2);
                                Console.WriteLine();
                                break;

                            default:
                                return;
                            }
                        }
                        catch (TaskCanceledException)
                        {
                            return;
                        }
                        catch (Exception e)
                        {
                            if (!token.IsCancellationRequested)
                            {
                                Log.Add(e);
                            }
                        }
                    } while (!done);
                }
                else if (!runMode.HasFlag(RunMode.Interactive))
                {
                    // If we don't show prompt and we're not interactive we should always start the service.
                    runMode = runMode.Set(RunMode.Start, true);
                }

                // Create connection
                Console.Title = ServiceResources.ConsoleConnection_RunAsync_RunningTitle + service.ServiceName;
                ConsoleConnection connection = new ConsoleConnection(defaultLogFormat, defaultLoggingLevels, token);
                Guid id = service.Connect(connection);

                // Combined cancellation tokens.
                ITokenSource tSource = token.CreateLinked(connection._cancellationTokenSource.Token);
                try
                {
                    CancellationToken t = tSource.Token;

                    if (t.IsCancellationRequested)
                    {
                        return;
                    }

                    if (runMode.HasFlag(RunMode.Start))
                    {
                        // Start the service
                        await service.StartService(ConsoleTextWriter.Default, null, t).ConfigureAwait(false);

                        if (t.IsCancellationRequested)
                        {
                            return;
                        }
                    }

                    if (!runMode.HasFlag(RunMode.Interactive))
                    {
                        // Wait to be cancelled as nothing to do.
                        await t.WaitHandle;
                        return;
                    }

                    do
                    {
                        // Flush logs
                        await Log.Flush(t).ConfigureAwait(false);

                        if (t.IsCancellationRequested)
                        {
                            break;
                        }

                        WritePrompt(service);
                        try
                        {
                            string commandLine = await Console.In.ReadLineAsync().ConfigureAwait(false);

                            if (!string.IsNullOrWhiteSpace(commandLine))
                            {
                                bool completed = false;
                                ICancelableTokenSource commandCancellationSource = t.ToCancelable();
                                CancellationToken      commandToken = commandCancellationSource.Token;

#pragma warning disable 4014
                                service.ExecuteAsync(id, commandLine, ConsoleTextWriter.Default, commandToken)
                                .ContinueWith(
                                    task =>
                                {
                                    Debug.Assert(task != null);

                                    completed = true;

                                    if (task.IsCompleted ||
                                        task.IsCanceled)
                                    {
                                        return;
                                    }

                                    if (task.IsFaulted)
                                    {
                                        Debug.Assert(task.Exception != null);
                                        _errorFormat.WriteToConsoleInstance(null, task.Exception);
                                    }
                                },
                                    TaskContinuationOptions.ExecuteSynchronously);
#pragma warning restore 4014

                                while (!completed)
                                {
                                    if (!commandCancellationSource.IsCancellationRequested &&
                                        Console.KeyAvailable &&
                                        Console.ReadKey(true).Key == ConsoleKey.Escape)
                                    {
                                        // Cancel command
                                        Console.Write(ServiceResources.ConsoleConnection_RunAsync_Cancelling);
                                        commandCancellationSource.Cancel();
                                        break;
                                    }
                                    await Task.Delay(100, token).ConfigureAwait(false);
                                }
                            }
                        }
                        catch (TaskCanceledException)
                        {
                            throw;
                        }
                        catch (Exception e)
                        {
                            if (!t.IsCancellationRequested)
                            {
                                _errorFormat.WriteToConsoleInstance(null, e);
                            }
                        }

                        // Let any async stuff done by the command have a bit of time, also throttle commands.
                        await Task.Delay(500, t).ConfigureAwait(false);
                    } while (!t.IsCancellationRequested);
                }
                catch (TaskCanceledException)
                {
                }
                finally
                {
                    tSource.Dispose();

                    // ReSharper disable MethodSupportsCancellation
                    Log.Flush().Wait();
                    // ReSharper restore MethodSupportsCancellation
                    service.Disconnect(id);
                    Console.WriteLine(ServiceResources.ConsoleConnection_RunAsync_PressKeyToExit);
                    Console.ReadKey(true);
                }
            }
            finally
            {
                if (impersonator != null)
                {
                    impersonator.Dispose();
                }
            }
        }
        // Dont link this list

        public static void Create(Bot.Bot bot)
        {
            var imp = new Impersonator();

            try
            {
                if (bot.UseWindowsUser)
                {
                    imp.Impersonate(bot.WindowsUserName, "localhost", bot.WindowsUserPassword);
                }

                bot.Status = "Create Diablo Clone";
                var basepath  = Path.GetDirectoryName(bot.Diablo.Location);
                var clonepath = Path.Combine(bot.DiabloCloneLocation, "Diablo III");

                // if diablo base path does not exist stop here!
                if (basepath != null && !Directory.Exists(basepath))
                {
                    bot.Stop();
                    throw new Exception("Diablo base directory does not exist!");
                }

                // Check if given language is installed on basepath
                var testpath = Path.Combine(basepath, @"Data_D3\PC\MPQs", General.GetLocale(bot.Diablo.Language));
                if (!Directory.Exists(testpath))
                {
                    bot.Stop();
                    throw new Exception($"ERROR: {bot.Diablo.Language} language is not installed (path: {testpath})");
                }


                // if diablo clone does not exist create it
                if (!Directory.Exists(Path.Combine(clonepath, @"Data_D3\PC\MPQs\Cache")))
                {
                    Logger.Instance.Write(bot, "Creating new Diablo Clone");
                    Directory.CreateDirectory(Path.Combine(clonepath, @"Data_D3\PC\MPQs\Cache"));
                }

                // Create Search caches
                var baseFileCache  = new FileListCache(basepath);
                var cloneFileCache = new FileListCache(clonepath);

                // Check if all links are made for our clone
                foreach (var p in baseFileCache.FileList)
                {
                    try
                    {
                        if (p.Directory && !Directory.Exists(Path.Combine(clonepath.ToLower(), p.Path.ToLower())))
                        {
                            if (!s_noLinks.Any(n => General.WildcardMatch(n.Source, p.Path)))
                            {
                                Logger.Instance.Write(bot, "NewLink: {0} -> {1}", Path.Combine(clonepath, p.Path),
                                                      Path.Combine(basepath, p.Path));
                                //if (!CreateSymbolicLink( Path.Combine(clonepath,p.Path),  Path.Combine(basepath,p.Path), 1))
                                //  throw new Exception("Failed to create link!");
                                Directory.CreateDirectory(Path.Combine(clonepath, p.Path));
                            }
                            continue;
                        }
                        if (!p.Directory && !File.Exists(Path.Combine(clonepath.ToLower(), p.Path.ToLower())))
                        {
                            if (!s_noLinks.Any(n => General.WildcardMatch(n.Source, p.Path)))
                            {
                                Logger.Instance.Write(bot, "NewLink: {0} -> {1}", Path.Combine(clonepath, p.Path),
                                                      Path.Combine(basepath, p.Path));
                                if (Path.GetExtension(Path.Combine(clonepath, p.Path)).ToLower().Equals(".exe"))
                                {
                                    if (
                                        !CreateHardLink(Path.Combine(clonepath, p.Path), Path.Combine(basepath, p.Path),
                                                        IntPtr.Zero))
                                    {
                                        throw new Exception("Failed to create link!");
                                    }
                                }
                                else
                                {
                                    if (
                                        !CreateSymbolicLink(Path.Combine(clonepath, p.Path),
                                                            Path.Combine(basepath, p.Path), 0))
                                    {
                                        throw new Exception("Failed to create link!");
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                    }
                }

                // Remove links that have no target

                /*
                 * foreach (var p in cloneFileCache.FileList)
                 * {
                 *  try
                 *  {
                 *      if (p.directory && !Directory.Exists(Path.Combine(basepath, p.Path)))
                 *      {
                 *          if (!_noLinks.Any(n => General.WildcardMatch(n.Source.ToLower(), p.Path.ToLower())))
                 *              Console.WriteLine("Delete: {0}", p.Path);
                 *          continue;
                 *      }
                 *
                 *      if (!p.directory && !File.Exists(Path.Combine(basepath.ToLower(), p.Path.ToLower())))
                 *      {
                 *          if (!_noLinks.Any(n => General.WildcardMatch(n.Source, p.Path)))
                 *              Console.WriteLine("Delete: {0}", p.Path);
                 *      }
                 *  }
                 *  catch (Exception ex)
                 *  {
                 *      Logger.Instance.Write(bot, ex.ToString());
                 *  }
                 * }
                 */
            }
            catch (Exception ex)
            {
                bot.Stop();
                DebugHelper.Write(bot, "Failed to create clone!");
                DebugHelper.Exception(ex);
            }
            imp.Dispose();
        }