/// <summary>
        /// Called PageLoaded (after navigating to a specific url) or from
        /// Task timer, if there is no url/bot is already on the url
        /// </summary>
        /// <param name="acc">Account</param>
        /// <param name="task">Task to be executed</param>
        /// <returns></returns>
        public static async Task Execute(Account acc, BotTask task)
        {
            //Before every execution, wait a random delay. TODO: needed?
            if (task.PostTaskCheck == null)
            {
                task.PostTaskCheck = new List <Action <HtmlDocument, Account> >();
            }
            if (acc.Wb?.CurrentUrl == null)
            {
                await acc.Wb.Navigate($"{acc.AccInfo.ServerUrl}/dorf1.php");
            }
            task.ErrorMessage = null;
            //Console.WriteLine($"Executing task {task.GetType()}");
            if (task.vill == null)
            {
                task.vill = acc.Villages.FirstOrDefault(x => x.Active);
            }
            try
            {
                switch (await task.Execute(acc.Wb.Html, acc.Wb.Driver, acc))
                {
                case TaskRes.Retry:
                    if (task.ErrorMessage != null)
                    {
                        Utils.log.Warning(LogHelper(acc, task, "warning") + "\n" + task.ErrorMessage);
                    }

                    // There was probably a problem, retry executing the task later.
                    task.RetryCounter++;
                    if (task.NextExecute == null)
                    {
                        task.NextExecute = DateTime.Now.AddMinutes(3);
                    }
                    break;

                default:
                    break;
                }
            }
            catch (Exception e)
            {
                Utils.log.Error(LogHelper(acc, task, "error") + $"\nStack Trace:\n{e.StackTrace}\n\nMessage:" + e.Message + "\n------------------------\n");
                task.RetryCounter++;
                if (task.NextExecute == null)
                {
                    task.NextExecute = DateTime.Now.AddMinutes(3);
                }
            }

            // Execute post tasks
            PostTask(task, acc);

            //We want to re-execute the same task later
            if (task.NextExecute != null && task.RetryCounter < 3)
            {
                task.ExecuteAt       = task.NextExecute ?? default;
                task.NextExecute     = null;
                task.Stage           = TaskStage.Start;
                task.DurationCounter = 0;
                task.RetryCounter    = 0;
                ReorderTaskList(acc);
                return;
            }
            // Remove the task from the task list
            acc.Tasks.Remove(task);
        }
        /// <summary>
        /// Called PageLoaded (after navigating to a specific url) or from
        /// Task timer, if there is no url/bot is already on the url
        /// </summary>
        /// <param name="acc">Account</param>
        /// <param name="task">Task to be executed</param>
        /// <returns></returns>
        public static async Task Execute(Account acc, BotTask task)
        {
            // Before every execution, wait a random delay
            if (acc.AccInfo.ServerVersion == Classificator.ServerVersionEnum.T4_5)
            {
                await Task.Delay(AccountHelper.Delay());
            }

            if (acc.Wb?.CurrentUrl == null && task.GetType() != typeof(CheckProxy))
            {
                await acc.Wb.Navigate($"{acc.AccInfo.ServerUrl}/dorf1.php");
            }

            if (task.Vill == null)
            {
                task.Vill = acc.Villages.FirstOrDefault(x => x.Active);
            }

            try
            {
                acc.Wb.Log($"Executing task {task.GetName()}" + (task.Vill == null ? "" : $" in village {task.Vill.Name}"));

                switch (await task.Execute(acc))
                {
                case TaskRes.Retry:
                    task.RetryCounter++;
                    if (task.NextExecute == null)
                    {
                        task.NextExecute = DateTime.Now.AddMinutes(3);
                    }
                    break;

                default:
                    task.RetryCounter = 0;
                    if (task.NextTask != null)
                    {
                        task.NextTask.ExecuteAt = DateTime.MinValue.AddHours(5);
                        task.NextTask.Stage     = TaskStage.Start;
                        TaskExecutor.AddTask(acc, task.NextTask);
                        task.NextTask = null;
                    }
                    break;
                }
            }
            catch (Exception e)
            {
                if (acc.Wb != null)
                {
                    acc.Wb.Log($"Error executing task {task.GetName()}! Vill {task.Vill?.Name}", e);
                }
                task.RetryCounter++;
                if (task.NextExecute == null)
                {
                    task.NextExecute = DateTime.Now.AddMinutes(3);
                }
            }

            //We want to re-execute the same task later
            if (task.NextExecute != null && task.RetryCounter < 3)
            {
                task.ExecuteAt   = task.NextExecute ?? default;
                task.NextExecute = null;
                ReorderTaskList(acc);
                task.Stage = TaskStage.Start;
                return;
            }
            // Remove the task from the task list
            acc.Tasks.Remove(task);
        }
        /// <summary>
        /// Called PageLoaded (after navigating to a specific url) or from
        /// Task timer, if there is no url/bot is already on the url
        /// </summary>
        /// <param name="acc">Account</param>
        /// <param name="task">Task to be executed</param>
        /// <returns></returns>
        public static async Task Execute(Account acc, BotTask task)
        {
            // Before every execution, wait a random delay
            await Task.Delay(AccountHelper.Delay());

            if (task.Vill == null)
            {
                task.Vill = acc.Villages.FirstOrDefault(x => x.Active);
            }

            acc.Logger.Information($"Executing task {task.GetName()}" + (task.Vill == null ? "" : $" in village {task.Vill.Name}"));

            try
            {
                switch (await task.Execute(acc))
                {
                case TaskRes.Retry:
                    task.RetryCounter++;
                    if (task.NextExecute == null)
                    {
                        task.NextExecute = DateTime.Now.AddMinutes(3);
                    }
                    break;

                default:
                    task.RetryCounter = 0;
                    if (task.NextTask != null)
                    {
                        task.NextTask.ExecuteAt = DateTime.MinValue.AddHours(5);
                        task.NextTask.Stage     = TaskStage.Start;
                        acc.Tasks.Add(task.NextTask);
                        task.NextTask = null;
                    }
                    break;
                }
            }
            catch (WebDriverException e) when(e.Message.Contains("chrome not reachable") || e.Message.Contains("no such window:"))
            {
                acc.Logger.Warning($"Chrome has problem while executing task {task.GetName()}! Vill {task.Vill?.Name}. Try reopen Chrome");

                acc.Tasks.Add(new ReopenDriver()
                {
                    ExecuteAt = DateTime.MinValue,
                    Priority  = TaskPriority.High,
                    ReopenAt  = DateTime.MinValue
                });

                //try exccute task after we reopen chrome 1 mintues

                if (task.NextExecute == null)
                {
                    task.NextExecute = DateTime.MinValue.AddMinutes(1);                           // make sure current task is excuted after reopen driver
                }
            }
            catch (Exception e)
            {
                acc.Logger.Error(e, $"Error executing task {task.GetName()}! Vill {task.Vill?.Name}");
                task.RetryCounter++;
                if (task.NextExecute == null)
                {
                    task.NextExecute = DateTime.Now.AddMinutes(3);
                }
            }

            //We want to re-execute the same task later
            if (task.NextExecute != null && task.RetryCounter < 3)
            {
                task.ExecuteAt   = task.NextExecute ?? default;
                task.NextExecute = null;
                acc.Tasks.ReOrder();

                task.Stage = TaskStage.Start;
                acc.Logger.Warning($"Task {task.GetName()}" + (task.Vill == null ? "" : $" in village {task.Vill.Name} will be re-executed at {task.ExecuteAt}"));
                return;
            }
            // Remove the task from the task list
            acc.Tasks.Remove(task);
            if (task.RetryCounter >= 3)
            {
                acc.Logger.Warning($"Task {task.GetName()}" + (task.Vill == null ? "" : $" in village {task.Vill.Name} is already re-executed 3 times. Ignore it"));
            }
            else
            {
                acc.Logger.Information($"Task {task.GetName()}" + (task.Vill == null ? "" : $" in village {task.Vill.Name} is done."));
            }
        }