using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Collections.ObjectModel;
using Wpf.Ui.Mvvm.Contracts;
using Bloxstrap.UI.Elements.Dialogs;
using Newtonsoft.Json.Linq;
using System.Xml.Linq;
namespace Bloxstrap.UI.Elements.Menu.Pages
{
///
/// Interaction logic for FastFlagEditorPage.xaml
///
public partial class FastFlagEditorPage
{
// believe me when i say there is absolutely zero point to using mvvm for this
// using a datagrid is a codebehind thing only and thats it theres literally no way around it
private readonly ObservableCollection _fastFlagList = new();
private readonly List _validPrefixes = new()
{
"FFlag", "DFFlag", "SFFlag", "FInt", "DFInt", "FString", "DFString", "FLog", "DFLog"
};
// values must match the entire string to avoid cases where half the string
// matches but the filter would still be invalid
private readonly Regex _boolFilterPattern = new("(?:true|false)(;[\\d]{1,})+$");
private readonly Regex _intFilterPattern = new("([\\d]{1,})?(;[\\d]{1,})+$");
private readonly Regex _stringFilterPattern = new("^[^;]*(;[\\d]{1,})+$");
private bool _showPresets = false;
private string _searchFilter = "";
public FastFlagEditorPage()
{
InitializeComponent();
}
private void ReloadList()
{
var selectedEntry = DataGrid.SelectedItem as FastFlag;
_fastFlagList.Clear();
var presetFlags = FastFlagManager.PresetFlags.Values;
foreach (var pair in App.FastFlags.Prop.OrderBy(x => x.Key))
{
if (!_showPresets && presetFlags.Contains(pair.Key))
continue;
if (!pair.Key.ToLower().Contains(_searchFilter.ToLower()))
continue;
var entry = new FastFlag
{
// Enabled = true,
Name = pair.Key,
Value = pair.Value.ToString()!
};
/* if (entry.Name.StartsWith("Disable"))
{
entry.Enabled = false;
entry.Name = entry.Name[7..];
} */
_fastFlagList.Add(entry);
}
if (DataGrid.ItemsSource is null)
DataGrid.ItemsSource = _fastFlagList;
if (selectedEntry is null)
return;
var newSelectedEntry = _fastFlagList.Where(x => x.Name == selectedEntry.Name).FirstOrDefault();
if (newSelectedEntry is null)
return;
DataGrid.SelectedItem = newSelectedEntry;
DataGrid.ScrollIntoView(newSelectedEntry);
}
private void ClearSearch(bool refresh = true)
{
SearchTextBox.Text = "";
_searchFilter = "";
if (refresh)
ReloadList();
}
private void ShowAddDialog()
{
var dialog = new AddFastFlagDialog();
dialog.ShowDialog();
if (dialog.Result != MessageBoxResult.OK)
return;
if (dialog.Tabs.SelectedIndex == 0)
AddSingle(dialog.FlagNameTextBox.Text, dialog.FlagValueTextBox.Text);
else if (dialog.Tabs.SelectedIndex == 1)
ImportJSON(dialog.JsonTextBox.Text);
}
private void AddSingle(string name, string value)
{
FastFlag? entry;
if (App.FastFlags.GetValue(name) is null)
{
if (!ValidateFlagEntry(name, value))
{
ShowAddDialog();
return;
}
entry = new FastFlag
{
// Enabled = true,
Name = name,
Value = value
};
if (!name.Contains(_searchFilter))
ClearSearch();
_fastFlagList.Add(entry);
App.FastFlags.SetValue(entry.Name, entry.Value);
}
else
{
Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information);
bool refresh = false;
if (!_showPresets && FastFlagManager.PresetFlags.Values.Contains(name))
{
TogglePresetsButton.IsChecked = true;
_showPresets = true;
refresh = true;
}
if (!name.Contains(_searchFilter))
{
ClearSearch(false);
refresh = true;
}
if (refresh)
ReloadList();
entry = _fastFlagList.Where(x => x.Name == name).FirstOrDefault();
}
DataGrid.SelectedItem = entry;
DataGrid.ScrollIntoView(entry);
}
private void ImportJSON(string json)
{
Dictionary? list = null;
json = json.Trim();
// autocorrect where possible
if (!json.StartsWith('{'))
json = '{' + json;
if (!json.EndsWith('}'))
json += '}';
try
{
var options = new JsonSerializerOptions
{
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
};
list = JsonSerializer.Deserialize>(json, options);
if (list is null)
throw new Exception("JSON deserialization returned null");
}
catch (Exception ex)
{
Frontend.ShowMessageBox(
String.Format(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidJSON, ex.Message),
MessageBoxImage.Error
);
ShowAddDialog();
return;
}
if (list.Count > 16)
{
var result = Frontend.ShowMessageBox(
Bloxstrap.Resources.Strings.Menu_FastFlagEditor_LargeConfig,
MessageBoxImage.Warning,
MessageBoxButton.YesNo
);
if (result != MessageBoxResult.Yes)
return;
}
var conflictingFlags = App.FastFlags.Prop.Where(x => list.ContainsKey(x.Key)).Select(x => x.Key);
bool overwriteConflicting = false;
if (conflictingFlags.Any())
{
int count = conflictingFlags.Count();
string message = String.Format(
Bloxstrap.Resources.Strings.Menu_FastFlagEditor_ConflictingImport,
count,
String.Join(", ", conflictingFlags.Take(25))
);
if (count > 25)
message += "...";
var result = Frontend.ShowMessageBox(message, MessageBoxImage.Question, MessageBoxButton.YesNo);
overwriteConflicting = result == MessageBoxResult.Yes;
}
foreach (var pair in list)
{
if (App.FastFlags.Prop.ContainsKey(pair.Key) && !overwriteConflicting)
continue;
var val = pair.Value.ToString();
if (val is null)
continue;
if (!ValidateFlagEntry(pair.Key, val))
continue;
App.FastFlags.SetValue(pair.Key, pair.Value);
}
ClearSearch();
}
private bool ValidateFlagEntry(string name, string value)
{
string lowerValue = value.ToLowerInvariant();
string errorMessage = "";
if (!_validPrefixes.Any(name.StartsWith))
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidPrefix;
else if (!name.All(x => char.IsLetterOrDigit(x) || x == '_'))
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidCharacter;
// sorry
if (name.EndsWith("_PlaceFilter") || name.EndsWith("_DataCenterFilter"))
errorMessage = !Validate_Filter(name, value) ? Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidPlaceFilter : "";
else if ((name.StartsWith("FInt") || name.StartsWith("DFInt")) && !Int32.TryParse(value, out _))
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidNumberValue;
else if ((name.StartsWith("FFlag") || name.StartsWith("DFFlag")) && lowerValue != "true" && lowerValue != "false")
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidBoolValue;
if (!String.IsNullOrEmpty(errorMessage))
{
Frontend.ShowMessageBox(String.Format(errorMessage, name), MessageBoxImage.Error);
return false;
}
return true;
}
private bool Validate_Filter(string name, string value)
{
if(name.StartsWith("FFlag") || name.StartsWith("DFFlag"))
return _boolFilterPattern.IsMatch(value);
if (name.StartsWith("FInt") || name.StartsWith("DFInt"))
return _intFilterPattern.IsMatch(value);
if (name.StartsWith("FString") || name.StartsWith("DFString") || name.StartsWith("FLog") || name.StartsWith("DFLog"))
return _stringFilterPattern.IsMatch(value);
return false;
}
// refresh list on page load to synchronize with preset page
private void Page_Loaded(object sender, RoutedEventArgs e) => ReloadList();
private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
int index = e.Row.GetIndex();
FastFlag entry = _fastFlagList[index];
var textbox = e.EditingElement as TextBox;
if (textbox is null)
return;
switch (e.Column.Header)
{
case "Name":
string oldName = entry.Name;
string newName = textbox.Text;
if (newName == oldName)
return;
if (App.FastFlags.GetValue(newName) is not null)
{
Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information);
e.Cancel = true;
textbox.Text = oldName;
return;
}
App.FastFlags.SetValue(oldName, null);
App.FastFlags.SetValue(newName, entry.Value);
if (!newName.Contains(_searchFilter))
ClearSearch();
entry.Name = newName;
break;
case "Value":
string oldValue = entry.Value;
string newValue = textbox.Text;
if (!ValidateFlagEntry(entry.Name, newValue))
{
e.Cancel = true;
textbox.Text = oldValue;
return;
}
App.FastFlags.SetValue(entry.Name, newValue);
break;
}
}
private void BackButton_Click(object sender, RoutedEventArgs e)
{
if (Window.GetWindow(this) is INavigationWindow window)
window.Navigate(typeof(FastFlagsPage));
}
private void AddButton_Click(object sender, RoutedEventArgs e) => ShowAddDialog();
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var tempList = new List();
foreach (FastFlag entry in DataGrid.SelectedItems)
tempList.Add(entry);
foreach (FastFlag entry in tempList)
{
_fastFlagList.Remove(entry);
App.FastFlags.SetValue(entry.Name, null);
}
}
private void ToggleButton_Click(object sender, RoutedEventArgs e)
{
if (sender is not ToggleButton button)
return;
_showPresets = button.IsChecked ?? false;
ReloadList();
}
private void ExportJSONButton_Click(object sender, RoutedEventArgs e)
{
string json = JsonSerializer.Serialize(App.FastFlags.Prop, new JsonSerializerOptions { WriteIndented = true });
Clipboard.SetDataObject(json);
Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_JsonCopiedToClipboard, MessageBoxImage.Information);
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (sender is not TextBox textbox)
return;
_searchFilter = textbox.Text;
ReloadList();
}
}
}