using System.ComponentModel;
using System.Runtime.CompilerServices;
using MinecraftClient.Inventory;
namespace MinecraftClient.Tui
{
public class SlotViewModel : INotifyPropertyChanged
{
private bool _isSelected;
private bool _isHovered;
public int SlotId { get; }
public string ItemDisplayText { get; private set; }
public string CountDisplay { get; private set; }
public string FullInfo { get; private set; }
public string ItemTypeName { get; private set; }
public bool IsEmpty { get; private set; }
public bool IsHotbar { get; }
public int HotbarIndex { get; }
public ItemType ItemType { get; private set; }
public int ItemCount { get; private set; }
public Item? RawItem { get; private set; }
public int NameMaxWidth { get; set; } = 9;
public int NameMaxLines { get; set; } = 1;
public bool IsSelected
{
get => _isSelected;
set { _isSelected = value; OnPropertyChanged(); }
}
public bool IsHovered
{
get => _isHovered;
set { _isHovered = value; OnPropertyChanged(); }
}
public SlotViewModel(int slotId, bool isHotbar = false, int hotbarIndex = -1)
{
SlotId = slotId;
IsHotbar = isHotbar;
HotbarIndex = hotbarIndex;
ItemDisplayText = "";
CountDisplay = "";
FullInfo = "";
ItemTypeName = "";
IsEmpty = true;
ItemType = ItemType.Air;
ItemCount = 0;
}
public void Update(Item? item)
{
RawItem = item;
if (item == null || item.IsEmpty)
{
ItemDisplayText = "";
CountDisplay = "";
FullInfo = "";
ItemTypeName = "";
IsEmpty = true;
ItemType = ItemType.Air;
ItemCount = 0;
}
else
{
ItemType = item.Type;
ItemCount = item.Count;
string typeName = item.GetTypeString();
ItemTypeName = typeName;
ItemDisplayText = FormatMultiLine(typeName, NameMaxWidth, NameMaxLines);
CountDisplay = item.Count > 1 ? $"x{item.Count}" : "";
FullInfo = item.ToFullString();
IsEmpty = false;
}
OnPropertyChanged(nameof(ItemDisplayText));
OnPropertyChanged(nameof(CountDisplay));
OnPropertyChanged(nameof(FullInfo));
OnPropertyChanged(nameof(IsEmpty));
OnPropertyChanged(nameof(ItemType));
OnPropertyChanged(nameof(ItemTypeName));
OnPropertyChanged(nameof(ItemCount));
}
///
/// Format item name into multi-line display text that fits within
/// maxWidth columns and maxLines lines. Breaks at word boundaries.
///
private static string FormatMultiLine(string name, int maxWidth, int maxLines)
{
if (string.IsNullOrEmpty(name))
return "";
int colonIdx = name.LastIndexOf(':');
if (colonIdx >= 0 && colonIdx < name.Length - 1)
name = name[(colonIdx + 1)..];
name = name.Replace("_", " ").Trim();
name = InsertCamelCaseSpaces(name);
if (maxLines <= 1 || name.Length <= maxWidth)
return name.Length <= maxWidth ? name : name[..maxWidth];
var lines = new System.Collections.Generic.List();
string remaining = name;
for (int line = 0; line < maxLines && remaining.Length > 0; line++)
{
if (remaining.Length <= maxWidth)
{
lines.Add(remaining);
break;
}
int breakAt = -1;
for (int i = maxWidth; i >= 1; i--)
{
if (remaining[i] == ' ')
{
breakAt = i;
break;
}
}
if (breakAt < 0)
breakAt = maxWidth;
lines.Add(remaining[..breakAt].TrimEnd());
remaining = remaining[breakAt..].TrimStart();
}
return string.Join("\n", lines);
}
private static string InsertCamelCaseSpaces(string s)
{
if (s.Length < 2) return s;
var sb = new System.Text.StringBuilder(s.Length + 4);
sb.Append(s[0]);
for (int i = 1; i < s.Length; i++)
{
if (char.IsUpper(s[i]) && char.IsLower(s[i - 1]))
sb.Append(' ');
sb.Append(s[i]);
}
return sb.ToString();
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}