using UnityEngine;
using System.Linq;
using System;
using System.Collections.Generic;
using UnityEngine.UI;
using HMUI;
using VRUI;
using SongBrowserPlugin.DataAccess;
using System.IO;
using SongLoaderPlugin;
namespace SongBrowserPlugin.UI
{
///
/// Hijack the flow coordinator. Have access to all StandardLevel easily.
///
public class SongBrowserUI : MonoBehaviour
{
// Logging
public const String Name = "SongBrowserUI";
private Logger _log = new Logger(Name);
// Beat Saber UI Elements
StandardLevelSelectionFlowCoordinator _levelSelectionFlowCoordinator;
StandardLevelListViewController _levelListViewController;
StandardLevelDetailViewController _levelDetailViewController;
StandardLevelDifficultyViewController _levelDifficultyViewController;
StandardLevelSelectionNavigationController _levelSelectionNavigationController;
// New UI Elements
private List _sortButtonGroup;
private Button _addFavoriteButton;
private String _addFavoriteButtonText = null;
private SimpleDialogPromptViewController _simpleDialogPromptViewControllerPrefab;
private SimpleDialogPromptViewController _deleteDialog;
private Button _deleteButton;
// Debug
private int _sortButtonLastPushedIndex = 0;
private int _lastRow = 0;
// Model
private SongBrowserModel _model;
///
/// Constructor
///
public SongBrowserUI() : base()
{
if (_model == null)
{
_model = new SongBrowserModel();
}
_model.Init();
_sortButtonLastPushedIndex = (int)(_model.Settings.sortMode);
}
///
/// Builds the UI for this plugin.
///
public void CreateUI()
{
_log.Trace("CreateUI()");
try
{
if (_levelSelectionFlowCoordinator == null)
{
_levelSelectionFlowCoordinator = Resources.FindObjectsOfTypeAll().First();
}
if (_levelListViewController == null)
{
_levelListViewController = _levelSelectionFlowCoordinator.GetPrivateField("_levelListViewController");
}
if (_levelDetailViewController == null)
{
_levelDetailViewController = _levelSelectionFlowCoordinator.GetPrivateField("_levelDetailViewController");
}
if (_levelSelectionNavigationController == null)
{
_levelSelectionNavigationController = _levelSelectionFlowCoordinator.GetPrivateField("_levelSelectionNavigationController");
}
if (_levelDifficultyViewController == null)
{
_levelDifficultyViewController = _levelSelectionFlowCoordinator.GetPrivateField("_levelDifficultyViewController");
}
_simpleDialogPromptViewControllerPrefab = Resources.FindObjectsOfTypeAll().First();
this._deleteDialog = UnityEngine.Object.Instantiate(this._simpleDialogPromptViewControllerPrefab);
this._deleteDialog.gameObject.SetActive(false);
this.CreateUIElements();
_levelListViewController.didSelectLevelEvent += OnDidSelectLevelEvent;
}
catch (Exception e)
{
_log.Exception("Exception during CreateUI: ", e);
}
}
///
/// Builds the SongBrowser UI
///
private void CreateUIElements()
{
_log.Trace("CreateUIElements");
try
{
RectTransform rect = this._levelSelectionNavigationController.transform as RectTransform;
// Create Sorting Songs By-Buttons
_log.Debug("Creating sort by buttons...");
System.Action onSortButtonClickEvent = delegate (SongSortMode sortMode) {
_log.Debug("Sort button - {0} - pressed.", sortMode.ToString());
SongBrowserModel.LastSelectedLevelId = null;
_model.Settings.sortMode = sortMode;
_model.Settings.Save();
UpdateSongList();
RefreshSongList();
};
_sortButtonGroup = new List
{
UIBuilder.CreateSortButton(rect, "PlayButton", "Favorite", 3, "AllDirectionsIcon", 66, 74.5f, 16f, 5f, SongSortMode.Favorites, onSortButtonClickEvent),
UIBuilder.CreateSortButton(rect, "PlayButton", "Song", 3, "AllDirectionsIcon", 50f, 74.5f, 16f, 5f, SongSortMode.Default, onSortButtonClickEvent),
UIBuilder.CreateSortButton(rect, "PlayButton", "Author", 3, "AllDirectionsIcon", 34f, 74.5f, 16f, 5f, SongSortMode.Author, onSortButtonClickEvent),
UIBuilder.CreateSortButton(rect, "PlayButton", "Original", 3, "AllDirectionsIcon", 18f, 74.5f, 16f, 5f, SongSortMode.Original, onSortButtonClickEvent),
UIBuilder.CreateSortButton(rect, "PlayButton", "Newest", 3, "AllDirectionsIcon", 2f, 74.5f, 16f, 5f, SongSortMode.Newest, onSortButtonClickEvent),
};
// Creaate Add to Favorites Button
_log.Debug("Creating add to favorites button...");
RectTransform transform = this._levelDetailViewController.transform as RectTransform;
_addFavoriteButton = UIBuilder.CreateUIButton(transform, "QuitButton", SongBrowserApplication.Instance.ButtonTemplate);
(_addFavoriteButton.transform as RectTransform).anchoredPosition = new Vector2(40f, 5.75f);
(_addFavoriteButton.transform as RectTransform).sizeDelta = new Vector2(10f, 10f);
UIBuilder.SetButtonText(ref _addFavoriteButton, _addFavoriteButtonText);
UIBuilder.SetButtonTextSize(ref _addFavoriteButton, 3);
UIBuilder.SetButtonIconEnabled(ref _addFavoriteButton, false);
_addFavoriteButton.onClick.RemoveAllListeners();
_addFavoriteButton.onClick.AddListener(delegate () {
ToggleSongInFavorites();
});
if (_addFavoriteButtonText == null)
{
IStandardLevel level = this._levelListViewController.selectedLevel;
if (level != null)
{
RefreshAddFavoriteButton(level.levelID);
}
}
// Create delete button
_log.Debug("Creating delete button...");
transform = this._levelDetailViewController.transform as RectTransform;
_deleteButton = UIBuilder.CreateUIButton(transform, "QuitButton", SongBrowserApplication.Instance.ButtonTemplate);
(_deleteButton.transform as RectTransform).anchoredPosition = new Vector2(46f, 0f);
(_deleteButton.transform as RectTransform).sizeDelta = new Vector2(15f, 5f);
UIBuilder.SetButtonText(ref _deleteButton, "Delete");
UIBuilder.SetButtonTextSize(ref _deleteButton, 3);
UIBuilder.SetButtonIconEnabled(ref _deleteButton, false);
_deleteButton.onClick.RemoveAllListeners();
_deleteButton.onClick.AddListener(delegate () {
HandleDeleteSelectedLevel();
});
RefreshUI();
}
catch (Exception e)
{
_log.Exception("Exception CreateUIElements:", e);
}
}
///
/// Adjust UI based on level selected.
/// Various ways of detecting if a level is not properly selected. Seems most hit the first one.
///
private void OnDidSelectLevelEvent(StandardLevelListViewController view, IStandardLevel level)
{
_log.Trace("OnDidSelectLevelEvent({0}", level.levelID);
if (level == null)
{
_log.Debug("No level selected?");
return;
}
if (_model.Settings == null)
{
_log.Debug("Settings not instantiated yet?");
return;
}
SongBrowserModel.LastSelectedLevelId = level.levelID;
RefreshAddFavoriteButton(level.levelID);
}
///
/// Pop up a delete dialog.
///
private void HandleDeleteSelectedLevel()
{
IStandardLevel level = this._levelListViewController.selectedLevel;
if (level == null)
{
_log.Info("No level selected, cannot delete nothing...");
return;
}
if (level.levelID.StartsWith("Level"))
{
_log.Debug("Cannot delete non-custom levels.");
return;
}
SongLoaderPlugin.OverrideClasses.CustomLevel customLevel = _model.LevelIdToCustomSongInfos[level.levelID];
this._deleteDialog.Init("Delete level warning!", String.Format("Permanently delete level: {0}\n Do you want to continue?", customLevel.songName), "YES", "NO");
this._deleteDialog.didFinishEvent += this.HandleDeleteDialogPromptViewControllerDidFinish;
this._levelSelectionNavigationController.PresentModalViewController(this._deleteDialog, null, false);
}
///
/// Handle delete dialog resolution.
///
///
///
public void HandleDeleteDialogPromptViewControllerDidFinish(SimpleDialogPromptViewController viewController, bool ok)
{
viewController.didFinishEvent -= this.HandleDeleteDialogPromptViewControllerDidFinish;
if (!ok)
{
viewController.DismissModalViewController(null, false);
}
else
{
IStandardLevel level = this._levelListViewController.selectedLevel;
SongLoaderPlugin.OverrideClasses.CustomLevel customLevel = _model.LevelIdToCustomSongInfos[level.levelID];
viewController.DismissModalViewController(null, false);
_log.Debug("Deleting: {0}", customLevel.customSongInfo.path);
FileAttributes attr = File.GetAttributes(customLevel.customSongInfo.path);
if (attr.HasFlag(FileAttributes.Directory))
Directory.Delete(customLevel.customSongInfo.path);
else
File.Delete(customLevel.customSongInfo.path);
SongLoaderPlugin.SongLoader.Instance.RemoveSongWithPath(customLevel.customSongInfo.path);
}
}
///
/// Add/Remove song from favorites depending on if it already exists.
///
private void ToggleSongInFavorites()
{
IStandardLevel songInfo = this._levelListViewController.selectedLevel;
if (_model.Settings.favorites.Contains(songInfo.levelID))
{
_log.Info("Remove {0} from favorites", songInfo.songName);
_model.Settings.favorites.Remove(songInfo.levelID);
_addFavoriteButtonText = "+1";
}
else
{
_log.Info("Add {0} to favorites", songInfo.songName);
_model.Settings.favorites.Add(songInfo.levelID);
_addFavoriteButtonText = "-1";
}
UIBuilder.SetButtonText(ref _addFavoriteButton, _addFavoriteButtonText);
_model.Settings.Save();
}
///
/// Helper to quickly refresh add to favorites button
///
///
private void RefreshAddFavoriteButton(String levelId)
{
if (levelId == null)
{
_addFavoriteButtonText = "0";
return;
}
if (_model.Settings.favorites.Contains(levelId))
{
_addFavoriteButtonText = "-1";
}
else
{
_addFavoriteButtonText = "+1";
}
UIBuilder.SetButtonText(ref _addFavoriteButton, _addFavoriteButtonText);
}
///
/// Adjust the UI colors.
///
public void RefreshUI()
{
// So far all we need to refresh is the sort buttons.
foreach (SongSortButton sortButton in _sortButtonGroup)
{
UIBuilder.SetButtonBorder(ref sortButton.Button, Color.black);
if (sortButton.SortMode == _model.Settings.sortMode)
{
UIBuilder.SetButtonBorder(ref sortButton.Button, Color.red);
}
}
}
///
/// Try to refresh the song list. Broken for now.
///
public void RefreshSongList()
{
_log.Info("Refreshing the song list view.");
try
{
if (_model.SortedSongList == null)
{
_log.Debug("Songs are not sorted yet, nothing to refresh.");
return;
}
StandardLevelSO[] levels = _model.SortedSongList.ToArray();
StandardLevelListViewController songListViewController = this._levelSelectionFlowCoordinator.GetPrivateField("_levelListViewController");
StandardLevelListTableView _songListTableView = songListViewController.GetComponentInChildren();
ReflectionUtil.SetPrivateField(_songListTableView, "_levels", levels);
ReflectionUtil.SetPrivateField(songListViewController, "_levels", levels);
TableView tableView = ReflectionUtil.GetPrivateField(_songListTableView, "_tableView");
tableView.ReloadData();
String selectedLevelID = null;
if (SongBrowserModel.LastSelectedLevelId != null)
{
selectedLevelID = SongBrowserModel.LastSelectedLevelId;
_log.Debug("Scrolling to row for level ID: {0}", selectedLevelID);
}
else
{
selectedLevelID = levels.FirstOrDefault().levelID;
}
if (levels.Any(x => x.levelID == selectedLevelID))
{
SelectAndScrollToLevel(_songListTableView, selectedLevelID);
}
RefreshUI();
}
catch (Exception e)
{
_log.Exception("Exception refreshing song list:", e);
}
}
private void SelectAndScrollToLevel(StandardLevelListTableView table, string levelID)
{
int row = table.RowNumberForLevelID(levelID);
TableView _tableView = table.GetComponentInChildren();
_tableView.SelectRow(row, true);
_tableView.ScrollToRow(row, true);
}
///
/// Helper for updating the model (which updates the song list)c
///
public void UpdateSongList()
{
_log.Trace("UpdateSongList()");
GameplayMode gameplayMode = _levelSelectionFlowCoordinator.GetPrivateField("_gameplayMode");
_model.UpdateSongLists(gameplayMode);
}
///
/// Not normally called by the game-engine. Dependent on SongBrowserApplication to call it.
///
public void LateUpdate()
{
if (this._levelListViewController.isInViewControllerHierarchy)
{
CheckDebugUserInput();
}
}
///
/// Map some key presses directly to UI interactions to make testing easier.
///
private void CheckDebugUserInput()
{
try
{
// back
if (Input.GetKeyDown(KeyCode.Escape))
{
this._levelSelectionNavigationController.DismissButtonWasPressed();
}
// cycle sort modes
if (Input.GetKeyDown(KeyCode.T))
{
_sortButtonLastPushedIndex = (_sortButtonLastPushedIndex + 1) % _sortButtonGroup.Count;
_sortButtonGroup[_sortButtonLastPushedIndex].Button.onClick.Invoke();
}
// delete
if (Input.GetKeyDown(KeyCode.D))
{
if (_deleteDialog.isInViewControllerHierarchy)
{
return;
}
_deleteButton.onClick.Invoke();
}
StandardLevelListTableView levelListTableView = this._levelListViewController.GetComponentInChildren();
// z,x,c,v can be used to get into a song, b will hit continue button after song ends
if (Input.GetKeyDown(KeyCode.C))
{
levelListTableView.SelectAndScrollToLevel(_model.SortedSongList[0].levelID);
this._levelListViewController.HandleLevelListTableViewDidSelectRow(levelListTableView, 0);
this._levelDifficultyViewController.HandleDifficultyTableViewDidSelectRow(null, 0);
this._levelSelectionFlowCoordinator.HandleDifficultyViewControllerDidSelectDifficulty(_levelDifficultyViewController, _model.SortedSongList[0].GetDifficultyLevel(LevelDifficulty.Easy));
}
if (Input.GetKeyDown(KeyCode.V))
{
this._levelSelectionFlowCoordinator.HandleLevelDetailViewControllerDidPressPlayButton(this._levelDetailViewController);
}
// change song index
if (Input.GetKeyDown(KeyCode.N))
{
_lastRow = (_lastRow - 1) != -1 ? (_lastRow - 1) % this._model.SortedSongList.Count : 0;
levelListTableView.SelectAndScrollToLevel(_model.SortedSongList[_lastRow].levelID);
this._levelListViewController.HandleLevelListTableViewDidSelectRow(levelListTableView, _lastRow);
}
if (Input.GetKeyDown(KeyCode.M))
{
_lastRow = (_lastRow + 1) % this._model.SortedSongList.Count;
levelListTableView.SelectAndScrollToLevel(_model.SortedSongList[_lastRow].levelID);
this._levelListViewController.HandleLevelListTableViewDidSelectRow(levelListTableView, _lastRow);
}
// add to favorites
if (Input.GetKeyDown(KeyCode.F))
{
ToggleSongInFavorites();
}
}
catch (Exception e)
{
_log.Exception("Debug Input caused Exception: ", e);
}
}
}
}