// TODO IMPLEMENT REMOVE

        //public bool RemoveFromBenchmarking(params string[] removeKeys)
        //{
        //    bool allRemoved = true;
        //    foreach (var key in removeKeys)
        //    {
        //        allRemoved &= _benchmarkAlgorithms.TryRemove(key, out var _);
        //    }
        //    return allRemoved;
        //}

        //// TODO on plugin updates update algorithms and stop benchmarking if the current active algorithm is in the update array
        //public bool UpdateForBenchmarking(params AlgorithmContainer[] algorithmContainers)
        //{
        //    throw new NotImplementedException();
        //    StopCurrentAlgorithmBenchmark();
        //}

        public async Task Benchmark()
        {
            bool showFailed  = false;
            bool startMining = false;

            try
            {
                BenchmarkingHandlers.TryAdd(Device, this);
                // whole benchmark scope
                using (_stopBenchmark = CancellationTokenSource.CreateLinkedTokenSource(ApplicationStateManager.ExitApplication.Token))
                {
                    // Until container is not empty
                    while (!_benchmarkAlgorithms.IsEmpty)
                    {
                        if (_stopBenchmark.IsCancellationRequested)
                        {
                            break;
                        }
                        // per algo benchmark scope
                        using (_stopCurrentAlgorithmBenchmark = CancellationTokenSource.CreateLinkedTokenSource(_stopBenchmark.Token))
                        {
                            try
                            {
                                // this will drain the container
                                await BenchmarkNextAlgorithm(_stopCurrentAlgorithmBenchmark.Token);

                                await Task.Delay(ConfigManager.GeneralConfig.MinerRestartDelayMS, _stopCurrentAlgorithmBenchmark.Token);

                                BenchmarkManager.StepUpBenchmarkStepProgress();
                            }
                            catch (Exception e)
                            {
                                Logger.Error("BenchmarkHandler", $"Exception occurred in benchmark task: {e.Message}");
                            }
                        }
                    }
                    AfterBenchmarkCleanup();
                    // TODO set device to stopped if not mining after benchmark
                    // TODO start mining after benchmark
                    var cancel = _stopBenchmark.IsCancellationRequested;
                    // don't show unbenchmarked algos if user canceled
                    showFailed  = _benchmarkFailedAlgo.Count > 0 && !cancel;
                    startMining = StartMiningAfterBenchmark && !cancel;
                }
            }
            finally
            {
                BenchmarkingHandlers.TryRemove(Device, out var _);
                Device.State = DeviceState.Stopped;
                BenchmarkManager.EndBenchmarkForDevice(Device, showFailed, startMining);
            }
        }
        private async Task BenchmarkAlgorithm(AlgorithmContainer algo, CancellationToken stop)
        {
            using (var powerHelper = new PowerHelper(algo.ComputeDevice))
            {
                BenchmarkManager.AddToStatusCheck(algo.ComputeDevice, algo);
                var plugin     = algo.PluginContainer;
                var miner      = plugin.CreateMiner();
                var miningPair = new MinerPlugin.MiningPair
                {
                    Device    = algo.ComputeDevice.BaseDevice,
                    Algorithm = algo.Algorithm
                };
                // check ethlargement
                var miningPairs = new List <MinerPlugin.MiningPair> {
                    miningPair
                };
                EthlargementIntegratedPlugin.Instance.Start(miningPairs);
                miner.InitMiningPairs(miningPairs);
                // fill service since the benchmark might be online. DemoUser.BTC must be used
                miner.InitMiningLocationAndUsername(StratumService.SelectedServiceLocation, DemoUser.BTC);
                powerHelper.Start();
                algo.ComputeDevice.State = DeviceState.Benchmarking;
                var result = await miner.StartBenchmark(stop, PerformanceType);

                //EthlargementIntegratedPlugin.Instance.Stop(miningPairs); // TODO check stopping
                var power = powerHelper.Stop();
                if (result.Success || result.AlgorithmTypeSpeeds?.Count > 0)
                {
                    var ids    = result.AlgorithmTypeSpeeds.Select(ats => ats.AlgorithmType).ToList();
                    var speeds = result.AlgorithmTypeSpeeds.Select(ats => ats.Speed).ToList();
                    algo.Speeds     = speeds;
                    algo.PowerUsage = power;
                    // set status to empty string it will return speed
                    BenchmarkManager.SetCurrentStatus(algo.ComputeDevice, algo, "");
                    ConfigManager.CommitBenchmarksForDevice(algo.ComputeDevice);
                }
                else
                {
                    // mark it as failed
                    algo.LastBenchmarkingFailed = true;
                    // add new failed list
                    _benchmarkFailedAlgo.Add(algo.AlgorithmName);
                    algo.SetError(result.ErrorMessage);
                    BenchmarkManager.SetCurrentStatus(algo.ComputeDevice, algo, result.ErrorMessage);
                }
            }
        }
        private async Task Benchmark()
        {
            AlgorithmContainer currentAlgorithm = null;

            while (_benchmarkAlgorithmQueue.Count > 0)
            {
                try
                {
                    if (_stopBenchmark.IsCancellationRequested)
                    {
                        break;
                    }
                    currentAlgorithm = _benchmarkAlgorithmQueue.Dequeue();
                    BenchmarkManager.AddToStatusCheck(Device, currentAlgorithm);
                    currentAlgorithm.InBenchmark = true;
                    await BenchmarkAlgorithm(currentAlgorithm);

                    currentAlgorithm.InBenchmark = false;
                    await Task.Delay(ConfigManager.GeneralConfig.MinerRestartDelayMS);

                    if (_stopBenchmark.IsCancellationRequested)
                    {
                        break;
                    }
                    currentAlgorithm.IsReBenchmark = false;
                    BenchmarkManager.StepUpBenchmarkStepProgress();
                    ConfigManager.CommitBenchmarksForDevice(Device);
                }
                catch (Exception e)
                {
                    Logger.Error("BenchmarkHandler", $"Exception occurred in benchmark task: {e.Message}");
                }
            }
            currentAlgorithm?.ClearBenchmarkPending();
            var cancel = _stopBenchmark.IsCancellationRequested;
            // don't show unbenchmarked algos if user canceled
            var showFailed  = _benchmarkFailedAlgo.Count > 0 && !cancel;
            var startMining = _startMiningAfterBenchmark && !cancel;

            BenchmarkManager.EndBenchmarkForDevice(Device, showFailed, startMining);
        }
        private async Task BenchmarkNextAlgorithm(CancellationToken stop)
        {
            var nextAlgo = TakeNextAlgorithm();

            if (nextAlgo == null)
            {
                Logger.Error("BenchmarkingComputeDeviceHandler.BenchmarkNextAlgorithm", $"TakeNextAlgorithm returned null");
                return;
            }
            try
            {
                BenchmarkManager.AddToStatusCheck(nextAlgo.ComputeDevice, nextAlgo);
                nextAlgo.InBenchmark = true;
                await BenchmarkAlgorithm(nextAlgo, stop);
            }
            finally
            {
                nextAlgo.ClearBenchmarkPending();
                nextAlgo.InBenchmark = false;
            }
        }