/// <summary>
        /// Loads the next track and plays it
        /// </summary>
        /// <param name="ignoreSingleRepeat">Ignore repeating a single track</param>
        /// <param name="startPlayback">Starts the playback</param>
        /// <seealso cref="Shuffle"/>
        /// <seealso cref="Repeat"/>
        /// <seealso cref="HistoryIndex"/>
        /// <seealso cref="Queue"/>
        public static void Next(bool ignoreSingleRepeat = false, bool startPlayback = false)
        {
            try
            {
                // repeat current track?
                if (SettingsManager.Repeat == RepeatState.RepeatOne && SettingsManager.CurrentTrack != null && !ignoreSingleRepeat)
                {
                    TrackData t = SettingsManager.CurrentTrack;
                    Stop();
                    Load(t);
                    Play();
                    return;
                }

                TrackData nextTrack = null;
                List<TrackData> trackCollection = FetchCollectionCallback();

                if (trackCollection == null)
                {
                    Stop();
                    return;
                }

                if (SettingsManager.CurrentTrack == null)
                {
                    if (trackCollection.Count > 0)
                    {
                        nextTrack = (TrackData)trackCollection[0];
                        SettingsManager.HistoryIndex = SettingsManager.HistoryTracks.Count;
                    }
                    else
                        return;
                }

                // play track from history?
                if (SettingsManager.HistoryIndex < SettingsManager.HistoryTracks.Count - 1)
                {
                    nextTrack = SettingsManager.HistoryTracks.ElementAt<TrackData>(SettingsManager.HistoryIndex + 1);
                }

                // play track from queue?
                else if (SettingsManager.QueueTracks.Count > 0)
                    nextTrack = SettingsManager.QueueTracks.First<TrackData>();

                // play track from track collection
                else
                {
                    if (trackCollection.Count < 1) // no track found, nothing more to do here...
                    {
                        Stop();
                        return;
                    }

                    // apply search filter
                    bool ApplySearch = (SearchMatch != null && Filter != null && Filter != "" && Filter != U.T("PlaybackSearch"));

                    // remove all songs we have already played
                    ObservableCollection<TrackData> tracksLeft = new ObservableCollection<TrackData>();
                    foreach (TrackData t in trackCollection)
                        if (!songsAlreadyPlayed.Contains(t) &&
                            (!ApplySearch || SearchMatch(t, Filter)))
                            tracksLeft.Add(t);

                    if (tracksLeft.Count < 1) // we have played all songs
                    {
                        songsAlreadyPlayed.Clear();
                        if (SettingsManager.Repeat == RepeatState.RepeatAll) // we have repeat on, so we add all track again and start over
                        {
                            foreach (TrackData t in trackCollection)
                                if (!ApplySearch || SearchMatch(t, Filter))
                                    tracksLeft.Add(t);
                        }
                        else // repeat is off, so we stop playing
                        {
                            Stop();
                            return;
                        }
                    }

                    if (SettingsManager.Shuffle) // shuffle is on, so we find a random song
                    {
                        Random r = new Random();
                        int x = r.Next(tracksLeft.Count - 1);
                        nextTrack = tracksLeft.ElementAt<TrackData>(x);
                    }
                    else // shuffle is off, so we get the next song in the list
                    {
                        if (trackCollection.Count <= 0)
                            return;

                        // find CurrentTrack in TrackCollection (Contains() cannot be used since CurrentTrack may be a copy)
                        int i = -1;
                        if (SettingsManager.CurrentTrack != null)
                        {
                            foreach (TrackData t in trackCollection)
                            {
                                if (t.Path == SettingsManager.CurrentTrack.Path)
                                {
                                    i = trackCollection.IndexOf(t);
                                    break;
                                }
                            }
                        }

                        if (SettingsManager.CurrentTrack != null && i >= 0)
                        {
                            if (i >= trackCollection.Count - 1)
                                i = -1;
                            nextTrack = (TrackData)trackCollection[i + 1];
                        }
                        else
                            nextTrack = (TrackData)trackCollection[0];
                    }
                }

                DispatchTrackSwitched(GetSourceTrack(SettingsManager.CurrentTrack), nextTrack);

                // if we are playing we start to play the next track
                if (SettingsManager.MediaState == MediaState.Playing || startPlayback)
                {
                    Stop();
                    Load(nextTrack);
                    Play();
                }

                // otherwise we just change the track
                else
                {
                    Load(nextTrack);
                    SettingsManager.CurrentTrack = nextTrack;
                }
            }
            catch (Exception e)
            {
                U.L(LogLevel.Warning, "MEDIA", "Could not select next track: " + e.Message);
                Stop();
            }
        }
        private ObservableCollection<ScreenCategoryButton> CreateCategoryButtons(ScreenMenu screenMenu)
        {
            if (screenMenu != null)
            {
                if (MenuItems != null) MenuItems.Clear();

                _currentScreenMenu = screenMenu;
                var result = new ObservableCollection<ScreenCategoryButton>();

                foreach (var category in screenMenu.Categories.OrderBy(x => x.Order).Where(x => !x.MostUsedItemsCategory))
                {
                    var sButton = new ScreenCategoryButton(category, CategoryCommand);
                    result.Add(sButton);
                }

                if (result.Count > 0)
                {
                    var c = result.First();
                    if (_selectedCategory != null)
                        c = result.SingleOrDefault(x => x.Category.Name.ToLower() == _selectedCategory.Name.ToLower());
                    if (c == null && result.Count > 0) c = result.ElementAt(0);
                    if (c != null) OnCategoryCommandExecute(c.Category);
                }

                return result;
            }

            if (MenuItems != null) MenuItems.Clear();
            if (Categories != null) Categories.Clear();
            _currentScreenMenu = null;

            return Categories;
        }
		private void HandleMainWindowLoadedMessage(MainWindowLoadedMessage msg)
		{
			DispatcherHelper.CheckBeginInvokeOnUI(
			async () =>
			{
				List<string> folders = await _cacheService.GetFoldersAsync();
				Folders = new ObservableCollection<string>(folders);

				IsShowAllFolders = Properties.Settings.Default.ShowAllFolders;

				if (IsShowAllFolders)
				{
					SelectedFolder = null;
				}
				else if (!string.IsNullOrWhiteSpace(Properties.Settings.Default.LastFolder))
				{
					SelectedFolder = Properties.Settings.Default.LastFolder;
				}
				else if (Folders.Count > 0)
				{
					SelectedFolder = Folders.ElementAt(0);
				}
				
				await LoadDemosHeader();
				await RefreshLastRankAccount();
				if (!AppSettings.IsInternetConnectionAvailable()) await RefreshBannedPlayerCount();

				Messenger.Default.Register<RefreshDemosMessage>(this, HandleRefreshDemosMessage);
				Messenger.Default.Register<SelectedAccountChangedMessage>(this, HandleSelectedAccountChangedMessage);
				_isMainWindowLoaded = true;
			});
		}
		private void HandleMainWindowLoadedMessage(MainWindowLoadedMessage msg)
		{
			DispatcherHelper.CheckBeginInvokeOnUI(
			async () =>
			{
				List<string> folders = await _cacheService.GetFoldersAsync();
				Folders = new ObservableCollection<string>(folders);

				IsShowAllFolders = Properties.Settings.Default.ShowAllFolders;

				if (IsShowAllFolders)
				{
					SelectedFolder = null;
				}
				else if (!string.IsNullOrWhiteSpace(Properties.Settings.Default.LastFolder))
				{
					SelectedFolder = Properties.Settings.Default.LastFolder;
				}
				else if (Folders.Count > 0)
				{
					SelectedFolder = Folders.ElementAt(0);
				}

				if (!AppSettings.IsInternetConnectionAvailable()) return;

				HasNotification = true;
				IsBusy = true;
				NotificationMessage = "Checking for new banned suspects...";
				await RefreshBannedPlayerCount();
				HasNotification = false;
				IsBusy = false;
			});
		}
    /// <summary>
    /// Get the link to the weather icon for all the time periods
    /// </summary>
    /// <param name="xmlWeather"></param>
    /// <param name="newForecastList"></param>
    private void GetWeatherIcon(XElement xmlWeather, ObservableCollection<ForecastPeriod> newForecastList)
    {
        XElement xmlCurrent = default(XElement);
        int elementIndex = 0;

        // get a link to the weather icon for each time period
        xmlCurrent = xmlWeather.Descendants("conditions-icon").First();

        foreach (XElement curElement in xmlCurrent.Elements("icon-link")) {
            try {
                newForecastList.ElementAt(elementIndex).ConditionIcon = Convert.ToString(curElement.Value);
            } catch (FormatException e1) {
                newForecastList.ElementAt(elementIndex).ConditionIcon = "";
            }

            elementIndex += 1;
        }
    }
    /// <summary>
    /// Get the long text forecast for all the time periods
    /// </summary>
    private void GetTextForecast(XElement xmlWeather, ObservableCollection<ForecastPeriod> newForecastList)
    {
        XElement xmlCurrent = default(XElement);
        int elementIndex = 0;

        // get a text forecast for each time period
        xmlCurrent = xmlWeather.Descendants("wordedForecast").First();

        foreach (XElement curElement in xmlCurrent.Elements("text")) {
            try {
                newForecastList.ElementAt(elementIndex).TextForecast = Convert.ToString(curElement.Value);
            } catch (FormatException e1) {
                newForecastList.ElementAt(elementIndex).TextForecast = "";
            }

            elementIndex += 1;
        }
    }
    /// <summary>
    /// Get the minimum and maximum temperatures for all the time periods
    /// </summary>
    private void GetMinMaxTemperatures(XElement xmlWeather, ObservableCollection<ForecastPeriod> newForecastList)
    {
        XElement xmlCurrent = default(XElement);

        // Find the temperature parameters.   if first time period is "Tonight",
        // then the Daily Minimum Temperatures are listed first.
        // Otherwise the Daily Maximum Temperatures are listed first
        xmlCurrent = xmlWeather.Descendants("parameters").First();

        int minTemperatureIndex = 1;
        int maxTemperatureIndex = 0;

        // If "Tonight" is the first time period, then store Daily Minimum
        // Temperatures first, then Daily Maximum Temperatuers next
        if (newForecastList.ElementAt(0).TimeName == "Tonight") {
            minTemperatureIndex = 0;
            maxTemperatureIndex = 1;

            // get the Daily Minimum Temperatures
            foreach (XElement curElement in xmlCurrent.Elements("temperature").ElementAt(0).Elements("value")) {
                newForecastList.ElementAt(minTemperatureIndex).Temperature = int.Parse(curElement.Value);

                minTemperatureIndex += 2;
            }

            // then get the Daily Maximum Temperatures
            foreach (XElement curElement in xmlCurrent.Elements("temperature").ElementAt(1).Elements("value")) {
                newForecastList.ElementAt(maxTemperatureIndex).Temperature = int.Parse(curElement.Value);

                maxTemperatureIndex += 2;
            }

        // otherwise we have a daytime time period first
        } else {
            // get the Daily Maximum Temperatures
            foreach (XElement curElement in xmlCurrent.Elements("temperature").ElementAt(0).Elements("value")) {
                newForecastList.ElementAt(maxTemperatureIndex).Temperature = int.Parse(curElement.Value);

                maxTemperatureIndex += 2;
            }

            // then get the Daily Minimum Temperatures
            foreach (XElement curElement in xmlCurrent.Elements("temperature").ElementAt(1).Elements("value")) {
                newForecastList.ElementAt(minTemperatureIndex).Temperature = int.Parse(curElement.Value);

                minTemperatureIndex += 2;
            }
        }
    }
    /// <summary>
    /// Get the current conditions for all the time periods
    /// </summary>
    private void GetCurrentConditions(XElement xmlWeather, ObservableCollection<ForecastPeriod> newForecastList)
    {
        XElement xmlCurrent = default(XElement);
        int elementIndex = 0;

        // now get the current weather conditions for each time period
        xmlCurrent = xmlWeather.Descendants("weather").First();

        foreach (XElement curElement in xmlCurrent.Elements("weather-conditions")) {
            try {
                newForecastList.ElementAt(elementIndex).WeatherType = Convert.ToString(curElement.Attribute("weather-summary").Value);
            } catch (FormatException e1) {
                newForecastList.ElementAt(elementIndex).WeatherType = "";
            }

            elementIndex += 1;
        }
    }
    /// <summary>
    /// Get the chance of precipitation for all the time periods
    /// </summary>
    private void GetChancePrecipitation(XElement xmlWeather, ObservableCollection<ForecastPeriod> newForecastList)
    {
        XElement xmlCurrent = default(XElement);

        // now find the probablity of precipitation for each time period
        xmlCurrent = xmlWeather.Descendants("probability-of-precipitation").First();

        int elementIndex = 0;

        foreach (XElement curElement in xmlCurrent.Elements("value")) {
            try {
                newForecastList.ElementAt(elementIndex).ChancePrecipitation = int.Parse(curElement.Value);
            // some values are nil
            } catch (FormatException e1) {
                newForecastList.ElementAt(elementIndex).ChancePrecipitation = 0;
            }

            elementIndex += 1;
        }
    }